diff options
author | wolfbeast <mcwerewolf@gmail.com> | 2014-05-21 11:38:25 +0200 |
---|---|---|
committer | wolfbeast <mcwerewolf@gmail.com> | 2014-05-21 11:38:25 +0200 |
commit | d25ba7d760b017b038e5aa6c0a605b4a330eb68d (patch) | |
tree | 16ec27edc7d5f83986f16236d3a36a2682a0f37e /build | |
parent | a942906574671868daf122284a9c4689e6924f74 (diff) | |
download | palemoon-gre-d25ba7d760b017b038e5aa6c0a605b4a330eb68d.tar.gz |
Recommit working copy to repo with proper line endings.
Diffstat (limited to 'build')
495 files changed, 53337 insertions, 0 deletions
diff --git a/build/ConfigStatus.py b/build/ConfigStatus.py new file mode 100644 index 000000000..d424f31c1 --- /dev/null +++ b/build/ConfigStatus.py @@ -0,0 +1,133 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +# Combined with build/autoconf/config.status.m4, ConfigStatus is an almost +# drop-in replacement for autoconf 2.13's config.status, with features +# borrowed from autoconf > 2.5, and additional features. + +from __future__ import print_function + +import logging +import os +import sys + +from optparse import OptionParser + +from mach.logging import LoggingManager +from mozbuild.backend.configenvironment import ConfigEnvironment +from mozbuild.backend.recursivemake import RecursiveMakeBackend +from mozbuild.frontend.emitter import TreeMetadataEmitter +from mozbuild.frontend.reader import BuildReader + +from Preprocessor import Preprocessor + + +log_manager = LoggingManager() + + +def config_status(topobjdir = '.', topsrcdir = '.', + defines = [], non_global_defines = [], substs = [], + files = [], headers = []): + '''Main function, providing config.status functionality. + + Contrary to config.status, it doesn't use CONFIG_FILES or CONFIG_HEADERS + variables, but like config.status from autoconf 2.6, single files may be + generated with the --file and --header options. Several such options can + be given to generate several files at the same time. + + Without the -n option, this program acts as config.status and considers + the current directory as the top object directory, even when config.status + is in a different directory. It will, however, treat the directory + containing config.status as the top object directory with the -n option, + while files given to the --file and --header arguments are considered + relative to the current directory. + + The --recheck option, like with the original config.status, runs configure + again, with the options given in the "ac_configure_args" subst. + + The options to this function are passed when creating the + ConfigEnvironment, except for files and headers, which contain the list + of files and headers to be generated by default. These lists, as well as + the actual wrapper script around this function, are meant to be generated + by configure. See build/autoconf/config.status.m4. + + Unlike config.status behaviour with CONFIG_FILES and CONFIG_HEADERS, + but like config.status behaviour with --file and --header, providing + files or headers on the command line inhibits the default generation of + files when given headers and headers when given files. + + Unlike config.status, the FILE:TEMPLATE syntax is not supported for + files and headers. The template is always the filename suffixed with + '.in', in the corresponding directory under the top source directory. + ''' + + if 'CONFIG_FILES' in os.environ: + raise Exception, 'Using the CONFIG_FILES environment variable is not supported. Use --file instead.' + if 'CONFIG_HEADERS' in os.environ: + raise Exception, 'Using the CONFIG_HEADERS environment variable is not supported. Use --header instead.' + + parser = OptionParser() + parser.add_option('--recheck', dest='recheck', action='store_true', + help='update config.status by reconfiguring in the same conditions') + parser.add_option('--file', dest='files', metavar='FILE', action='append', + help='instantiate the configuration file FILE') + parser.add_option('--header', dest='headers', metavar='FILE', action='append', + help='instantiate the configuration header FILE') + parser.add_option('-v', '--verbose', dest='verbose', action='store_true', + help='display verbose output') + parser.add_option('-n', dest='not_topobjdir', action='store_true', + help='do not consider current directory as top object directory') + (options, args) = parser.parse_args() + + # Without -n, the current directory is meant to be the top object directory + if not options.not_topobjdir: + topobjdir = os.path.abspath('.') + + env = ConfigEnvironment(topsrcdir, topobjdir, defines=defines, + non_global_defines=non_global_defines, substs=substs) + + reader = BuildReader(env) + emitter = TreeMetadataEmitter(env) + backend = RecursiveMakeBackend(env) + # This won't actually do anything because of the magic of generators. + definitions = emitter.emit(reader.read_topsrcdir()) + + if options.recheck: + # Execute configure from the top object directory + if not os.path.isabs(topsrcdir): + topsrcdir = relpath(topsrcdir, topobjdir) + os.chdir(topobjdir) + os.execlp('sh', 'sh', '-c', ' '.join([os.path.join(topsrcdir, 'configure'), env.substs['ac_configure_args'], '--no-create', '--no-recursion'])) + + if options.files: + files = options.files + headers = [] + if options.headers: + headers = options.headers + if not options.files: + files = [] + # Default to display messages when giving --file or --headers on the + # command line. + log_level = logging.INFO + + if options.files or options.headers or options.verbose: + log_level = logging.DEBUG + + log_manager.add_terminal_logging(level=log_level) + log_manager.enable_unstructured() + + if not options.files and not options.headers: + print('Reticulating splines...', file=sys.stderr) + summary = backend.consume(definitions) + + for line in summary.summaries(): + print(line, file=sys.stderr) + + files = [os.path.join(topobjdir, f) for f in files] + headers = [os.path.join(topobjdir, f) for f in headers] + + for file in files: + env.create_config_file(file) + for header in headers: + env.create_config_header(header) diff --git a/build/Makefile.in b/build/Makefile.in new file mode 100644 index 000000000..3da5dda83 --- /dev/null +++ b/build/Makefile.in @@ -0,0 +1,165 @@ +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +DEPTH = @DEPTH@ +topsrcdir = @top_srcdir@ +srcdir = @srcdir@ +VPATH = @srcdir@ + +include $(DEPTH)/config/autoconf.mk +USE_RCS_MK := 1 +include $(topsrcdir)/config/makefiles/makeutils.mk + +ifdef MOZ_APP_BASENAME +DIST_FILES = $(srcdir)/application.ini + +ifneq (android,$(MOZ_WIDGET_TOOLKIT)) +ifdef MOZ_UPDATER +DIST_FILES += update-settings.ini +endif +endif + +ifdef LIBXUL_SDK +APP_INI_DEPS = $(LIBXUL_DIST)/bin/platform.ini +else +APP_INI_DEPS = $(topsrcdir)/config/milestone.txt +endif + +APP_BUILDID := $(shell cat $(DEPTH)/config/buildid) +APP_INI_DEPS += $(DEPTH)/config/buildid + +DEFINES += -DGRE_MILESTONE=$(GRE_MILESTONE) -DAPP_BUILDID=$(APP_BUILDID) + +DEFINES += -DMOZ_APP_VERSION="$(MOZ_APP_VERSION)" +APP_INI_DEPS += $(DEPTH)/config/autoconf.mk + +MOZ_SOURCE_STAMP := $(firstword $(shell cd $(topsrcdir)/$(MOZ_BUILD_APP)/.. && hg parent --template="{node|short}\n" 2>/dev/null)) +ifdef MOZ_SOURCE_STAMP +DEFINES += -DMOZ_SOURCE_STAMP="$(MOZ_SOURCE_STAMP)" +endif + +source_repo ?= $(call getSourceRepo,$(topsrcdir)/$(MOZ_BUILD_APP)/..) +ifneq (,$(source_repo)) + DEFINES += -DMOZ_SOURCE_REPO="$(source_repo)" +endif + +DEFINES += \ + -DMOZ_APP_BASENAME="$(MOZ_APP_BASENAME)" \ + -DMOZ_APP_VENDOR="$(MOZ_APP_VENDOR)" \ + -DMOZ_APP_ID="$(MOZ_APP_ID)" \ + -DMAR_CHANNEL_ID="$(MAR_CHANNEL_ID)" \ + -DACCEPTED_MAR_CHANNEL_IDS="$(ACCEPTED_MAR_CHANNEL_IDS)" \ + $(NULL) + +ifeq ($(MOZ_BUILD_APP),browser) +DEFINES += -DMOZ_BUILD_APP_IS_BROWSER +endif + +ifdef MOZ_APP_PROFILE +DEFINES += -DMOZ_APP_PROFILE="$(MOZ_APP_PROFILE)" +endif + +ifdef MOZILLA_OFFICIAL +DEFINES += -DMOZILLA_OFFICIAL +endif + +ifdef MOZ_PROFILE_MIGRATOR +DEFINES += -DMOZ_PROFILE_MIGRATOR +endif + +ifdef MOZ_EXTENSION_MANAGER +DEFINES += -DMOZ_EXTENSION_MANAGER +endif + +endif + +# Put a useful .gdbinit in the bin directory, to be picked up automatically +# by GDB when we debug executables there. +# NOTE: Keep .gdbinit in the topsrcdir for people who run gdb from the topsrcdir. +GDBINIT_FILES := $(topsrcdir)/.gdbinit +GDBINIT_DEST = $(FINAL_TARGET) +INSTALL_TARGETS += GDBINIT + +include $(topsrcdir)/config/rules.mk + +# we install to _leaktest/ +TARGET_DEPTH = .. +include $(srcdir)/automation-build.mk + +_LEAKTEST_DIR = $(DEPTH)/_leaktest + +_LEAKTEST_FILES = \ + automation.py \ + automationutils.py \ + $(topsrcdir)/testing/profiles/prefs_general.js \ + leaktest.py \ + bloatcycle.html \ + $(topsrcdir)/build/pgo/server-locations.txt \ + $(topsrcdir)/build/pgo/favicon.ico \ + $(topsrcdir)/build/pgo/blueprint/sample.html \ + $(topsrcdir)/build/pgo/blueprint/elements.html \ + $(topsrcdir)/build/pgo/blueprint/forms.html \ + $(topsrcdir)/build/pgo/blueprint/grid.html \ + $(topsrcdir)/build/pgo/blueprint/test.jpg \ + $(topsrcdir)/build/pgo/blueprint/test-small.jpg \ + $(topsrcdir)/build/pgo/blueprint/valid.png \ + $(topsrcdir)/build/pgo/blueprint/screen.css \ + $(topsrcdir)/build/pgo/blueprint/print.css \ + $(topsrcdir)/build/pgo/blueprint/grid.png \ + $(topsrcdir)/build/pgo/blueprint/fancytype-screen.css \ + $(NULL) + +leaktest.py: leaktest.py.in + $(PYTHON) $(topsrcdir)/config/Preprocessor.py $^ > $@ + chmod +x $@ +GARBAGE += leaktest.py + +ifdef MOZ_APP_BASENAME +$(FINAL_TARGET)/application.ini: $(APP_INI_DEPS) + +ifdef MOZ_APP_STATIC_INI +DEFINES += -DMOZ_APP_STATIC_INI + +application.ini.h: appini_header.py $(FINAL_TARGET)/application.ini + $(PYTHON) $^ > $@ +export:: application.ini.h +GARBAGE += application.ini.h +endif +endif + +libs:: $(_LEAKTEST_FILES) + $(INSTALL) $^ $(_LEAKTEST_DIR) + +ifdef MOZ_VALGRIND +_VALGRIND_DIR = $(DEPTH)/_valgrind +GARBAGE_DIRS += $(_VALGRIND_DIR) + +_VALGRIND_FILES = \ + $(topsrcdir)/build/valgrind/cross-architecture.sup \ + $(topsrcdir)/build/valgrind/i386-redhat-linux-gnu.sup \ + $(topsrcdir)/build/valgrind/x86_64-redhat-linux-gnu.sup \ + $(NULL) + +libs:: $(_VALGRIND_FILES) + $(INSTALL) $^ $(_VALGRIND_DIR) +endif + +ifdef ENABLE_TESTS +libs:: $(topsrcdir)/tools/rb/fix_stack_using_bpsyms.py + $(INSTALL) $< $(DIST)/bin + +ifeq ($(OS_ARCH),Darwin) +libs:: $(topsrcdir)/tools/rb/fix_macosx_stack.py + $(INSTALL) $< $(DIST)/bin +endif + +ifeq ($(OS_ARCH),Linux) +libs:: $(topsrcdir)/tools/rb/fix-linux-stack.pl + $(INSTALL) $< $(DIST)/bin +endif + +GARBAGE += $(srcdir)/automationutils.pyc + +endif # ENABLE_TESTS diff --git a/build/__init__.py b/build/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/build/__init__.py diff --git a/build/appini_header.py b/build/appini_header.py new file mode 100644 index 000000000..678c51976 --- /dev/null +++ b/build/appini_header.py @@ -0,0 +1,53 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +'''Parses a given application.ini file and outputs the corresponding + XULAppData structure as a C++ header file''' + +import ConfigParser +import sys + +def main(file): + config = ConfigParser.RawConfigParser() + config.read(file) + flags = set() + try: + if config.getint('XRE', 'EnableExtensionManager') == 1: + flags.add('NS_XRE_ENABLE_EXTENSION_MANAGER') + except: pass + try: + if config.getint('XRE', 'EnableProfileMigrator') == 1: + flags.add('NS_XRE_ENABLE_PROFILE_MIGRATOR') + except: pass + try: + if config.getint('Crash Reporter', 'Enabled') == 1: + flags.add('NS_XRE_ENABLE_CRASH_REPORTER') + except: pass + appdata = dict(("%s:%s" % (s, o), config.get(s, o)) for s in config.sections() for o in config.options(s)) + appdata['flags'] = ' | '.join(flags) if flags else '0' + appdata['App:profile'] = '"%s"' % appdata['App:profile'] if 'App:profile' in appdata else 'NULL' + + print '''#include "nsXREAppData.h" + static const nsXREAppData sAppData = { + sizeof(nsXREAppData), + NULL, // directory + "%(App:vendor)s", + "%(App:name)s", + "%(App:version)s", + "%(App:buildid)s", + "%(App:id)s", + NULL, // copyright + %(flags)s, + NULL, // xreDirectory + "%(Gecko:minversion)s", + "%(Gecko:maxversion)s", + "%(Crash Reporter:serverurl)s", + %(App:profile)s + };''' % appdata + +if __name__ == '__main__': + if len(sys.argv) != 1: + main(sys.argv[1]) + else: + print >>sys.stderr, "Usage: %s /path/to/application.ini" % sys.argv[0] diff --git a/build/application.ini b/build/application.ini new file mode 100644 index 000000000..fada1e9d7 --- /dev/null +++ b/build/application.ini @@ -0,0 +1,47 @@ +#if MOZ_APP_STATIC_INI +#ifdef MOZ_BUILD_APP_IS_BROWSER +; This file is not used. If you modify it and want the application to use +; your modifications, move it under the browser/ subdirectory and start with +; the "-app /path/to/browser/application.ini" argument. +#else +; This file is not used. If you modify it and want the application to use +; your modifications, start with the "-app /path/to/application.ini" +; argument. +#endif +#endif +#if 0 +; This Source Code Form is subject to the terms of the Mozilla Public +; License, v. 2.0. If a copy of the MPL was not distributed with this +; file, You can obtain one at http://mozilla.org/MPL/2.0/. +#endif +#filter substitution +[App] +Vendor=Moonchild Productions +Name=Pale Moon +Version=@MOZ_APP_VERSION@ +#ifdef MOZ_APP_PROFILE +Profile=@MOZ_APP_PROFILE@ +#endif +BuildID=@APP_BUILDID@ +#ifdef MOZ_SOURCE_REPO +SourceRepository=@MOZ_SOURCE_REPO@ +#endif +#ifdef MOZ_SOURCE_STAMP +SourceStamp=@MOZ_SOURCE_STAMP@ +#endif +ID=@MOZ_APP_ID@ + +[Gecko] +MinVersion=@GRE_MILESTONE@ +MaxVersion=@GRE_MILESTONE@ + +[XRE] +#ifdef MOZ_PROFILE_MIGRATOR +EnableProfileMigrator=1 +#endif +#ifdef MOZ_EXTENSION_MANAGER +EnableExtensionManager=1 +#endif + +[Crash Reporter] +ServerURL= diff --git a/build/autoconf/acwinpaths.m4 b/build/autoconf/acwinpaths.m4 new file mode 100644 index 000000000..ad9c754bb --- /dev/null +++ b/build/autoconf/acwinpaths.m4 @@ -0,0 +1,31 @@ +dnl This Source Code Form is subject to the terms of the Mozilla Public +dnl License, v. 2.0. If a copy of the MPL was not distributed with this +dnl file, You can obtain one at http://mozilla.org/MPL/2.0/. + +define(GENERATE_SUB_ABS, [ +define([AC_OUTPUT_FILES_SUB1], [ +patsubst($@, [/\*)], [/* | ?:/*)]) +]) +]) +GENERATE_SUB_ABS(defn([AC_OUTPUT_FILES])) + +define(GENERATE_SUB_NOSPLIT, [ +define([AC_OUTPUT_FILES], [ +patsubst($@, [-e "s%:% \$ac_given_srcdir/%g"], []) +]) +]) +GENERATE_SUB_NOSPLIT(defn([AC_OUTPUT_FILES_SUB1])) + +define(GENERATE_HEADER_NOSPLIT, [ +define([AC_OUTPUT_HEADER], [ +patsubst($@, [-e "s%:% \$ac_given_srcdir/%g"], []) +]) +]) +GENERATE_HEADER_NOSPLIT(defn([AC_OUTPUT_HEADER])) + +define(GENERATE_SUBDIRS_ABS, [ +define([AC_OUTPUT_SUBDIRS], [ +patsubst($@, [/\*)], [/* | ?:/*)]) +]) +]) +GENERATE_SUBDIRS_ABS(defn([AC_OUTPUT_SUBDIRS])) diff --git a/build/autoconf/altoptions.m4 b/build/autoconf/altoptions.m4 new file mode 100644 index 000000000..3dc8c6e9a --- /dev/null +++ b/build/autoconf/altoptions.m4 @@ -0,0 +1,122 @@ +dnl This Source Code Form is subject to the terms of the Mozilla Public +dnl License, v. 2.0. If a copy of the MPL was not distributed with this +dnl file, You can obtain one at http://mozilla.org/MPL/2.0/. + +dnl altoptions.m4 - An alternative way of specifying command-line options. +dnl These macros are needed to support a menu-based configurator. +dnl This file also includes the macro, AM_READ_MYCONFIG, for reading +dnl the 'myconfig.m4' file. + +dnl Send comments, improvements, bugs to Steve Lamm (slamm@netscape.com). + + +dnl MOZ_ARG_ENABLE_BOOL( NAME, HELP, IF-YES [, IF-NO [, ELSE]]) +dnl MOZ_ARG_DISABLE_BOOL( NAME, HELP, IF-NO [, IF-YES [, ELSE]]) +dnl MOZ_ARG_ENABLE_STRING( NAME, HELP, IF-SET [, ELSE]) +dnl MOZ_ARG_ENABLE_BOOL_OR_STRING( NAME, HELP, IF-YES, IF-NO, IF-SET[, ELSE]]]) +dnl MOZ_ARG_WITH_BOOL( NAME, HELP, IF-YES [, IF-NO [, ELSE]) +dnl MOZ_ARG_WITHOUT_BOOL( NAME, HELP, IF-NO [, IF-YES [, ELSE]) +dnl MOZ_ARG_WITH_STRING( NAME, HELP, IF-SET [, ELSE]) +dnl MOZ_ARG_HEADER(Comment) +dnl MOZ_CHECK_PTHREADS( NAME, IF-YES [, ELSE ]) +dnl MOZ_READ_MYCONFIG() - Read in 'myconfig.sh' file + + +dnl MOZ_TWO_STRING_TEST(NAME, VAL, STR1, IF-STR1, STR2, IF-STR2 [, ELSE]) +AC_DEFUN([MOZ_TWO_STRING_TEST], +[if test "[$2]" = "[$3]"; then + ifelse([$4], , :, [$4]) + elif test "[$2]" = "[$5]"; then + ifelse([$6], , :, [$6]) + else + ifelse([$7], , + [AC_MSG_ERROR([Option, [$1], does not take an argument ([$2]).])], + [$7]) + fi]) + +dnl MOZ_ARG_ENABLE_BOOL(NAME, HELP, IF-YES [, IF-NO [, ELSE]]) +AC_DEFUN([MOZ_ARG_ENABLE_BOOL], +[AC_ARG_ENABLE([$1], [$2], + [MOZ_TWO_STRING_TEST([$1], [$enableval], yes, [$3], no, [$4])], + [$5])]) + +dnl MOZ_ARG_DISABLE_BOOL(NAME, HELP, IF-NO [, IF-YES [, ELSE]]) +AC_DEFUN([MOZ_ARG_DISABLE_BOOL], +[AC_ARG_ENABLE([$1], [$2], + [MOZ_TWO_STRING_TEST([$1], [$enableval], no, [$3], yes, [$4])], + [$5])]) + +dnl MOZ_ARG_ENABLE_STRING(NAME, HELP, IF-SET [, ELSE]) +AC_DEFUN([MOZ_ARG_ENABLE_STRING], +[AC_ARG_ENABLE([$1], [$2], [$3], [$4])]) + +dnl MOZ_ARG_ENABLE_BOOL_OR_STRING(NAME, HELP, IF-YES, IF-NO, IF-SET[, ELSE]]]) +AC_DEFUN([MOZ_ARG_ENABLE_BOOL_OR_STRING], +[ifelse([$5], , + [errprint([Option, $1, needs an "IF-SET" argument. +]) + m4exit(1)], + [AC_ARG_ENABLE([$1], [$2], + [MOZ_TWO_STRING_TEST([$1], [$enableval], yes, [$3], no, [$4], [$5])], + [$6])])]) + +dnl MOZ_ARG_WITH_BOOL(NAME, HELP, IF-YES [, IF-NO [, ELSE]) +AC_DEFUN([MOZ_ARG_WITH_BOOL], +[AC_ARG_WITH([$1], [$2], + [MOZ_TWO_STRING_TEST([$1], [$withval], yes, [$3], no, [$4])], + [$5])]) + +dnl MOZ_ARG_WITHOUT_BOOL(NAME, HELP, IF-NO [, IF-YES [, ELSE]) +AC_DEFUN([MOZ_ARG_WITHOUT_BOOL], +[AC_ARG_WITH([$1], [$2], + [MOZ_TWO_STRING_TEST([$1], [$withval], no, [$3], yes, [$4])], + [$5])]) + +dnl MOZ_ARG_WITH_STRING(NAME, HELP, IF-SET [, ELSE]) +AC_DEFUN([MOZ_ARG_WITH_STRING], +[AC_ARG_WITH([$1], [$2], [$3], [$4])]) + +dnl MOZ_ARG_HEADER(Comment) +dnl This is used by webconfig to group options +define(MOZ_ARG_HEADER, [# $1]) + +dnl +dnl Apparently, some systems cannot properly check for the pthread +dnl library unless <pthread.h> is included so we need to test +dnl using it +dnl +dnl MOZ_CHECK_PTHREADS(lib, success, failure) +AC_DEFUN([MOZ_CHECK_PTHREADS], +[ +AC_MSG_CHECKING([for pthread_create in -l$1]) +echo " + #include <pthread.h> + #include <stdlib.h> + void *foo(void *v) { int a = 1; } + int main() { + pthread_t t; + if (!pthread_create(&t, 0, &foo, 0)) { + pthread_join(t, 0); + } + exit(0); + }" > dummy.c ; + echo "${CC-cc} -o dummy${ac_exeext} dummy.c $CFLAGS $CPPFLAGS -l[$1] $LDFLAGS $LIBS" 1>&5; + ${CC-cc} -o dummy${ac_exeext} dummy.c $CFLAGS $CPPFLAGS -l[$1] $LDFLAGS $LIBS 2>&5; + _res=$? ; + rm -f dummy.c dummy${ac_exeext} ; + if test "$_res" = "0"; then + AC_MSG_RESULT([yes]) + [$2] + else + AC_MSG_RESULT([no]) + [$3] + fi +]) + +dnl MOZ_READ_MYCONFIG() - Read in 'myconfig.sh' file +AC_DEFUN([MOZ_READ_MOZCONFIG], +[AC_REQUIRE([AC_INIT_BINSH])dnl +# Read in '.mozconfig' script to set the initial options. +# See the mozconfig2configure script for more details. +_AUTOCONF_TOOLS_DIR=`dirname [$]0`/[$1]/build/autoconf +. $_AUTOCONF_TOOLS_DIR/mozconfig2configure]) diff --git a/build/autoconf/android.m4 b/build/autoconf/android.m4 new file mode 100644 index 000000000..fa31afbf4 --- /dev/null +++ b/build/autoconf/android.m4 @@ -0,0 +1,339 @@ +dnl This Source Code Form is subject to the terms of the Mozilla Public +dnl License, v. 2.0. If a copy of the MPL was not distributed with this +dnl file, You can obtain one at http://mozilla.org/MPL/2.0/. + +AC_DEFUN([MOZ_ANDROID_NDK], +[ + +MOZ_ARG_WITH_STRING(android-ndk, +[ --with-android-ndk=DIR + location where the Android NDK can be found], + android_ndk=$withval) + +MOZ_ARG_WITH_STRING(android-toolchain, +[ --with-android-toolchain=DIR + location of the android toolchain], + android_toolchain=$withval) + +MOZ_ARG_WITH_STRING(android-gnu-compiler-version, +[ --with-android-gnu-compiler-version=VER + gnu compiler version to use], + android_gnu_compiler_version=$withval) + +MOZ_ARG_ENABLE_BOOL(android-libstdcxx, +[ --enable-android-libstdcxx + use GNU libstdc++ instead of STLPort], + MOZ_ANDROID_LIBSTDCXX=1, + MOZ_ANDROID_LIBSTDCXX= ) + +define([MIN_ANDROID_VERSION], [9]) +android_version=MIN_ANDROID_VERSION + +MOZ_ARG_WITH_STRING(android-version, +[ --with-android-version=VER + android platform version, default] MIN_ANDROID_VERSION, + android_version=$withval) + +if test $android_version -lt MIN_ANDROID_VERSION ; then + AC_MSG_ERROR([--with-android-version must be at least MIN_ANDROID_VERSION.]) +fi + +MOZ_ARG_WITH_STRING(android-platform, +[ --with-android-platform=DIR + location of platform dir], + android_platform=$withval) + +case "$target" in +arm-linux*-android*|*-linuxandroid*) + android_tool_prefix="arm-linux-androideabi" + ;; +i?86-*android*) + android_tool_prefix="i686-linux-android" + ;; +mipsel-*android*) + android_tool_prefix="mipsel-linux-android" + ;; +*) + android_tool_prefix="$target_os" + ;; +esac + +case "$target" in +*-android*|*-linuxandroid*) + if test -z "$android_ndk" ; then + AC_MSG_ERROR([You must specify --with-android-ndk=/path/to/ndk when targeting Android.]) + fi + + if test -z "$android_toolchain" ; then + AC_MSG_CHECKING([for android toolchain directory]) + + kernel_name=`uname -s | tr "[[:upper:]]" "[[:lower:]]"` + + for version in $android_gnu_compiler_version 4.6 4.4.3 ; do + case "$target_cpu" in + arm) + target_name=arm-linux-androideabi-$version + ;; + i?86) + target_name=x86-$version + ;; + mipsel) + target_name=mipsel-linux-android-$version + ;; + *) + AC_MSG_ERROR([target cpu is not supported]) + ;; + esac + case "$host_cpu" in + i*86) + android_toolchain="$android_ndk"/toolchains/$target_name/prebuilt/$kernel_name-x86 + ;; + x86_64) + android_toolchain="$android_ndk"/toolchains/$target_name/prebuilt/$kernel_name-x86_64 + if ! test -d "$android_toolchain" ; then + android_toolchain="$android_ndk"/toolchains/$target_name/prebuilt/$kernel_name-x86 + fi + ;; + *) + AC_MSG_ERROR([No known toolchain for your host cpu]) + ;; + esac + if test -d "$android_toolchain" ; then + android_gnu_compiler_version=$version + break + elif test -n "$android_gnu_compiler_version" ; then + AC_MSG_ERROR([not found. Your --with-android-gnu-compiler-version may be wrong.]) + fi + done + + if test -z "$android_gnu_compiler_version" ; then + AC_MSG_ERROR([not found. You have to specify --with-android-toolchain=/path/to/ndk/toolchain.]) + else + AC_MSG_RESULT([$android_toolchain]) + fi + NSPR_CONFIGURE_ARGS="$NSPR_CONFIGURE_ARGS --with-android-toolchain=$android_toolchain" + fi + + NSPR_CONFIGURE_ARGS="$NSPR_CONFIGURE_ARGS --with-android-version=$android_version" + + if test -z "$android_platform" ; then + AC_MSG_CHECKING([for android platform directory]) + + case "$target_cpu" in + arm) + target_name=arm + ;; + i?86) + target_name=x86 + ;; + mipsel) + target_name=mips + ;; + esac + + android_platform="$android_ndk"/platforms/android-"$android_version"/arch-"$target_name" + + if test -d "$android_platform" ; then + AC_MSG_RESULT([$android_platform]) + else + AC_MSG_ERROR([not found. You have to specify --with-android-platform=/path/to/ndk/platform.]) + fi + NSPR_CONFIGURE_ARGS="$NSPR_CONFIGURE_ARGS --with-android-platform=$android_platform" + fi + + dnl Old NDK support. If minimum requirement is changed to NDK r8b, + dnl please remove this. + case "$target_cpu" in + i?86) + if ! test -e "$android_toolchain"/bin/"$android_tool_prefix"-gcc; then + dnl Old NDK toolchain name + android_tool_prefix="i686-android-linux" + fi + ;; + esac + + dnl set up compilers + TOOLCHAIN_PREFIX="$android_toolchain/bin/$android_tool_prefix-" + AS="$android_toolchain"/bin/"$android_tool_prefix"-as + CC="$android_toolchain"/bin/"$android_tool_prefix"-gcc + CXX="$android_toolchain"/bin/"$android_tool_prefix"-g++ + CPP="$android_toolchain"/bin/"$android_tool_prefix"-cpp + LD="$android_toolchain"/bin/"$android_tool_prefix"-ld + AR="$android_toolchain"/bin/"$android_tool_prefix"-ar + RANLIB="$android_toolchain"/bin/"$android_tool_prefix"-ranlib + STRIP="$android_toolchain"/bin/"$android_tool_prefix"-strip + OBJCOPY="$android_toolchain"/bin/"$android_tool_prefix"-objcopy + + CPPFLAGS="-isystem $android_platform/usr/include $CPPFLAGS" + CFLAGS="-mandroid -fno-short-enums -fno-exceptions $CFLAGS" + CXXFLAGS="-mandroid -fno-short-enums -fno-exceptions -Wno-psabi $CXXFLAGS" + ASFLAGS="-isystem $android_platform/usr/include -DANDROID $ASFLAGS" + + dnl Add -llog by default, since we use it all over the place. + dnl Add --allow-shlib-undefined, because libGLESv2 links to an + dnl undefined symbol (present on the hardware, just not in the + dnl NDK.) + LDFLAGS="-mandroid -L$android_platform/usr/lib -Wl,-rpath-link=$android_platform/usr/lib --sysroot=$android_platform -llog -Wl,--allow-shlib-undefined $LDFLAGS" + dnl prevent cross compile section from using these flags as host flags + if test -z "$HOST_CPPFLAGS" ; then + HOST_CPPFLAGS=" " + fi + if test -z "$HOST_CFLAGS" ; then + HOST_CFLAGS=" " + fi + if test -z "$HOST_CXXFLAGS" ; then + HOST_CXXFLAGS=" " + fi + if test -z "$HOST_LDFLAGS" ; then + HOST_LDFLAGS=" " + fi + + ANDROID_NDK="${android_ndk}" + ANDROID_TOOLCHAIN="${android_toolchain}" + ANDROID_PLATFORM="${android_platform}" + + AC_DEFINE(ANDROID) + CROSS_COMPILE=1 + AC_SUBST(ANDROID_NDK) + AC_SUBST(ANDROID_TOOLCHAIN) + AC_SUBST(ANDROID_PLATFORM) + + ;; +esac + +]) + +AC_DEFUN([MOZ_ANDROID_STLPORT], +[ + +if test "$OS_TARGET" = "Android" -a -z "$gonkdir"; then + case "${CPU_ARCH}-${MOZ_ARCH}" in + arm-armv7*) + ANDROID_CPU_ARCH=armeabi-v7a + ;; + arm-*) + ANDROID_CPU_ARCH=armeabi + ;; + x86-*) + ANDROID_CPU_ARCH=x86 + ;; + mips-*) # When target_cpu is mipsel, CPU_ARCH is mips + ANDROID_CPU_ARCH=mips + ;; + esac + + AC_SUBST(ANDROID_CPU_ARCH) + + if test -z "$STLPORT_CPPFLAGS$STLPORT_LDFLAGS$STLPORT_LIBS"; then + if test -n "$MOZ_ANDROID_LIBSTDCXX" ; then + if test -e "$android_ndk/sources/cxx-stl/gnu-libstdc++/$android_gnu_compiler_version/libs/$ANDROID_CPU_ARCH/libgnustl_static.a"; then + # android-ndk-r8b + STLPORT_LDFLAGS="-L$android_ndk/sources/cxx-stl/gnu-libstdc++/$android_gnu_compiler_version/libs/$ANDROID_CPU_ARCH/" + STLPORT_CPPFLAGS="-I$android_ndk/sources/cxx-stl/gnu-libstdc++/$android_gnu_compiler_version/include -I$android_ndk/sources/cxx-stl/gnu-libstdc++/$android_gnu_compiler_version/libs/$ANDROID_CPU_ARCH/include" + STLPORT_LIBS="-lgnustl_static" + elif test -e "$android_ndk/sources/cxx-stl/gnu-libstdc++/libs/$ANDROID_CPU_ARCH/libgnustl_static.a"; then + # android-ndk-r7, android-ndk-r7b, android-ndk-r8 + STLPORT_LDFLAGS="-L$android_ndk/sources/cxx-stl/gnu-libstdc++/libs/$ANDROID_CPU_ARCH/" + STLPORT_CPPFLAGS="-I$android_ndk/sources/cxx-stl/gnu-libstdc++/include -I$android_ndk/sources/cxx-stl/gnu-libstdc++/libs/$ANDROID_CPU_ARCH/include" + STLPORT_LIBS="-lgnustl_static" + elif test -e "$android_ndk/sources/cxx-stl/gnu-libstdc++/libs/$ANDROID_CPU_ARCH/libstdc++.a"; then + # android-ndk-r5c, android-ndk-r6, android-ndk-r6b + STLPORT_CPPFLAGS="-I$android_ndk/sources/cxx-stl/gnu-libstdc++/include -I$android_ndk/sources/cxx-stl/gnu-libstdc++/libs/$ANDROID_CPU_ARCH/include" + STLPORT_LDFLAGS="-L$android_ndk/sources/cxx-stl/gnu-libstdc++/libs/$ANDROID_CPU_ARCH/" + STLPORT_LIBS="-lstdc++" + else + AC_MSG_ERROR([Couldn't find path to gnu-libstdc++ in the android ndk]) + fi + elif test -e "$android_ndk/sources/cxx-stl/stlport/src/iostream.cpp" ; then + if test -e "$android_ndk/sources/cxx-stl/stlport/libs/$ANDROID_CPU_ARCH/libstlport_static.a"; then + STLPORT_LDFLAGS="-L$_objdir/build/stlport -L$android_ndk/sources/cxx-stl/stlport/libs/$ANDROID_CPU_ARCH/" + elif test -e "$android_ndk/tmp/ndk-digit/build/install/sources/cxx-stl/stlport/libs/$ANDROID_CPU_ARCH/libstlport_static.a"; then + STLPORT_LDFLAGS="-L$_objdir/build/stlport -L$android_ndk/tmp/ndk-digit/build/install/sources/cxx-stl/stlport/libs/$ANDROID_CPU_ARCH/" + else + AC_MSG_ERROR([Couldn't find path to stlport in the android ndk]) + fi + STLPORT_SOURCES="$android_ndk/sources/cxx-stl/stlport" + STLPORT_CPPFLAGS="-I$_objdir/build/stlport -I$android_ndk/sources/cxx-stl/stlport/stlport" + STLPORT_LIBS="-lstlport_static -static-libstdc++" + elif test "$target" != "arm-android-eabi"; then + dnl fail if we're not building with NDKr4 + AC_MSG_ERROR([Couldn't find path to stlport in the android ndk]) + fi + fi + CXXFLAGS="$CXXFLAGS $STLPORT_CPPFLAGS" + LDFLAGS="$LDFLAGS $STLPORT_LDFLAGS" + LIBS="$LIBS $STLPORT_LIBS" +fi +AC_SUBST([STLPORT_SOURCES]) + +]) + +AC_DEFUN([MOZ_ANDROID_SDK], +[ + +MOZ_ARG_WITH_STRING(android-sdk, +[ --with-android-sdk=DIR + location where the Android SDK can be found (base directory, e.g. .../android/platforms/android-6)], + android_sdk=$withval) + +case "$target" in +*-android*|*-linuxandroid*) + if test -z "$android_sdk" ; then + AC_MSG_ERROR([You must specify --with-android-sdk=/path/to/sdk when targeting Android.]) + else + if ! test -e "$android_sdk"/source.properties ; then + AC_MSG_ERROR([The path in --with-android-sdk isn't valid (source.properties hasn't been found).]) + fi + + # Get the api level from "$android_sdk"/source.properties. + android_api_level=`$AWK -F = changequote(<<, >>)'<<$>>1 == "AndroidVersion.ApiLevel" {print <<$>>2}'changequote([, ]) "$android_sdk"/source.properties` + + if test -z "$android_api_level" ; then + AC_MSG_ERROR([Unexpected error: no AndroidVersion.ApiLevel field has been found in source.properties.]) + fi + + if ! test "$android_api_level" -eq "$android_api_level" ; then + AC_MSG_ERROR([Unexpected error: the found android api value isn't a number! (found $android_api_level)]) + fi + + if test $android_api_level -lt $1 ; then + AC_MSG_ERROR([The given Android SDK provides API level $android_api_level ($1 or higher required).]) + fi + fi + + android_platform_tools="$android_sdk"/../../platform-tools + if test ! -d "$android_platform_tools" ; then + android_platform_tools="$android_sdk"/tools # SDK Tools < r8 + fi + # The build tools got moved around to different directories in + # SDK Tools r22. Try to locate them. + android_build_tools="" + for suffix in 17.0.0 android-4.2.2; do + tools_directory="$android_sdk/../../build-tools/$suffix" + if test -d "$tools_directory" ; then + android_build_tools="$tools_directory" + break + fi + done + if test -z "$android_build_tools" ; then + android_build_tools="$android_platform_tools" # SDK Tools < r22 + fi + ANDROID_SDK="${android_sdk}" + if test -e "${android_sdk}/../../extras/android/compatibility/v4/android-support-v4.jar" ; then + ANDROID_COMPAT_LIB="${android_sdk}/../../extras/android/compatibility/v4/android-support-v4.jar" + else + ANDROID_COMPAT_LIB="${android_sdk}/../../extras/android/support/v4/android-support-v4.jar"; + fi + ANDROID_PLATFORM_TOOLS="${android_platform_tools}" + ANDROID_BUILD_TOOLS="${android_build_tools}" + AC_SUBST(ANDROID_SDK) + AC_SUBST(ANDROID_COMPAT_LIB) + if ! test -e $ANDROID_COMPAT_LIB ; then + AC_MSG_ERROR([You must download the Android support library when targeting Android. Run the Android SDK tool and install Android Support Library under Extras. See https://developer.android.com/tools/extras/support-library.html for more info. (looked for $ANDROID_COMPAT_LIB)]) + fi + AC_SUBST(ANDROID_PLATFORM_TOOLS) + AC_SUBST(ANDROID_BUILD_TOOLS) + ;; +esac + +]) diff --git a/build/autoconf/arch.m4 b/build/autoconf/arch.m4 new file mode 100644 index 000000000..ed7b2cd3b --- /dev/null +++ b/build/autoconf/arch.m4 @@ -0,0 +1,247 @@ +dnl This Source Code Form is subject to the terms of the Mozilla Public +dnl License, v. 2.0. If a copy of the MPL was not distributed with this +dnl file, You can obtain one at http://mozilla.org/MPL/2.0/. + +AC_DEFUN([MOZ_ARCH_OPTS], +[ + +dnl ======================================================== +dnl = ARM toolchain tweaks +dnl ======================================================== + +MOZ_THUMB=toolchain-default +MOZ_THUMB_INTERWORK=toolchain-default +MOZ_FPU=toolchain-default +MOZ_FLOAT_ABI=toolchain-default +MOZ_SOFT_FLOAT=toolchain-default + +MOZ_ARG_WITH_STRING(arch, +[ --with-arch=[[type|toolchain-default]] + Use specific CPU features (-march=type). Resets + thumb, fpu, float-abi, etc. defaults when set], + if test -z "$GNU_CC"; then + AC_MSG_ERROR([--with-arch is not supported on non-GNU toolchains]) + fi + MOZ_ARCH=$withval) + +if test -z "$MOZ_ARCH"; then + dnl Defaults + case "${CPU_ARCH}-${OS_TARGET}" in + arm-Android) + MOZ_THUMB=yes + MOZ_ARCH=armv7-a + MOZ_FPU=vfp + MOZ_FLOAT_ABI=softfp + ;; + arm-Darwin) + MOZ_ARCH=toolchain-default + MOZ_THUMB=yes + ;; + arm-*) + if test -n "$MOZ_PLATFORM_MAEMO"; then + MOZ_THUMB=no + MOZ_ARCH=armv7-a + MOZ_FLOAT_ABI=softfp + fi + if test "$MOZ_PLATFORM_MAEMO" = 6; then + MOZ_THUMB=yes + fi + ;; + esac +fi + +if test "$MOZ_ARCH" = "armv6" -a "$OS_TARGET" = "Android"; then + MOZ_FPU=vfp +fi + +MOZ_ARG_WITH_STRING(thumb, +[ --with-thumb[[=yes|no|toolchain-default]]] +[ Use Thumb instruction set (-mthumb)], + if test -z "$GNU_CC"; then + AC_MSG_ERROR([--with-thumb is not supported on non-GNU toolchains]) + fi + MOZ_THUMB=$withval) + +MOZ_ARG_WITH_STRING(thumb-interwork, +[ --with-thumb-interwork[[=yes|no|toolchain-default]] + Use Thumb/ARM instuctions interwork (-mthumb-interwork)], + if test -z "$GNU_CC"; then + AC_MSG_ERROR([--with-thumb-interwork is not supported on non-GNU toolchains]) + fi + MOZ_THUMB_INTERWORK=$withval) + +MOZ_ARG_WITH_STRING(fpu, +[ --with-fpu=[[type|toolchain-default]] + Use specific FPU type (-mfpu=type)], + if test -z "$GNU_CC"; then + AC_MSG_ERROR([--with-fpu is not supported on non-GNU toolchains]) + fi + MOZ_FPU=$withval) + +MOZ_ARG_WITH_STRING(float-abi, +[ --with-float-abi=[[type|toolchain-default]] + Use specific arm float ABI (-mfloat-abi=type)], + if test -z "$GNU_CC"; then + AC_MSG_ERROR([--with-float-abi is not supported on non-GNU toolchains]) + fi + MOZ_FLOAT_ABI=$withval) + +MOZ_ARG_WITH_STRING(soft-float, +[ --with-soft-float[[=yes|no|toolchain-default]] + Use soft float library (-msoft-float)], + if test -z "$GNU_CC"; then + AC_MSG_ERROR([--with-soft-float is not supported on non-GNU toolchains]) + fi + MOZ_SOFT_FLOAT=$withval) + +case "$MOZ_ARCH" in +toolchain-default|"") + arch_flag="" + ;; +*) + arch_flag="-march=$MOZ_ARCH" + ;; +esac + +case "$MOZ_THUMB" in +yes) + MOZ_THUMB2=1 + thumb_flag="-mthumb" + ;; +no) + MOZ_THUMB2= + thumb_flag="-marm" + ;; +*) + _SAVE_CFLAGS="$CFLAGS" + CFLAGS="$arch_flag" + AC_TRY_COMPILE([],[return sizeof(__thumb2__);], + MOZ_THUMB2=1, + MOZ_THUMB2=) + CFLAGS="$_SAVE_CFLAGS" + thumb_flag="" + ;; +esac + +if test "$MOZ_THUMB2" = 1; then + AC_DEFINE(MOZ_THUMB2) +fi + +case "$MOZ_THUMB_INTERWORK" in +yes) + thumb_interwork_flag="-mthumb-interwork" + ;; +no) + thumb_interwork_flag="-mno-thumb-interwork" + ;; +*) # toolchain-default + thumb_interwork_flag="" + ;; +esac + +case "$MOZ_FPU" in +toolchain-default|"") + fpu_flag="" + ;; +*) + fpu_flag="-mfpu=$MOZ_FPU" + ;; +esac + +case "$MOZ_FLOAT_ABI" in +toolchain-default|"") + float_abi_flag="" + ;; +*) + float_abi_flag="-mfloat-abi=$MOZ_FLOAT_ABI" + ;; +esac + +case "$MOZ_SOFT_FLOAT" in +yes) + soft_float_flag="-msoft-float" + ;; +no) + soft_float_flag="-mno-soft-float" + ;; +*) # toolchain-default + soft_float_flag="" + ;; +esac + +dnl Use echo to avoid accumulating space characters +all_flags=`echo $arch_flag $thumb_flag $thumb_interwork_flag $fpu_flag $float_abi_flag $soft_float_flag` +if test -n "$all_flags"; then + _SAVE_CFLAGS="$CFLAGS" + CFLAGS="$all_flags" + AC_MSG_CHECKING(whether the chosen combination of compiler flags ($all_flags) works) + AC_TRY_COMPILE([],[return 0;], + AC_MSG_RESULT([yes]), + AC_MSG_ERROR([no])) + + CFLAGS="$_SAVE_CFLAGS $all_flags" + CXXFLAGS="$CXXFLAGS $all_flags" + ASFLAGS="$ASFLAGS $all_flags" + if test -n "$thumb_flag"; then + LDFLAGS="$LDFLAGS $thumb_flag" + fi +fi + +AC_SUBST(MOZ_THUMB2) + +if test "$CPU_ARCH" = "arm"; then + AC_MSG_CHECKING(for ARM SIMD support in compiler) + # We try to link so that this also fails when + # building with LTO. + AC_TRY_LINK([], + [asm("uqadd8 r1, r1, r2");], + result="yes", result="no") + AC_MSG_RESULT("$result") + if test "$result" = "yes"; then + AC_DEFINE(HAVE_ARM_SIMD) + HAVE_ARM_SIMD=1 + fi + + AC_MSG_CHECKING(ARM version support in compiler) + dnl Determine the target ARM architecture (5 for ARMv5, v5T, v5E, etc.; 6 for ARMv6, v6K, etc.) + ARM_ARCH=`${CC-cc} ${CFLAGS} -dM -E - < /dev/null | sed -n 's/.*__ARM_ARCH_\([[0-9]]*\).*/\1/p'` + AC_MSG_RESULT("$ARM_ARCH") + + AC_MSG_CHECKING(for ARM NEON support in compiler) + # We try to link so that this also fails when + # building with LTO. + AC_TRY_LINK([], + [asm(".fpu neon\n vadd.i8 d0, d0, d0");], + result="yes", result="no") + AC_MSG_RESULT("$result") + if test "$result" = "yes"; then + AC_DEFINE(HAVE_ARM_NEON) + HAVE_ARM_NEON=1 + + dnl We don't need to build NEON support if we're targetting a non-NEON device. + dnl This matches media/webrtc/trunk/webrtc/build/common.gypi. + if test -n "$ARM_ARCH"; then + if test "$ARM_ARCH" -lt 7; then + BUILD_ARM_NEON=0 + else + BUILD_ARM_NEON=1 + fi + fi + fi + +fi # CPU_ARCH = arm + +AC_SUBST(HAVE_ARM_SIMD) +AC_SUBST(HAVE_ARM_NEON) +AC_SUBST(BUILD_ARM_NEON) + +if test -n "$MOZ_ARCH"; then + NSPR_CONFIGURE_ARGS="$NSPR_CONFIGURE_ARGS --with-arch=$MOZ_ARCH" + NSPR_CONFIGURE_ARGS="$NSPR_CONFIGURE_ARGS --with-thumb=$MOZ_THUMB" + NSPR_CONFIGURE_ARGS="$NSPR_CONFIGURE_ARGS --with-thumb-interwork=$MOZ_THUMB_INTERWORK" + NSPR_CONFIGURE_ARGS="$NSPR_CONFIGURE_ARGS --with-fpu=$MOZ_FPU" + NSPR_CONFIGURE_ARGS="$NSPR_CONFIGURE_ARGS --with-float-abi=$MOZ_FLOAT_ABI" + NSPR_CONFIGURE_ARGS="$NSPR_CONFIGURE_ARGS --with-soft-float=$MOZ_SOFT_FLOAT" +fi + +]) diff --git a/build/autoconf/ccache.m4 b/build/autoconf/ccache.m4 new file mode 100644 index 000000000..5e2469195 --- /dev/null +++ b/build/autoconf/ccache.m4 @@ -0,0 +1,36 @@ +dnl This Source Code Form is subject to the terms of the Mozilla Public +dnl License, v. 2.0. If a copy of the MPL was not distributed with this +dnl file, You can obtain one at http://mozilla.org/MPL/2.0/. + +dnl ====================================================== +dnl = Enable compiling with ccache +dnl ====================================================== +AC_DEFUN([MOZ_CHECK_CCACHE], +[ +MOZ_ARG_WITH_STRING(ccache, +[ --with-ccache[=path/to/ccache] + Enable compiling with ccache], + CCACHE=$withval, CCACHE="no") + +if test "$CCACHE" != "no"; then + if test -z "$CCACHE" -o "$CCACHE" = "yes"; then + CCACHE= + else + if test ! -e "$CCACHE"; then + AC_MSG_ERROR([$CCACHE not found]) + fi + fi + MOZ_PATH_PROGS(CCACHE, $CCACHE ccache) + if test -z "$CCACHE" -o "$CCACHE" = ":"; then + AC_MSG_ERROR([ccache not found]) + elif test -x "$CCACHE"; then + CC="$CCACHE $CC" + CXX="$CCACHE $CXX" + MOZ_USING_CCACHE=1 + else + AC_MSG_ERROR([$CCACHE is not executable]) + fi +fi + +AC_SUBST(MOZ_USING_CCACHE) +]) diff --git a/build/autoconf/check_debug_ranges.py b/build/autoconf/check_debug_ranges.py new file mode 100644 index 000000000..04cf41ff3 --- /dev/null +++ b/build/autoconf/check_debug_ranges.py @@ -0,0 +1,60 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +# This script returns the number of items for the DW_AT_ranges corresponding +# to a given compilation unit. This is used as a helper to find a bug in some +# versions of GNU ld. + +import subprocess +import sys +import re + +def get_range_for(compilation_unit, debug_info): + '''Returns the range offset for a given compilation unit + in a given debug_info.''' + name = ranges = '' + search_cu = False + for nfo in debug_info.splitlines(): + if 'DW_TAG_compile_unit' in nfo: + search_cu = True + elif 'DW_TAG_' in nfo or not nfo.strip(): + if name == compilation_unit: + return int(ranges, 16) + name = ranges = '' + search_cu = False + if search_cu: + if 'DW_AT_name' in nfo: + name = nfo.rsplit(None, 1)[1] + elif 'DW_AT_ranges' in nfo: + ranges = nfo.rsplit(None, 1)[1] + return None + +def get_range_length(range, debug_ranges): + '''Returns the number of items in the range starting at the + given offset.''' + length = 0 + for line in debug_ranges.splitlines(): + m = re.match('\s*([0-9a-fA-F]+)\s+([0-9a-fA-F]+)\s+([0-9a-fA-F]+)', line) + if m and int(m.group(1), 16) == range: + length += 1 + return length + +def main(bin, compilation_unit): + p = subprocess.Popen(['objdump', '-W', bin], stdout = subprocess.PIPE, stderr = subprocess.PIPE) + (out, err) = p.communicate() + sections = re.split('\n(Contents of the|The section) ', out) + debug_info = [s for s in sections if s.startswith('.debug_info')] + debug_ranges = [s for s in sections if s.startswith('.debug_ranges')] + if not debug_ranges or not debug_info: + return 0 + + range = get_range_for(compilation_unit, debug_info[0]) + if range is not None: + return get_range_length(range, debug_ranges[0]) + + return -1 + + +if __name__ == '__main__': + print main(*sys.argv[1:]) diff --git a/build/autoconf/clean-config.sh b/build/autoconf/clean-config.sh new file mode 100644 index 000000000..2505547be --- /dev/null +++ b/build/autoconf/clean-config.sh @@ -0,0 +1,31 @@ +#!/bin/sh +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +# clean-config.sh - Removes all files generated by mozilla configure. +# Only removes files from the topsrcdir. To clean up an objdir, +# simply remove the directory and start over. +# +# Usage: +# 1. cd <topsrcdir> +# 2. build/autoconf/clean-config.sh +# +# Send comments, improvements, bugs to slamm@netscape.com + +topsrcdir=`cd \`dirname $0\`/../..; pwd` + +if [ ! -f configure.in ]; then + echo "clean-config.sh only cleans the source tree. To run," 2>&1 + echo " cd $topsrcdir; build/autoconf/clean-config.sh" 2>&1 + echo " (To clean a separate objdir, simple remove the directory.)" 2>&1 + exit 1 +fi + +rm -fr \ + config-defs.h \ + config.cache \ + config.log \ + config.status \ + $NULL diff --git a/build/autoconf/codeset.m4 b/build/autoconf/codeset.m4 new file mode 100644 index 000000000..4dc1d5018 --- /dev/null +++ b/build/autoconf/codeset.m4 @@ -0,0 +1,24 @@ +# codeset.m4 serial AM1 (gettext-0.10.40) +dnl Copyright (C) 2000-2002 Free Software Foundation, Inc. +dnl This file is free software, distributed under the terms of the GNU +dnl General Public License. As a special exception to the GNU General +dnl Public License, this file may be distributed as part of a program +dnl that contains a configuration script generated by Autoconf, under +dnl the same distribution terms as the rest of that program. + +dnl From Bruno Haible. + +AC_DEFUN([AM_LANGINFO_CODESET], +[ + AC_CACHE_CHECK([for nl_langinfo and CODESET], am_cv_langinfo_codeset, + [AC_TRY_LINK([#include <langinfo.h>], + [char* cs = nl_langinfo(CODESET);], + am_cv_langinfo_codeset=yes, + am_cv_langinfo_codeset=no) + ]) + if test $am_cv_langinfo_codeset = yes; then + AC_DEFINE(HAVE_LANGINFO_CODESET, 1, + [Define if you have <langinfo.h> and nl_langinfo(CODESET).]) + HAVE_LANGINFO_CODESET=1 + fi +]) diff --git a/build/autoconf/compiler-opts.m4 b/build/autoconf/compiler-opts.m4 new file mode 100644 index 000000000..e09a4563c --- /dev/null +++ b/build/autoconf/compiler-opts.m4 @@ -0,0 +1,223 @@ +dnl This Source Code Form is subject to the terms of the Mozilla Public +dnl License, v. 2.0. If a copy of the MPL was not distributed with this +dnl file, You can obtain one at http://mozilla.org/MPL/2.0/. + +dnl Add compiler specific options + +AC_DEFUN([MOZ_DEFAULT_COMPILER], +[ +dnl Default to MSVC for win32 and gcc-4.2 for darwin +dnl ============================================================== +if test -z "$CROSS_COMPILE"; then +case "$target" in +*-mingw*) + if test -z "$CC"; then CC=cl; fi + if test -z "$CXX"; then CXX=cl; fi + if test -z "$CPP"; then CPP="cl -E -nologo"; fi + if test -z "$CXXCPP"; then CXXCPP="cl -TP -E -nologo"; ac_cv_prog_CXXCPP="$CXXCPP"; fi + if test -z "$LD"; then LD=link; fi + if test -z "$AS"; then + case "${target_cpu}" in + i*86) + AS=ml; + ;; + x86_64) + AS=ml64; + ;; + esac + fi + if test -z "$MIDL"; then MIDL=midl; fi + + # need override this flag since we don't use $(LDFLAGS) for this. + if test -z "$HOST_LDFLAGS" ; then + HOST_LDFLAGS=" " + fi + ;; +*-darwin*) + # GCC on darwin is based on gcc 4.2 and we don't support it anymore. + if test -z "$CC"; then + MOZ_PATH_PROGS(CC, clang) + fi + if test -z "$CXX"; then + MOZ_PATH_PROGS(CXX, clang++) + fi + IS_GCC=$($CC -v 2>&1 | grep gcc) + if test -n "$IS_GCC" + then + echo gcc is known to be broken on OS X, please use clang. + echo see http://developer.mozilla.org/en-US/docs/Developer_Guide/Build_Instructions/Mac_OS_X_Prerequisites + echo for more information. + exit 1 + fi + ;; +esac +fi +]) + +dnl ============================================================================ +dnl C++ rtti +dnl We don't use it in the code, but it can be usefull for debugging, so give +dnl the user the option of enabling it. +dnl ============================================================================ +AC_DEFUN([MOZ_RTTI], +[ +MOZ_ARG_ENABLE_BOOL(cpp-rtti, +[ --enable-cpp-rtti Enable C++ RTTI ], +[ _MOZ_USE_RTTI=1 ], +[ _MOZ_USE_RTTI= ]) + +if test -z "$_MOZ_USE_RTTI"; then + if test "$GNU_CC"; then + CXXFLAGS="$CXXFLAGS -fno-rtti" + else + case "$target" in + *-mingw*) + CXXFLAGS="$CXXFLAGS -GR-" + esac + fi +fi +]) + +dnl A high level macro for selecting compiler options. +AC_DEFUN([MOZ_COMPILER_OPTS], +[ + MOZ_RTTI +if test "$CLANG_CXX"; then + ## We disable return-type-c-linkage because jsval is defined as a C++ type but is + ## returned by C functions. This is possible because we use knowledge about the ABI + ## to typedef it to a C type with the same layout when the headers are included + ## from C. + ## + ## mismatched-tags is disabled (bug 780474) mostly because it's useless. + ## Worse, it's not supported by gcc, so it will cause tryserver bustage + ## without any easy way for non-Clang users to check for it. + _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Wno-unknown-warning-option -Wno-return-type-c-linkage -Wno-mismatched-tags" +fi + +if test -z "$GNU_CC"; then + case "$target" in + *-mingw*) + ## Warning 4099 (equivalent of mismatched-tags) is disabled (bug 780474) + ## for the same reasons as above. + _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -wd4099" + esac +fi + +if test "$GNU_CC"; then + CFLAGS="$CFLAGS -ffunction-sections -fdata-sections" + CXXFLAGS="$CXXFLAGS -ffunction-sections -fdata-sections -fno-exceptions" +fi + +dnl ======================================================== +dnl = Identical Code Folding +dnl ======================================================== + +MOZ_ARG_DISABLE_BOOL(icf, +[ --disable-icf Disable Identical Code Folding], + MOZ_DISABLE_ICF=1, + MOZ_DISABLE_ICF= ) + +if test "$GNU_CC" -a "$GCC_USE_GNU_LD" -a -z "$MOZ_DISABLE_ICF"; then + AC_CACHE_CHECK([whether the linker supports Identical Code Folding], + LD_SUPPORTS_ICF, + [echo 'int foo() {return 42;}' \ + 'int bar() {return 42;}' \ + 'int main() {return foo() - bar();}' > conftest.${ac_ext} + # If the linker supports ICF, foo and bar symbols will have + # the same address + if AC_TRY_COMMAND([${CC-cc} -o conftest${ac_exeext} $LDFLAGS -Wl,--icf=safe -ffunction-sections conftest.${ac_ext} $LIBS 1>&2]) && + test -s conftest${ac_exeext} && + objdump -t conftest${ac_exeext} | awk changequote(<<, >>)'{a[<<$>>6] = <<$>>1} END {if (a["foo"] && (a["foo"] != a["bar"])) { exit 1 }}'changequote([, ]); then + LD_SUPPORTS_ICF=yes + else + LD_SUPPORTS_ICF=no + fi + rm -rf conftest*]) + if test "$LD_SUPPORTS_ICF" = yes; then + _SAVE_LDFLAGS="$LDFLAGS -Wl,--icf=safe" + LDFLAGS="$LDFLAGS -Wl,--icf=safe -Wl,--print-icf-sections" + AC_TRY_LINK([], [], + [LD_PRINT_ICF_SECTIONS=-Wl,--print-icf-sections], + [LD_PRINT_ICF_SECTIONS=]) + AC_SUBST([LD_PRINT_ICF_SECTIONS]) + LDFLAGS="$_SAVE_LDFLAGS" + fi +fi + +dnl ======================================================== +dnl = Automatically remove dead symbols +dnl ======================================================== + +if test "$GNU_CC" -a "$GCC_USE_GNU_LD" -a -n "$MOZ_DEBUG_FLAGS"; then + dnl See bug 670659 + AC_CACHE_CHECK([whether removing dead symbols breaks debugging], + GC_SECTIONS_BREAKS_DEBUG_RANGES, + [echo 'int foo() {return 42;}' \ + 'int bar() {return 1;}' \ + 'int main() {return foo();}' > conftest.${ac_ext} + if AC_TRY_COMMAND([${CC-cc} -o conftest.${ac_objext} $CFLAGS $MOZ_DEBUG_FLAGS -c conftest.${ac_ext} 1>&2]) && + AC_TRY_COMMAND([${CC-cc} -o conftest${ac_exeext} $LDFLAGS $MOZ_DEBUG_FLAGS -Wl,--gc-sections conftest.${ac_objext} $LIBS 1>&2]) && + test -s conftest${ac_exeext} -a -s conftest.${ac_objext}; then + if test "`$PYTHON "$_topsrcdir"/build/autoconf/check_debug_ranges.py conftest.${ac_objext} conftest.${ac_ext}`" = \ + "`$PYTHON "$_topsrcdir"/build/autoconf/check_debug_ranges.py conftest${ac_exeext} conftest.${ac_ext}`"; then + GC_SECTIONS_BREAKS_DEBUG_RANGES=no + else + GC_SECTIONS_BREAKS_DEBUG_RANGES=yes + fi + else + dnl We really don't expect to get here, but just in case + GC_SECTIONS_BREAKS_DEBUG_RANGES="no, but it's broken in some other way" + fi + rm -rf conftest*]) + if test "$GC_SECTIONS_BREAKS_DEBUG_RANGES" = no; then + DSO_LDOPTS="$DSO_LDOPTS -Wl,--gc-sections" + fi +fi + +]) + +dnl GCC and clang will fail if given an unknown warning option like -Wfoobar. +dnl But later versions won't fail if given an unknown negated warning option +dnl like -Wno-foobar. So when we are check for support of negated warning +dnl options, we actually test the positive form, but add the negated form to +dnl the flags variable. + +AC_DEFUN([MOZ_C_SUPPORTS_WARNING], +[ + AC_CACHE_CHECK(whether the C compiler supports $1$2, $3, + [ + AC_LANG_SAVE + AC_LANG_C + _SAVE_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS -Werror -W$2" + AC_TRY_COMPILE([], + [return(0);], + $3="yes", + $3="no") + CFLAGS="$_SAVE_CFLAGS" + AC_LANG_RESTORE + ]) + if test "${$3}" = "yes"; then + _WARNINGS_CFLAGS="${_WARNINGS_CFLAGS} $1$2" + fi +]) + +AC_DEFUN([MOZ_CXX_SUPPORTS_WARNING], +[ + AC_CACHE_CHECK(whether the C++ compiler supports $1$2, $3, + [ + AC_LANG_SAVE + AC_LANG_CPLUSPLUS + _SAVE_CXXFLAGS="$CXXFLAGS" + CXXFLAGS="$CXXFLAGS -Werror -W$2" + AC_TRY_COMPILE([], + [return(0);], + $3="yes", + $3="no") + CXXFLAGS="$_SAVE_CXXFLAGS" + AC_LANG_RESTORE + ]) + if test "${$3}" = "yes"; then + _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} $1$2" + fi +]) diff --git a/build/autoconf/config.guess b/build/autoconf/config.guess new file mode 100644 index 000000000..721b21a24 --- /dev/null +++ b/build/autoconf/config.guess @@ -0,0 +1,1537 @@ +#! /bin/sh +# Attempt to guess a canonical system name. +# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, +# 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 +# Free Software Foundation, Inc. + +timestamp='2009-08-19' + +# This file is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA +# 02110-1301, USA. +# +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + + +# Originally written by Per Bothner. Please send patches (context +# diff format) to <config-patches@gnu.org> and include a ChangeLog +# entry. +# +# This script attempts to guess a canonical system name similar to +# config.sub. If it succeeds, it prints the system name on stdout, and +# exits with 0. Otherwise, it exits with 1. +# +# You can get the latest version of this script from: +# http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess;hb=HEAD + +me=`echo "$0" | sed -e 's,.*/,,'` + +usage="\ +Usage: $0 [OPTION] + +Output the configuration name of the system \`$me' is run on. + +Operation modes: + -h, --help print this help, then exit + -t, --time-stamp print date of last modification, then exit + -v, --version print version number, then exit + +Report bugs and patches to <config-patches@gnu.org>." + +version="\ +GNU config.guess ($timestamp) + +Originally written by Per Bothner. +Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, +2002, 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc. + +This is free software; see the source for copying conditions. There is NO +warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." + +help=" +Try \`$me --help' for more information." + +# Parse command line +while test $# -gt 0 ; do + case $1 in + --time-stamp | --time* | -t ) + echo "$timestamp" ; exit ;; + --version | -v ) + echo "$version" ; exit ;; + --help | --h* | -h ) + echo "$usage"; exit ;; + -- ) # Stop option processing + shift; break ;; + - ) # Use stdin as input. + break ;; + -* ) + echo "$me: invalid option $1$help" >&2 + exit 1 ;; + * ) + break ;; + esac +done + +if test $# != 0; then + echo "$me: too many arguments$help" >&2 + exit 1 +fi + +trap 'exit 1' 1 2 15 + +# CC_FOR_BUILD -- compiler used by this script. Note that the use of a +# compiler to aid in system detection is discouraged as it requires +# temporary files to be created and, as you can see below, it is a +# headache to deal with in a portable fashion. + +# Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still +# use `HOST_CC' if defined, but it is deprecated. + +# Portable tmp directory creation inspired by the Autoconf team. + +set_cc_for_build=' +trap "exitcode=\$?; (rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null) && exit \$exitcode" 0 ; +trap "rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null; exit 1" 1 2 13 15 ; +: ${TMPDIR=/tmp} ; + { tmp=`(umask 077 && mktemp -d "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } || + { test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir $tmp) ; } || + { tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir $tmp) && echo "Warning: creating insecure temp directory" >&2 ; } || + { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; } ; +dummy=$tmp/dummy ; +tmpfiles="$dummy.c $dummy.o $dummy.rel $dummy" ; +case $CC_FOR_BUILD,$HOST_CC,$CC in + ,,) echo "int x;" > $dummy.c ; + for c in cc gcc c89 c99 ; do + if ($c -c -o $dummy.o $dummy.c) >/dev/null 2>&1 ; then + CC_FOR_BUILD="$c"; break ; + fi ; + done ; + if test x"$CC_FOR_BUILD" = x ; then + CC_FOR_BUILD=no_compiler_found ; + fi + ;; + ,,*) CC_FOR_BUILD=$CC ;; + ,*,*) CC_FOR_BUILD=$HOST_CC ;; +esac ; set_cc_for_build= ;' + +# This is needed to find uname on a Pyramid OSx when run in the BSD universe. +# (ghazi@noc.rutgers.edu 1994-08-24) +if (test -f /.attbin/uname) >/dev/null 2>&1 ; then + PATH=$PATH:/.attbin ; export PATH +fi + +UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown +UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown +UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown +UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown + +# Note: order is significant - the case branches are not exclusive. + +case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in + *:NetBSD:*:*) + # NetBSD (nbsd) targets should (where applicable) match one or + # more of the tupples: *-*-netbsdelf*, *-*-netbsdaout*, + # *-*-netbsdecoff* and *-*-netbsd*. For targets that recently + # switched to ELF, *-*-netbsd* would select the old + # object file format. This provides both forward + # compatibility and a consistent mechanism for selecting the + # object file format. + # + # Note: NetBSD doesn't particularly care about the vendor + # portion of the name. We always set it to "unknown". + sysctl="sysctl -n hw.machine_arch" + UNAME_MACHINE_ARCH=`(/sbin/$sysctl 2>/dev/null || \ + /usr/sbin/$sysctl 2>/dev/null || echo unknown)` + case "${UNAME_MACHINE_ARCH}" in + armeb) machine=armeb-unknown ;; + arm*) machine=arm-unknown ;; + sh3el) machine=shl-unknown ;; + sh3eb) machine=sh-unknown ;; + sh5el) machine=sh5le-unknown ;; + *) machine=${UNAME_MACHINE_ARCH}-unknown ;; + esac + # The Operating System including object format, if it has switched + # to ELF recently, or will in the future. + case "${UNAME_MACHINE_ARCH}" in + arm*|i386|m68k|ns32k|sh3*|sparc|vax) + eval $set_cc_for_build + if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \ + | grep -q __ELF__ + then + # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout). + # Return netbsd for either. FIX? + os=netbsd + else + os=netbsdelf + fi + ;; + *) + os=netbsd + ;; + esac + # The OS release + # Debian GNU/NetBSD machines have a different userland, and + # thus, need a distinct triplet. However, they do not need + # kernel version information, so it can be replaced with a + # suitable tag, in the style of linux-gnu. + case "${UNAME_VERSION}" in + Debian*) + release='-gnu' + ;; + *) + release=`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'` + ;; + esac + # Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM: + # contains redundant information, the shorter form: + # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used. + echo "${machine}-${os}${release}" + exit ;; + *:OpenBSD:*:*) + UNAME_MACHINE_ARCH=`arch | sed 's/OpenBSD.//'` + echo ${UNAME_MACHINE_ARCH}-unknown-openbsd${UNAME_RELEASE} + exit ;; + *:ekkoBSD:*:*) + echo ${UNAME_MACHINE}-unknown-ekkobsd${UNAME_RELEASE} + exit ;; + *:SolidBSD:*:*) + echo ${UNAME_MACHINE}-unknown-solidbsd${UNAME_RELEASE} + exit ;; + macppc:MirBSD:*:*) + echo powerpc-unknown-mirbsd${UNAME_RELEASE} + exit ;; + *:MirBSD:*:*) + echo ${UNAME_MACHINE}-unknown-mirbsd${UNAME_RELEASE} + exit ;; + alpha:OSF1:*:*) + case $UNAME_RELEASE in + *4.0) + UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'` + ;; + *5.*) + UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $4}'` + ;; + esac + # According to Compaq, /usr/sbin/psrinfo has been available on + # OSF/1 and Tru64 systems produced since 1995. I hope that + # covers most systems running today. This code pipes the CPU + # types through head -n 1, so we only detect the type of CPU 0. + ALPHA_CPU_TYPE=`/usr/sbin/psrinfo -v | sed -n -e 's/^ The alpha \(.*\) processor.*$/\1/p' | head -n 1` + case "$ALPHA_CPU_TYPE" in + "EV4 (21064)") + UNAME_MACHINE="alpha" ;; + "EV4.5 (21064)") + UNAME_MACHINE="alpha" ;; + "LCA4 (21066/21068)") + UNAME_MACHINE="alpha" ;; + "EV5 (21164)") + UNAME_MACHINE="alphaev5" ;; + "EV5.6 (21164A)") + UNAME_MACHINE="alphaev56" ;; + "EV5.6 (21164PC)") + UNAME_MACHINE="alphapca56" ;; + "EV5.7 (21164PC)") + UNAME_MACHINE="alphapca57" ;; + "EV6 (21264)") + UNAME_MACHINE="alphaev6" ;; + "EV6.7 (21264A)") + UNAME_MACHINE="alphaev67" ;; + "EV6.8CB (21264C)") + UNAME_MACHINE="alphaev68" ;; + "EV6.8AL (21264B)") + UNAME_MACHINE="alphaev68" ;; + "EV6.8CX (21264D)") + UNAME_MACHINE="alphaev68" ;; + "EV6.9A (21264/EV69A)") + UNAME_MACHINE="alphaev69" ;; + "EV7 (21364)") + UNAME_MACHINE="alphaev7" ;; + "EV7.9 (21364A)") + UNAME_MACHINE="alphaev79" ;; + esac + # A Pn.n version is a patched version. + # A Vn.n version is a released version. + # A Tn.n version is a released field test version. + # A Xn.n version is an unreleased experimental baselevel. + # 1.2 uses "1.2" for uname -r. + echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[PVTX]//' | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` + exit ;; + Alpha\ *:Windows_NT*:*) + # How do we know it's Interix rather than the generic POSIX subsystem? + # Should we change UNAME_MACHINE based on the output of uname instead + # of the specific Alpha model? + echo alpha-pc-interix + exit ;; + 21064:Windows_NT:50:3) + echo alpha-dec-winnt3.5 + exit ;; + Amiga*:UNIX_System_V:4.0:*) + echo m68k-unknown-sysv4 + exit ;; + *:[Aa]miga[Oo][Ss]:*:*) + echo ${UNAME_MACHINE}-unknown-amigaos + exit ;; + *:[Mm]orph[Oo][Ss]:*:*) + echo ${UNAME_MACHINE}-unknown-morphos + exit ;; + *:OS/390:*:*) + echo i370-ibm-openedition + exit ;; + *:z/VM:*:*) + echo s390-ibm-zvmoe + exit ;; + *:OS400:*:*) + echo powerpc-ibm-os400 + exit ;; + arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*) + echo arm-acorn-riscix${UNAME_RELEASE} + exit ;; + arm:riscos:*:*|arm:RISCOS:*:*) + echo arm-unknown-riscos + exit ;; + SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*) + echo hppa1.1-hitachi-hiuxmpp + exit ;; + Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*) + # akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE. + if test "`(/bin/universe) 2>/dev/null`" = att ; then + echo pyramid-pyramid-sysv3 + else + echo pyramid-pyramid-bsd + fi + exit ;; + NILE*:*:*:dcosx) + echo pyramid-pyramid-svr4 + exit ;; + DRS?6000:unix:4.0:6*) + echo sparc-icl-nx6 + exit ;; + DRS?6000:UNIX_SV:4.2*:7* | DRS?6000:isis:4.2*:7*) + case `/usr/bin/uname -p` in + sparc) echo sparc-icl-nx7; exit ;; + esac ;; + s390x:SunOS:*:*) + echo ${UNAME_MACHINE}-ibm-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit ;; + sun4H:SunOS:5.*:*) + echo sparc-hal-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit ;; + sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*) + echo sparc-sun-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit ;; + i86pc:SunOS:5.*:* | i86xen:SunOS:5.*:*) + eval $set_cc_for_build + SUN_ARCH="i386" + # If there is a compiler, see if it is configured for 64-bit objects. + # Note that the Sun cc does not turn __LP64__ into 1 like gcc does. + # This test works for both compilers. + if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then + if (echo '#ifdef __amd64'; echo IS_64BIT_ARCH; echo '#endif') | \ + (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \ + grep IS_64BIT_ARCH >/dev/null + then + SUN_ARCH="x86_64" + fi + fi + echo ${SUN_ARCH}-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit ;; + sun4*:SunOS:6*:*) + # According to config.sub, this is the proper way to canonicalize + # SunOS6. Hard to guess exactly what SunOS6 will be like, but + # it's likely to be more like Solaris than SunOS4. + echo sparc-sun-solaris3`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit ;; + sun4*:SunOS:*:*) + case "`/usr/bin/arch -k`" in + Series*|S4*) + UNAME_RELEASE=`uname -v` + ;; + esac + # Japanese Language versions have a version number like `4.1.3-JL'. + echo sparc-sun-sunos`echo ${UNAME_RELEASE}|sed -e 's/-/_/'` + exit ;; + sun3*:SunOS:*:*) + echo m68k-sun-sunos${UNAME_RELEASE} + exit ;; + sun*:*:4.2BSD:*) + UNAME_RELEASE=`(sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null` + test "x${UNAME_RELEASE}" = "x" && UNAME_RELEASE=3 + case "`/bin/arch`" in + sun3) + echo m68k-sun-sunos${UNAME_RELEASE} + ;; + sun4) + echo sparc-sun-sunos${UNAME_RELEASE} + ;; + esac + exit ;; + aushp:SunOS:*:*) + echo sparc-auspex-sunos${UNAME_RELEASE} + exit ;; + # The situation for MiNT is a little confusing. The machine name + # can be virtually everything (everything which is not + # "atarist" or "atariste" at least should have a processor + # > m68000). The system name ranges from "MiNT" over "FreeMiNT" + # to the lowercase version "mint" (or "freemint"). Finally + # the system name "TOS" denotes a system which is actually not + # MiNT. But MiNT is downward compatible to TOS, so this should + # be no problem. + atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*) + echo m68k-atari-mint${UNAME_RELEASE} + exit ;; + atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*) + echo m68k-atari-mint${UNAME_RELEASE} + exit ;; + *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*) + echo m68k-atari-mint${UNAME_RELEASE} + exit ;; + milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*) + echo m68k-milan-mint${UNAME_RELEASE} + exit ;; + hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*) + echo m68k-hades-mint${UNAME_RELEASE} + exit ;; + *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*) + echo m68k-unknown-mint${UNAME_RELEASE} + exit ;; + m68k:machten:*:*) + echo m68k-apple-machten${UNAME_RELEASE} + exit ;; + powerpc:machten:*:*) + echo powerpc-apple-machten${UNAME_RELEASE} + exit ;; + RISC*:Mach:*:*) + echo mips-dec-mach_bsd4.3 + exit ;; + RISC*:ULTRIX:*:*) + echo mips-dec-ultrix${UNAME_RELEASE} + exit ;; + VAX*:ULTRIX*:*:*) + echo vax-dec-ultrix${UNAME_RELEASE} + exit ;; + 2020:CLIX:*:* | 2430:CLIX:*:*) + echo clipper-intergraph-clix${UNAME_RELEASE} + exit ;; + mips:*:*:UMIPS | mips:*:*:RISCos) + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c +#ifdef __cplusplus +#include <stdio.h> /* for printf() prototype */ + int main (int argc, char *argv[]) { +#else + int main (argc, argv) int argc; char *argv[]; { +#endif + #if defined (host_mips) && defined (MIPSEB) + #if defined (SYSTYPE_SYSV) + printf ("mips-mips-riscos%ssysv\n", argv[1]); exit (0); + #endif + #if defined (SYSTYPE_SVR4) + printf ("mips-mips-riscos%ssvr4\n", argv[1]); exit (0); + #endif + #if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD) + printf ("mips-mips-riscos%sbsd\n", argv[1]); exit (0); + #endif + #endif + exit (-1); + } +EOF + $CC_FOR_BUILD -o $dummy $dummy.c && + dummyarg=`echo "${UNAME_RELEASE}" | sed -n 's/\([0-9]*\).*/\1/p'` && + SYSTEM_NAME=`$dummy $dummyarg` && + { echo "$SYSTEM_NAME"; exit; } + echo mips-mips-riscos${UNAME_RELEASE} + exit ;; + Motorola:PowerMAX_OS:*:*) + echo powerpc-motorola-powermax + exit ;; + Motorola:*:4.3:PL8-*) + echo powerpc-harris-powermax + exit ;; + Night_Hawk:*:*:PowerMAX_OS | Synergy:PowerMAX_OS:*:*) + echo powerpc-harris-powermax + exit ;; + Night_Hawk:Power_UNIX:*:*) + echo powerpc-harris-powerunix + exit ;; + m88k:CX/UX:7*:*) + echo m88k-harris-cxux7 + exit ;; + m88k:*:4*:R4*) + echo m88k-motorola-sysv4 + exit ;; + m88k:*:3*:R3*) + echo m88k-motorola-sysv3 + exit ;; + AViiON:dgux:*:*) + # DG/UX returns AViiON for all architectures + UNAME_PROCESSOR=`/usr/bin/uname -p` + if [ $UNAME_PROCESSOR = mc88100 ] || [ $UNAME_PROCESSOR = mc88110 ] + then + if [ ${TARGET_BINARY_INTERFACE}x = m88kdguxelfx ] || \ + [ ${TARGET_BINARY_INTERFACE}x = x ] + then + echo m88k-dg-dgux${UNAME_RELEASE} + else + echo m88k-dg-dguxbcs${UNAME_RELEASE} + fi + else + echo i586-dg-dgux${UNAME_RELEASE} + fi + exit ;; + M88*:DolphinOS:*:*) # DolphinOS (SVR3) + echo m88k-dolphin-sysv3 + exit ;; + M88*:*:R3*:*) + # Delta 88k system running SVR3 + echo m88k-motorola-sysv3 + exit ;; + XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3) + echo m88k-tektronix-sysv3 + exit ;; + Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD) + echo m68k-tektronix-bsd + exit ;; + *:IRIX*:*:*) + echo mips-sgi-irix`echo ${UNAME_RELEASE}|sed -e 's/-/_/g'` + exit ;; + ????????:AIX?:[12].1:2) # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX. + echo romp-ibm-aix # uname -m gives an 8 hex-code CPU id + exit ;; # Note that: echo "'`uname -s`'" gives 'AIX ' + i*86:AIX:*:*) + echo i386-ibm-aix + exit ;; + ia64:AIX:*:*) + if [ -x /usr/bin/oslevel ] ; then + IBM_REV=`/usr/bin/oslevel` + else + IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE} + fi + echo ${UNAME_MACHINE}-ibm-aix${IBM_REV} + exit ;; + *:AIX:2:3) + if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + #include <sys/systemcfg.h> + + main() + { + if (!__power_pc()) + exit(1); + puts("powerpc-ibm-aix3.2.5"); + exit(0); + } +EOF + if $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy` + then + echo "$SYSTEM_NAME" + else + echo rs6000-ibm-aix3.2.5 + fi + elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then + echo rs6000-ibm-aix3.2.4 + else + echo rs6000-ibm-aix3.2 + fi + exit ;; + *:AIX:*:[456]) + IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'` + if /usr/sbin/lsattr -El ${IBM_CPU_ID} | grep ' POWER' >/dev/null 2>&1; then + IBM_ARCH=rs6000 + else + IBM_ARCH=powerpc + fi + if [ -x /usr/bin/oslevel ] ; then + IBM_REV=`/usr/bin/oslevel` + else + IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE} + fi + echo ${IBM_ARCH}-ibm-aix${IBM_REV} + exit ;; + *:AIX:*:*) + echo rs6000-ibm-aix + exit ;; + ibmrt:4.4BSD:*|romp-ibm:BSD:*) + echo romp-ibm-bsd4.4 + exit ;; + ibmrt:*BSD:*|romp-ibm:BSD:*) # covers RT/PC BSD and + echo romp-ibm-bsd${UNAME_RELEASE} # 4.3 with uname added to + exit ;; # report: romp-ibm BSD 4.3 + *:BOSX:*:*) + echo rs6000-bull-bosx + exit ;; + DPX/2?00:B.O.S.:*:*) + echo m68k-bull-sysv3 + exit ;; + 9000/[34]??:4.3bsd:1.*:*) + echo m68k-hp-bsd + exit ;; + hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*) + echo m68k-hp-bsd4.4 + exit ;; + 9000/[34678]??:HP-UX:*:*) + HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'` + case "${UNAME_MACHINE}" in + 9000/31? ) HP_ARCH=m68000 ;; + 9000/[34]?? ) HP_ARCH=m68k ;; + 9000/[678][0-9][0-9]) + if [ -x /usr/bin/getconf ]; then + sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null` + sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null` + case "${sc_cpu_version}" in + 523) HP_ARCH="hppa1.0" ;; # CPU_PA_RISC1_0 + 528) HP_ARCH="hppa1.1" ;; # CPU_PA_RISC1_1 + 532) # CPU_PA_RISC2_0 + case "${sc_kernel_bits}" in + 32) HP_ARCH="hppa2.0n" ;; + 64) HP_ARCH="hppa2.0w" ;; + '') HP_ARCH="hppa2.0" ;; # HP-UX 10.20 + esac ;; + esac + fi + if [ "${HP_ARCH}" = "" ]; then + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + + #define _HPUX_SOURCE + #include <stdlib.h> + #include <unistd.h> + + int main () + { + #if defined(_SC_KERNEL_BITS) + long bits = sysconf(_SC_KERNEL_BITS); + #endif + long cpu = sysconf (_SC_CPU_VERSION); + + switch (cpu) + { + case CPU_PA_RISC1_0: puts ("hppa1.0"); break; + case CPU_PA_RISC1_1: puts ("hppa1.1"); break; + case CPU_PA_RISC2_0: + #if defined(_SC_KERNEL_BITS) + switch (bits) + { + case 64: puts ("hppa2.0w"); break; + case 32: puts ("hppa2.0n"); break; + default: puts ("hppa2.0"); break; + } break; + #else /* !defined(_SC_KERNEL_BITS) */ + puts ("hppa2.0"); break; + #endif + default: puts ("hppa1.0"); break; + } + exit (0); + } +EOF + (CCOPTS= $CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null) && HP_ARCH=`$dummy` + test -z "$HP_ARCH" && HP_ARCH=hppa + fi ;; + esac + if [ ${HP_ARCH} = "hppa2.0w" ] + then + eval $set_cc_for_build + + # hppa2.0w-hp-hpux* has a 64-bit kernel and a compiler generating + # 32-bit code. hppa64-hp-hpux* has the same kernel and a compiler + # generating 64-bit code. GNU and HP use different nomenclature: + # + # $ CC_FOR_BUILD=cc ./config.guess + # => hppa2.0w-hp-hpux11.23 + # $ CC_FOR_BUILD="cc +DA2.0w" ./config.guess + # => hppa64-hp-hpux11.23 + + if echo __LP64__ | (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | + grep -q __LP64__ + then + HP_ARCH="hppa2.0w" + else + HP_ARCH="hppa64" + fi + fi + echo ${HP_ARCH}-hp-hpux${HPUX_REV} + exit ;; + ia64:HP-UX:*:*) + HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'` + echo ia64-hp-hpux${HPUX_REV} + exit ;; + 3050*:HI-UX:*:*) + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + #include <unistd.h> + int + main () + { + long cpu = sysconf (_SC_CPU_VERSION); + /* The order matters, because CPU_IS_HP_MC68K erroneously returns + true for CPU_PA_RISC1_0. CPU_IS_PA_RISC returns correct + results, however. */ + if (CPU_IS_PA_RISC (cpu)) + { + switch (cpu) + { + case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break; + case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break; + case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break; + default: puts ("hppa-hitachi-hiuxwe2"); break; + } + } + else if (CPU_IS_HP_MC68K (cpu)) + puts ("m68k-hitachi-hiuxwe2"); + else puts ("unknown-hitachi-hiuxwe2"); + exit (0); + } +EOF + $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy` && + { echo "$SYSTEM_NAME"; exit; } + echo unknown-hitachi-hiuxwe2 + exit ;; + 9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:* ) + echo hppa1.1-hp-bsd + exit ;; + 9000/8??:4.3bsd:*:*) + echo hppa1.0-hp-bsd + exit ;; + *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*) + echo hppa1.0-hp-mpeix + exit ;; + hp7??:OSF1:*:* | hp8?[79]:OSF1:*:* ) + echo hppa1.1-hp-osf + exit ;; + hp8??:OSF1:*:*) + echo hppa1.0-hp-osf + exit ;; + i*86:OSF1:*:*) + if [ -x /usr/sbin/sysversion ] ; then + echo ${UNAME_MACHINE}-unknown-osf1mk + else + echo ${UNAME_MACHINE}-unknown-osf1 + fi + exit ;; + parisc*:Lites*:*:*) + echo hppa1.1-hp-lites + exit ;; + C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*) + echo c1-convex-bsd + exit ;; + C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*) + if getsysinfo -f scalar_acc + then echo c32-convex-bsd + else echo c2-convex-bsd + fi + exit ;; + C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*) + echo c34-convex-bsd + exit ;; + C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*) + echo c38-convex-bsd + exit ;; + C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*) + echo c4-convex-bsd + exit ;; + CRAY*Y-MP:*:*:*) + echo ymp-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit ;; + CRAY*[A-Z]90:*:*:*) + echo ${UNAME_MACHINE}-cray-unicos${UNAME_RELEASE} \ + | sed -e 's/CRAY.*\([A-Z]90\)/\1/' \ + -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \ + -e 's/\.[^.]*$/.X/' + exit ;; + CRAY*TS:*:*:*) + echo t90-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit ;; + CRAY*T3E:*:*:*) + echo alphaev5-cray-unicosmk${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit ;; + CRAY*SV1:*:*:*) + echo sv1-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit ;; + *:UNICOS/mp:*:*) + echo craynv-cray-unicosmp${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit ;; + F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*) + FUJITSU_PROC=`uname -m | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` + FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'` + FUJITSU_REL=`echo ${UNAME_RELEASE} | sed -e 's/ /_/'` + echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" + exit ;; + 5000:UNIX_System_V:4.*:*) + FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'` + FUJITSU_REL=`echo ${UNAME_RELEASE} | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/ /_/'` + echo "sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" + exit ;; + i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*) + echo ${UNAME_MACHINE}-pc-bsdi${UNAME_RELEASE} + exit ;; + sparc*:BSD/OS:*:*) + echo sparc-unknown-bsdi${UNAME_RELEASE} + exit ;; + *:BSD/OS:*:*) + echo ${UNAME_MACHINE}-unknown-bsdi${UNAME_RELEASE} + exit ;; + *:FreeBSD:*:*) + case ${UNAME_MACHINE} in + pc98) + echo i386-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;; + amd64) + echo x86_64-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;; + *) + echo ${UNAME_MACHINE}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;; + esac + exit ;; + i*:CYGWIN*:*) + echo ${UNAME_MACHINE}-pc-cygwin + exit ;; + *:MINGW*:*) + echo ${UNAME_MACHINE}-pc-mingw32 + exit ;; + i*:windows32*:*) + # uname -m includes "-pc" on this system. + echo ${UNAME_MACHINE}-mingw32 + exit ;; + i*:PW*:*) + echo ${UNAME_MACHINE}-pc-pw32 + exit ;; + *:Interix*:[3456]*) + case ${UNAME_MACHINE} in + x86) + echo i586-pc-interix${UNAME_RELEASE} + exit ;; + EM64T | authenticamd | genuineintel) + echo x86_64-unknown-interix${UNAME_RELEASE} + exit ;; + IA64) + echo ia64-unknown-interix${UNAME_RELEASE} + exit ;; + esac ;; + [345]86:Windows_95:* | [345]86:Windows_98:* | [345]86:Windows_NT:*) + echo i${UNAME_MACHINE}-pc-mks + exit ;; + 8664:Windows_NT:*) + echo x86_64-pc-mks + exit ;; + i*:Windows_NT*:* | Pentium*:Windows_NT*:*) + # How do we know it's Interix rather than the generic POSIX subsystem? + # It also conflicts with pre-2.0 versions of AT&T UWIN. Should we + # UNAME_MACHINE based on the output of uname instead of i386? + echo i586-pc-interix + exit ;; + i*:UWIN*:*) + echo ${UNAME_MACHINE}-pc-uwin + exit ;; + amd64:CYGWIN*:*:* | x86_64:CYGWIN*:*:*) + echo x86_64-unknown-cygwin + exit ;; + p*:CYGWIN*:*) + echo powerpcle-unknown-cygwin + exit ;; + prep*:SunOS:5.*:*) + echo powerpcle-unknown-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit ;; + *:GNU:*:*) + # the GNU system + echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-unknown-gnu`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'` + exit ;; + *:GNU/*:*:*) + # other systems with GNU libc and userland + echo ${UNAME_MACHINE}-unknown-`echo ${UNAME_SYSTEM} | sed 's,^[^/]*/,,' | tr '[A-Z]' '[a-z]'``echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`-gnu + exit ;; + i*86:Minix:*:*) + echo ${UNAME_MACHINE}-pc-minix + exit ;; + arm*:Linux:*:*) + eval $set_cc_for_build + if echo __ARM_EABI__ | $CC_FOR_BUILD -E - 2>/dev/null \ + | grep -q __ARM_EABI__ + then + echo ${UNAME_MACHINE}-unknown-linux-gnu + else + echo ${UNAME_MACHINE}-unknown-linux-gnueabi + fi + exit ;; + avr32*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit ;; + cris:Linux:*:*) + echo cris-axis-linux-gnu + exit ;; + crisv32:Linux:*:*) + echo crisv32-axis-linux-gnu + exit ;; + frv:Linux:*:*) + echo frv-unknown-linux-gnu + exit ;; + ia64:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit ;; + m32r*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit ;; + m68*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit ;; + mips:Linux:*:* | mips64:Linux:*:*) + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + #undef CPU + #undef ${UNAME_MACHINE} + #undef ${UNAME_MACHINE}el + #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL) + CPU=${UNAME_MACHINE}el + #else + #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB) + CPU=${UNAME_MACHINE} + #else + CPU= + #endif + #endif +EOF + eval "`$CC_FOR_BUILD -E $dummy.c 2>/dev/null | sed -n ' + /^CPU/{ + s: ::g + p + }'`" + test x"${CPU}" != x && { echo "${CPU}-unknown-linux-gnu"; exit; } + ;; + or32:Linux:*:*) + echo or32-unknown-linux-gnu + exit ;; + ppc:Linux:*:*) + echo powerpc-unknown-linux-gnu + exit ;; + ppc64:Linux:*:*) + echo powerpc64-unknown-linux-gnu + exit ;; + alpha:Linux:*:*) + case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in + EV5) UNAME_MACHINE=alphaev5 ;; + EV56) UNAME_MACHINE=alphaev56 ;; + PCA56) UNAME_MACHINE=alphapca56 ;; + PCA57) UNAME_MACHINE=alphapca56 ;; + EV6) UNAME_MACHINE=alphaev6 ;; + EV67) UNAME_MACHINE=alphaev67 ;; + EV68*) UNAME_MACHINE=alphaev68 ;; + esac + objdump --private-headers /bin/sh | grep -q ld.so.1 + if test "$?" = 0 ; then LIBC="libc1" ; else LIBC="" ; fi + echo ${UNAME_MACHINE}-unknown-linux-gnu${LIBC} + exit ;; + padre:Linux:*:*) + echo sparc-unknown-linux-gnu + exit ;; + parisc:Linux:*:* | hppa:Linux:*:*) + # Look for CPU level + case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in + PA7*) echo hppa1.1-unknown-linux-gnu ;; + PA8*) echo hppa2.0-unknown-linux-gnu ;; + *) echo hppa-unknown-linux-gnu ;; + esac + exit ;; + parisc64:Linux:*:* | hppa64:Linux:*:*) + echo hppa64-unknown-linux-gnu + exit ;; + s390:Linux:*:* | s390x:Linux:*:*) + echo ${UNAME_MACHINE}-ibm-linux + exit ;; + sh64*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit ;; + sh*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit ;; + sparc:Linux:*:* | sparc64:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit ;; + vax:Linux:*:*) + echo ${UNAME_MACHINE}-dec-linux-gnu + exit ;; + x86_64:Linux:*:*) + echo x86_64-unknown-linux-gnu + exit ;; + xtensa*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit ;; + i*86:Linux:*:*) + # The BFD linker knows what the default object file format is, so + # first see if it will tell us. cd to the root directory to prevent + # problems with other programs or directories called `ld' in the path. + # Set LC_ALL=C to ensure ld outputs messages in English. + ld_supported_targets=`cd /; LC_ALL=C ld --help 2>&1 \ + | sed -ne '/supported targets:/!d + s/[ ][ ]*/ /g + s/.*supported targets: *// + s/ .*// + p'` + case "$ld_supported_targets" in + elf32-i386) + TENTATIVE="${UNAME_MACHINE}-pc-linux-gnu" + ;; + esac + # Determine whether the default compiler is a.out or elf + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + #include <features.h> + #ifdef __ELF__ + # ifdef __GLIBC__ + # if __GLIBC__ >= 2 + LIBC=gnu + # else + LIBC=gnulibc1 + # endif + # else + LIBC=gnulibc1 + # endif + #else + #if defined(__INTEL_COMPILER) || defined(__PGI) || defined(__SUNPRO_C) || defined(__SUNPRO_CC) + LIBC=gnu + #else + LIBC=gnuaout + #endif + #endif + #ifdef __dietlibc__ + LIBC=dietlibc + #endif +EOF + eval "`$CC_FOR_BUILD -E $dummy.c 2>/dev/null | sed -n ' + /^LIBC/{ + s: ::g + p + }'`" + test x"${LIBC}" != x && { + echo "${UNAME_MACHINE}-pc-linux-${LIBC}" + exit + } + test x"${TENTATIVE}" != x && { echo "${TENTATIVE}"; exit; } + ;; + i*86:DYNIX/ptx:4*:*) + # ptx 4.0 does uname -s correctly, with DYNIX/ptx in there. + # earlier versions are messed up and put the nodename in both + # sysname and nodename. + echo i386-sequent-sysv4 + exit ;; + i*86:UNIX_SV:4.2MP:2.*) + # Unixware is an offshoot of SVR4, but it has its own version + # number series starting with 2... + # I am not positive that other SVR4 systems won't match this, + # I just have to hope. -- rms. + # Use sysv4.2uw... so that sysv4* matches it. + echo ${UNAME_MACHINE}-pc-sysv4.2uw${UNAME_VERSION} + exit ;; + i*86:OS/2:*:*) + # If we were able to find `uname', then EMX Unix compatibility + # is probably installed. + echo ${UNAME_MACHINE}-pc-os2-emx + exit ;; + i*86:XTS-300:*:STOP) + echo ${UNAME_MACHINE}-unknown-stop + exit ;; + i*86:atheos:*:*) + echo ${UNAME_MACHINE}-unknown-atheos + exit ;; + i*86:syllable:*:*) + echo ${UNAME_MACHINE}-pc-syllable + exit ;; + i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.[02]*:*) + echo i386-unknown-lynxos${UNAME_RELEASE} + exit ;; + i*86:*DOS:*:*) + echo ${UNAME_MACHINE}-pc-msdosdjgpp + exit ;; + i*86:*:4.*:* | i*86:SYSTEM_V:4.*:*) + UNAME_REL=`echo ${UNAME_RELEASE} | sed 's/\/MP$//'` + if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then + echo ${UNAME_MACHINE}-univel-sysv${UNAME_REL} + else + echo ${UNAME_MACHINE}-pc-sysv${UNAME_REL} + fi + exit ;; + i*86:*:5:[678]*) + # UnixWare 7.x, OpenUNIX and OpenServer 6. + case `/bin/uname -X | grep "^Machine"` in + *486*) UNAME_MACHINE=i486 ;; + *Pentium) UNAME_MACHINE=i586 ;; + *Pent*|*Celeron) UNAME_MACHINE=i686 ;; + esac + echo ${UNAME_MACHINE}-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION} + exit ;; + i*86:*:3.2:*) + if test -f /usr/options/cb.name; then + UNAME_REL=`sed -n 's/.*Version //p' </usr/options/cb.name` + echo ${UNAME_MACHINE}-pc-isc$UNAME_REL + elif /bin/uname -X 2>/dev/null >/dev/null ; then + UNAME_REL=`(/bin/uname -X|grep Release|sed -e 's/.*= //')` + (/bin/uname -X|grep i80486 >/dev/null) && UNAME_MACHINE=i486 + (/bin/uname -X|grep '^Machine.*Pentium' >/dev/null) \ + && UNAME_MACHINE=i586 + (/bin/uname -X|grep '^Machine.*Pent *II' >/dev/null) \ + && UNAME_MACHINE=i686 + (/bin/uname -X|grep '^Machine.*Pentium Pro' >/dev/null) \ + && UNAME_MACHINE=i686 + echo ${UNAME_MACHINE}-pc-sco$UNAME_REL + else + echo ${UNAME_MACHINE}-pc-sysv32 + fi + exit ;; + pc:*:*:*) + # Left here for compatibility: + # uname -m prints for DJGPP always 'pc', but it prints nothing about + # the processor, so we play safe by assuming i586. + # Note: whatever this is, it MUST be the same as what config.sub + # prints for the "djgpp" host, or else GDB configury will decide that + # this is a cross-build. + echo i586-pc-msdosdjgpp + exit ;; + Intel:Mach:3*:*) + echo i386-pc-mach3 + exit ;; + paragon:*:*:*) + echo i860-intel-osf1 + exit ;; + i860:*:4.*:*) # i860-SVR4 + if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then + echo i860-stardent-sysv${UNAME_RELEASE} # Stardent Vistra i860-SVR4 + else # Add other i860-SVR4 vendors below as they are discovered. + echo i860-unknown-sysv${UNAME_RELEASE} # Unknown i860-SVR4 + fi + exit ;; + mini*:CTIX:SYS*5:*) + # "miniframe" + echo m68010-convergent-sysv + exit ;; + mc68k:UNIX:SYSTEM5:3.51m) + echo m68k-convergent-sysv + exit ;; + M680?0:D-NIX:5.3:*) + echo m68k-diab-dnix + exit ;; + M68*:*:R3V[5678]*:*) + test -r /sysV68 && { echo 'm68k-motorola-sysv'; exit; } ;; + 3[345]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 3[34]??/*:*:4.0:3.0 | 4400:*:4.0:3.0 | 4850:*:4.0:3.0 | SKA40:*:4.0:3.0 | SDS2:*:4.0:3.0 | SHG2:*:4.0:3.0 | S7501*:*:4.0:3.0) + OS_REL='' + test -r /etc/.relid \ + && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` + /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ + && { echo i486-ncr-sysv4.3${OS_REL}; exit; } + /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ + && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;; + 3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*) + /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ + && { echo i486-ncr-sysv4; exit; } ;; + NCR*:*:4.2:* | MPRAS*:*:4.2:*) + OS_REL='.3' + test -r /etc/.relid \ + && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` + /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ + && { echo i486-ncr-sysv4.3${OS_REL}; exit; } + /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ + && { echo i586-ncr-sysv4.3${OS_REL}; exit; } + /bin/uname -p 2>/dev/null | /bin/grep pteron >/dev/null \ + && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;; + m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*) + echo m68k-unknown-lynxos${UNAME_RELEASE} + exit ;; + mc68030:UNIX_System_V:4.*:*) + echo m68k-atari-sysv4 + exit ;; + TSUNAMI:LynxOS:2.*:*) + echo sparc-unknown-lynxos${UNAME_RELEASE} + exit ;; + rs6000:LynxOS:2.*:*) + echo rs6000-unknown-lynxos${UNAME_RELEASE} + exit ;; + PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.[02]*:*) + echo powerpc-unknown-lynxos${UNAME_RELEASE} + exit ;; + SM[BE]S:UNIX_SV:*:*) + echo mips-dde-sysv${UNAME_RELEASE} + exit ;; + RM*:ReliantUNIX-*:*:*) + echo mips-sni-sysv4 + exit ;; + RM*:SINIX-*:*:*) + echo mips-sni-sysv4 + exit ;; + *:SINIX-*:*:*) + if uname -p 2>/dev/null >/dev/null ; then + UNAME_MACHINE=`(uname -p) 2>/dev/null` + echo ${UNAME_MACHINE}-sni-sysv4 + else + echo ns32k-sni-sysv + fi + exit ;; + PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort + # says <Richard.M.Bartel@ccMail.Census.GOV> + echo i586-unisys-sysv4 + exit ;; + *:UNIX_System_V:4*:FTX*) + # From Gerald Hewes <hewes@openmarket.com>. + # How about differentiating between stratus architectures? -djm + echo hppa1.1-stratus-sysv4 + exit ;; + *:*:*:FTX*) + # From seanf@swdc.stratus.com. + echo i860-stratus-sysv4 + exit ;; + i*86:VOS:*:*) + # From Paul.Green@stratus.com. + echo ${UNAME_MACHINE}-stratus-vos + exit ;; + *:VOS:*:*) + # From Paul.Green@stratus.com. + echo hppa1.1-stratus-vos + exit ;; + mc68*:A/UX:*:*) + echo m68k-apple-aux${UNAME_RELEASE} + exit ;; + news*:NEWS-OS:6*:*) + echo mips-sony-newsos6 + exit ;; + R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*) + if [ -d /usr/nec ]; then + echo mips-nec-sysv${UNAME_RELEASE} + else + echo mips-unknown-sysv${UNAME_RELEASE} + fi + exit ;; + BeBox:BeOS:*:*) # BeOS running on hardware made by Be, PPC only. + echo powerpc-be-beos + exit ;; + BeMac:BeOS:*:*) # BeOS running on Mac or Mac clone, PPC only. + echo powerpc-apple-beos + exit ;; + BePC:BeOS:*:*) # BeOS running on Intel PC compatible. + echo i586-pc-beos + exit ;; + BePC:Haiku:*:*) # Haiku running on Intel PC compatible. + echo i586-pc-haiku + exit ;; + SX-4:SUPER-UX:*:*) + echo sx4-nec-superux${UNAME_RELEASE} + exit ;; + SX-5:SUPER-UX:*:*) + echo sx5-nec-superux${UNAME_RELEASE} + exit ;; + SX-6:SUPER-UX:*:*) + echo sx6-nec-superux${UNAME_RELEASE} + exit ;; + SX-7:SUPER-UX:*:*) + echo sx7-nec-superux${UNAME_RELEASE} + exit ;; + SX-8:SUPER-UX:*:*) + echo sx8-nec-superux${UNAME_RELEASE} + exit ;; + SX-8R:SUPER-UX:*:*) + echo sx8r-nec-superux${UNAME_RELEASE} + exit ;; + Power*:Rhapsody:*:*) + echo powerpc-apple-rhapsody${UNAME_RELEASE} + exit ;; + *:Rhapsody:*:*) + echo ${UNAME_MACHINE}-apple-rhapsody${UNAME_RELEASE} + exit ;; + *:Darwin:*:*) + UNAME_PROCESSOR=`uname -p` || UNAME_PROCESSOR=unknown + case $UNAME_PROCESSOR in + i386) eval $set_cc_for_build + if $CC_FOR_BUILD -E -dM -x c /dev/null | grep __LP64__>/dev/null 2>&1 ; then + UNAME_PROCESSOR=x86_64 + fi ;; + unknown) UNAME_PROCESSOR=powerpc ;; + esac + echo ${UNAME_PROCESSOR}-apple-darwin${UNAME_RELEASE} + exit ;; + *:procnto*:*:* | *:QNX:[0123456789]*:*) + UNAME_PROCESSOR=`uname -p` + if test "$UNAME_PROCESSOR" = "x86"; then + UNAME_PROCESSOR=i386 + UNAME_MACHINE=pc + fi + echo ${UNAME_PROCESSOR}-${UNAME_MACHINE}-nto-qnx${UNAME_RELEASE} + exit ;; + *:QNX:*:4*) + echo i386-pc-qnx + exit ;; + NSE-?:NONSTOP_KERNEL:*:*) + echo nse-tandem-nsk${UNAME_RELEASE} + exit ;; + NSR-?:NONSTOP_KERNEL:*:*) + echo nsr-tandem-nsk${UNAME_RELEASE} + exit ;; + *:NonStop-UX:*:*) + echo mips-compaq-nonstopux + exit ;; + BS2000:POSIX*:*:*) + echo bs2000-siemens-sysv + exit ;; + DS/*:UNIX_System_V:*:*) + echo ${UNAME_MACHINE}-${UNAME_SYSTEM}-${UNAME_RELEASE} + exit ;; + *:Plan9:*:*) + # "uname -m" is not consistent, so use $cputype instead. 386 + # is converted to i386 for consistency with other x86 + # operating systems. + if test "$cputype" = "386"; then + UNAME_MACHINE=i386 + else + UNAME_MACHINE="$cputype" + fi + echo ${UNAME_MACHINE}-unknown-plan9 + exit ;; + *:TOPS-10:*:*) + echo pdp10-unknown-tops10 + exit ;; + *:TENEX:*:*) + echo pdp10-unknown-tenex + exit ;; + KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*) + echo pdp10-dec-tops20 + exit ;; + XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*) + echo pdp10-xkl-tops20 + exit ;; + *:TOPS-20:*:*) + echo pdp10-unknown-tops20 + exit ;; + *:ITS:*:*) + echo pdp10-unknown-its + exit ;; + SEI:*:*:SEIUX) + echo mips-sei-seiux${UNAME_RELEASE} + exit ;; + *:DragonFly:*:*) + echo ${UNAME_MACHINE}-unknown-dragonfly`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` + exit ;; + *:*VMS:*:*) + UNAME_MACHINE=`(uname -p) 2>/dev/null` + case "${UNAME_MACHINE}" in + A*) echo alpha-dec-vms ; exit ;; + I*) echo ia64-dec-vms ; exit ;; + V*) echo vax-dec-vms ; exit ;; + esac ;; + *:XENIX:*:SysV) + echo i386-pc-xenix + exit ;; + i*86:skyos:*:*) + echo ${UNAME_MACHINE}-pc-skyos`echo ${UNAME_RELEASE}` | sed -e 's/ .*$//' + exit ;; + i*86:rdos:*:*) + echo ${UNAME_MACHINE}-pc-rdos + exit ;; + i*86:AROS:*:*) + echo ${UNAME_MACHINE}-pc-aros + exit ;; +esac + +#echo '(No uname command or uname output not recognized.)' 1>&2 +#echo "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" 1>&2 + +eval $set_cc_for_build +cat >$dummy.c <<EOF +#ifdef _SEQUENT_ +# include <sys/types.h> +# include <sys/utsname.h> +#endif +main () +{ +#if defined (sony) +#if defined (MIPSEB) + /* BFD wants "bsd" instead of "newsos". Perhaps BFD should be changed, + I don't know.... */ + printf ("mips-sony-bsd\n"); exit (0); +#else +#include <sys/param.h> + printf ("m68k-sony-newsos%s\n", +#ifdef NEWSOS4 + "4" +#else + "" +#endif + ); exit (0); +#endif +#endif + +#if defined (__arm) && defined (__acorn) && defined (__unix) + printf ("arm-acorn-riscix\n"); exit (0); +#endif + +#if defined (hp300) && !defined (hpux) + printf ("m68k-hp-bsd\n"); exit (0); +#endif + +#if defined (NeXT) +#if !defined (__ARCHITECTURE__) +#define __ARCHITECTURE__ "m68k" +#endif + int version; + version=`(hostinfo | sed -n 's/.*NeXT Mach \([0-9]*\).*/\1/p') 2>/dev/null`; + if (version < 4) + printf ("%s-next-nextstep%d\n", __ARCHITECTURE__, version); + else + printf ("%s-next-openstep%d\n", __ARCHITECTURE__, version); + exit (0); +#endif + +#if defined (MULTIMAX) || defined (n16) +#if defined (UMAXV) + printf ("ns32k-encore-sysv\n"); exit (0); +#else +#if defined (CMU) + printf ("ns32k-encore-mach\n"); exit (0); +#else + printf ("ns32k-encore-bsd\n"); exit (0); +#endif +#endif +#endif + +#if defined (__386BSD__) + printf ("i386-pc-bsd\n"); exit (0); +#endif + +#if defined (sequent) +#if defined (i386) + printf ("i386-sequent-dynix\n"); exit (0); +#endif +#if defined (ns32000) + printf ("ns32k-sequent-dynix\n"); exit (0); +#endif +#endif + +#if defined (_SEQUENT_) + struct utsname un; + + uname(&un); + + if (strncmp(un.version, "V2", 2) == 0) { + printf ("i386-sequent-ptx2\n"); exit (0); + } + if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */ + printf ("i386-sequent-ptx1\n"); exit (0); + } + printf ("i386-sequent-ptx\n"); exit (0); + +#endif + +#if defined (vax) +# if !defined (ultrix) +# include <sys/param.h> +# if defined (BSD) +# if BSD == 43 + printf ("vax-dec-bsd4.3\n"); exit (0); +# else +# if BSD == 199006 + printf ("vax-dec-bsd4.3reno\n"); exit (0); +# else + printf ("vax-dec-bsd\n"); exit (0); +# endif +# endif +# else + printf ("vax-dec-bsd\n"); exit (0); +# endif +# else + printf ("vax-dec-ultrix\n"); exit (0); +# endif +#endif + +#if defined (alliant) && defined (i860) + printf ("i860-alliant-bsd\n"); exit (0); +#endif + + exit (1); +} +EOF + +$CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null && SYSTEM_NAME=`$dummy` && + { echo "$SYSTEM_NAME"; exit; } + +# Apollos put the system type in the environment. + +test -d /usr/apollo && { echo ${ISP}-apollo-${SYSTYPE}; exit; } + +# Convex versions that predate uname can use getsysinfo(1) + +if [ -x /usr/convex/getsysinfo ] +then + case `getsysinfo -f cpu_type` in + c1*) + echo c1-convex-bsd + exit ;; + c2*) + if getsysinfo -f scalar_acc + then echo c32-convex-bsd + else echo c2-convex-bsd + fi + exit ;; + c34*) + echo c34-convex-bsd + exit ;; + c38*) + echo c38-convex-bsd + exit ;; + c4*) + echo c4-convex-bsd + exit ;; + esac +fi + +cat >&2 <<EOF +$0: unable to guess system type + +This script, last modified $timestamp, has failed to recognize +the operating system you are using. It is advised that you +download the most up to date version of the config scripts from + + http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess;hb=HEAD +and + http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub;hb=HEAD + +If the version you run ($0) is already up to date, please +send the following data and any information you think might be +pertinent to <config-patches@gnu.org> in order to provide the needed +information to handle your system. + +config.guess timestamp = $timestamp + +uname -m = `(uname -m) 2>/dev/null || echo unknown` +uname -r = `(uname -r) 2>/dev/null || echo unknown` +uname -s = `(uname -s) 2>/dev/null || echo unknown` +uname -v = `(uname -v) 2>/dev/null || echo unknown` + +/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null` +/bin/uname -X = `(/bin/uname -X) 2>/dev/null` + +hostinfo = `(hostinfo) 2>/dev/null` +/bin/universe = `(/bin/universe) 2>/dev/null` +/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null` +/bin/arch = `(/bin/arch) 2>/dev/null` +/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null` +/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null` + +UNAME_MACHINE = ${UNAME_MACHINE} +UNAME_RELEASE = ${UNAME_RELEASE} +UNAME_SYSTEM = ${UNAME_SYSTEM} +UNAME_VERSION = ${UNAME_VERSION} +EOF + +exit 1 + +# Local variables: +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "timestamp='" +# time-stamp-format: "%:y-%02m-%02d" +# time-stamp-end: "'" +# End: diff --git a/build/autoconf/config.status.m4 b/build/autoconf/config.status.m4 new file mode 100644 index 000000000..ceba24b67 --- /dev/null +++ b/build/autoconf/config.status.m4 @@ -0,0 +1,181 @@ +dnl This Source Code Form is subject to the terms of the Mozilla Public +dnl License, v. 2.0. If a copy of the MPL was not distributed with this +dnl file, You can obtain one at http://mozilla.org/MPL/2.0/. + +dnl For use in AC_SUBST replacement +define([MOZ_DIVERSION_SUBST], 11) + +dnl Replace AC_SUBST to store values in a format suitable for python. +dnl The necessary comma after the tuple can't be put here because it +dnl can mess around with things like: +dnl AC_SOMETHING(foo,AC_SUBST(),bar) +define([AC_SUBST], +[ifdef([AC_SUBST_$1], , +[define([AC_SUBST_$1], )dnl +AC_DIVERT_PUSH(MOZ_DIVERSION_SUBST)dnl + (''' $1 ''', r''' [$]$1 ''') +AC_DIVERT_POP()dnl +])]) + +dnl Wrap AC_DEFINE to store values in a format suitable for python. +dnl autoconf's AC_DEFINE still needs to be used to fill confdefs.h, +dnl which is #included during some compile checks. +dnl The necessary comma after the tuple can't be put here because it +dnl can mess around with things like: +dnl AC_SOMETHING(foo,AC_DEFINE(),bar) +define([_MOZ_AC_DEFINE], defn([AC_DEFINE])) +define([AC_DEFINE], +[cat >> confdefs.pytmp <<\EOF + (''' $1 ''', ifelse($#, 2, [r''' $2 '''], $#, 3, [r''' $2 '''], ' 1 ')) +EOF +ifelse($#, 2, _MOZ_AC_DEFINE([$1], [$2]), $#, 3, _MOZ_AC_DEFINE([$1], [$2], [$3]),_MOZ_AC_DEFINE([$1]))dnl +]) + +dnl Wrap AC_DEFINE_UNQUOTED to store values in a format suitable for +dnl python. +define([_MOZ_AC_DEFINE_UNQUOTED], defn([AC_DEFINE_UNQUOTED])) +define([AC_DEFINE_UNQUOTED], +[cat >> confdefs.pytmp <<EOF + (''' $1 ''', ifelse($#, 2, [r''' $2 '''], $#, 3, [r''' $2 '''], ' 1 ')) +EOF +ifelse($#, 2, _MOZ_AC_DEFINE_UNQUOTED($1, $2), $#, 3, _MOZ_AC_DEFINE_UNQUOTED($1, $2, $3),_MOZ_AC_DEFINE_UNQUOTED($1))dnl +]) + +dnl Replace AC_OUTPUT to create and call a python config.status +define([AC_OUTPUT], +[dnl Top source directory in Windows format (as opposed to msys format). +WIN_TOP_SRC= +encoding=utf-8 +case "$host_os" in +mingw*) + WIN_TOP_SRC=`cd $srcdir; pwd -W` + encoding=mbcs + ;; +esac +AC_SUBST(WIN_TOP_SRC) + +dnl Used in all Makefile.in files +top_srcdir=$srcdir +AC_SUBST(top_srcdir) + +dnl Picked from autoconf 2.13 +trap '' 1 2 15 +AC_CACHE_SAVE + +test "x$prefix" = xNONE && prefix=$ac_default_prefix +# Let make expand exec_prefix. +test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' + +trap 'rm -f $CONFIG_STATUS conftest*; exit 1' 1 2 15 + +: ${CONFIG_STATUS=./config.status} + +dnl We're going to need [ ] for python syntax. +changequote(<<<, >>>)dnl +echo creating $CONFIG_STATUS + +extra_python_path=${COMM_BUILD:+"'mozilla', "} + +cat > $CONFIG_STATUS <<EOF +#!${PYTHON} +# coding=$encoding + +import os +dnl topsrcdir is the top source directory in native form, as opposed to a +dnl form suitable for make. +topsrcdir = '''${WIN_TOP_SRC:-$srcdir}''' +if not os.path.isabs(topsrcdir): + rel = os.path.join(os.path.dirname(<<<__file__>>>), topsrcdir) + topsrcdir = os.path.normpath(os.path.abspath(rel)) + +topobjdir = os.path.abspath(os.path.dirname(<<<__file__>>>)) + +dnl All defines and substs are stored with an additional space at the beginning +dnl and at the end of the string, to avoid any problem with values starting or +dnl ending with quotes. +defines = [(name[1:-1], value[1:-1]) for name, value in [ +EOF + +dnl confdefs.pytmp contains AC_DEFINEs, in the expected format, but +dnl lacks the final comma (see above). +sed 's/$/,/' confdefs.pytmp >> $CONFIG_STATUS +rm confdefs.pytmp confdefs.h + +cat >> $CONFIG_STATUS <<\EOF +] ] + +substs = [(name[1:-1], value[1:-1]) for name, value in [ +EOF + +dnl The MOZ_DIVERSION_SUBST output diversion contains AC_SUBSTs, in the +dnl expected format, but lacks the final comma (see above). +sed 's/$/,/' >> $CONFIG_STATUS <<EOF +undivert(MOZ_DIVERSION_SUBST)dnl +EOF + +dnl Add in the output from the subconfigure script +for ac_subst_arg in $_subconfigure_ac_subst_args; do + variable='$'$ac_subst_arg + echo " (''' $ac_subst_arg ''', r''' `eval echo $variable` ''')," >> $CONFIG_STATUS +done + +cat >> $CONFIG_STATUS <<\EOF +] ] + +dnl List of files to apply AC_SUBSTs to. This is the list of files given +dnl as an argument to AC_OUTPUT ($1) +files = [ +EOF + +for out in $1; do + echo " '$out'," >> $CONFIG_STATUS +done + +cat >> $CONFIG_STATUS <<\EOF +] + +dnl List of header files to apply AC_DEFINEs to. This is stored in the +dnl AC_LIST_HEADER m4 macro by AC_CONFIG_HEADER. +headers = [ +EOF + +ifdef(<<<AC_LIST_HEADER>>>, <<< +HEADERS="AC_LIST_HEADER" +for header in $HEADERS; do + echo " '$header'," >> $CONFIG_STATUS +done +>>>)dnl + +cat >> $CONFIG_STATUS <<\EOF +] + +dnl List of AC_DEFINEs that aren't to be exposed in ALLDEFINES +non_global_defines = [ +EOF + +if test -n "$_NON_GLOBAL_ACDEFINES"; then + for var in $_NON_GLOBAL_ACDEFINES; do + echo " '$var'," >> $CONFIG_STATUS + done +fi + +cat >> $CONFIG_STATUS <<EOF +] + +__all__ = ['topobjdir', 'topsrcdir', 'defines', 'non_global_defines', 'substs', 'files', 'headers'] + +dnl Do the actual work +if __name__ == '__main__': + args = dict([(name, globals()[name]) for name in __all__]) + import sys +dnl Don't rely on virtualenv here. Standalone js doesn't use it. + sys.path.append(os.path.join(topsrcdir, ${extra_python_path}'build')) + from ConfigStatus import config_status + config_status(**args) +EOF +changequote([, ]) +chmod +x $CONFIG_STATUS +rm -fr confdefs* $ac_clean_files +dnl Execute config.status, unless --no-create was passed to configure. +test "$no_create" = yes || ${PYTHON} $CONFIG_STATUS || exit 1 +]) diff --git a/build/autoconf/config.sub b/build/autoconf/config.sub new file mode 100644 index 000000000..1c035c931 --- /dev/null +++ b/build/autoconf/config.sub @@ -0,0 +1,1708 @@ +#! /bin/sh +# Configuration validation subroutine script. +# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, +# 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 +# Free Software Foundation, Inc. + +timestamp='2011-01-03' + +# This file is (in principle) common to ALL GNU software. +# The presence of a machine in this file suggests that SOME GNU software +# can handle that machine. It does not imply ALL GNU software can. +# +# This file is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA +# 02110-1301, USA. +# +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + + +# Please send patches to <config-patches@gnu.org>. Submit a context +# diff and a properly formatted GNU ChangeLog entry. +# +# Configuration subroutine to validate and canonicalize a configuration type. +# Supply the specified configuration type as an argument. +# If it is invalid, we print an error message on stderr and exit with code 1. +# Otherwise, we print the canonical config type on stdout and succeed. + +# You can get the latest version of this script from: +# http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub;hb=HEAD + +# This file is supposed to be the same for all GNU packages +# and recognize all the CPU types, system types and aliases +# that are meaningful with *any* GNU software. +# Each package is responsible for reporting which valid configurations +# it does not support. The user should be able to distinguish +# a failure to support a valid configuration from a meaningless +# configuration. + +# The goal of this file is to map all the various variations of a given +# machine specification into a single specification in the form: +# CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM +# or in some cases, the newer four-part form: +# CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM +# It is wrong to echo any other type of specification. + +me=`echo "$0" | sed -e 's,.*/,,'` + +usage="\ +Usage: $0 [OPTION] CPU-MFR-OPSYS + $0 [OPTION] ALIAS + +Canonicalize a configuration name. + +Operation modes: + -h, --help print this help, then exit + -t, --time-stamp print date of last modification, then exit + -v, --version print version number, then exit + +Report bugs and patches to <config-patches@gnu.org>." + +version="\ +GNU config.sub ($timestamp) + +Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, +2002, 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc. + +This is free software; see the source for copying conditions. There is NO +warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." + +help=" +Try \`$me --help' for more information." + +# Parse command line +while test $# -gt 0 ; do + case $1 in + --time-stamp | --time* | -t ) + echo "$timestamp" ; exit ;; + --version | -v ) + echo "$version" ; exit ;; + --help | --h* | -h ) + echo "$usage"; exit ;; + -- ) # Stop option processing + shift; break ;; + - ) # Use stdin as input. + break ;; + -* ) + echo "$me: invalid option $1$help" + exit 1 ;; + + *local*) + # First pass through any local machine types. + echo $1 + exit ;; + + * ) + break ;; + esac +done + +case $# in + 0) echo "$me: missing argument$help" >&2 + exit 1;; + 1) ;; + *) echo "$me: too many arguments$help" >&2 + exit 1;; +esac + +# Separate what the user gave into CPU-COMPANY and OS or KERNEL-OS (if any). +# Here we must recognize all the valid KERNEL-OS combinations. +maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'` +case $maybe_os in + nto-qnx* | linux-gnu* | linux-dietlibc | linux-newlib* | linux-uclibc* | \ + uclinux-uclibc* | uclinux-gnu* | kfreebsd*-gnu* | knetbsd*-gnu* | netbsd*-gnu* | \ + kopensolaris*-gnu* | \ + storm-chaos* | os2-emx* | rtmk-nova* | wince-winmo*) + os=-$maybe_os + basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'` + ;; + *) + basic_machine=`echo $1 | sed 's/-[^-]*$//'` + if [ $basic_machine != $1 ] + then os=`echo $1 | sed 's/.*-/-/'` + else os=; fi + ;; +esac + +### Let's recognize common machines as not being operating systems so +### that things like config.sub decstation-3100 work. We also +### recognize some manufacturers as not being operating systems, so we +### can provide default operating systems below. +case $os in + -sun*os*) + # Prevent following clause from handling this invalid input. + ;; + -dec* | -mips* | -sequent* | -encore* | -pc532* | -sgi* | -sony* | \ + -att* | -7300* | -3300* | -delta* | -motorola* | -sun[234]* | \ + -unicom* | -ibm* | -next | -hp | -isi* | -apollo | -altos* | \ + -convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\ + -c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \ + -harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp | \ + -apple | -axis | -knuth | -cray | -microblaze) + os= + basic_machine=$1 + ;; + -bluegene*) + os=-cnk + ;; + -sim | -cisco | -oki | -wec | -winbond) + os= + basic_machine=$1 + ;; + -scout) + ;; + -wrs) + os=-vxworks + basic_machine=$1 + ;; + -chorusos*) + os=-chorusos + basic_machine=$1 + ;; + -chorusrdb) + os=-chorusrdb + basic_machine=$1 + ;; + -hiux*) + os=-hiuxwe2 + ;; + -sco6) + os=-sco5v6 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco5) + os=-sco3.2v5 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco4) + os=-sco3.2v4 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco3.2.[4-9]*) + os=`echo $os | sed -e 's/sco3.2./sco3.2v/'` + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco3.2v[4-9]*) + # Don't forget version if it is 3.2v4 or newer. + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco5v6*) + # Don't forget version if it is 3.2v4 or newer. + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco*) + os=-sco3.2v2 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -udk*) + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -isc) + os=-isc2.2 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -clix*) + basic_machine=clipper-intergraph + ;; + -isc*) + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -lynx*) + os=-lynxos + ;; + -ptx*) + basic_machine=`echo $1 | sed -e 's/86-.*/86-sequent/'` + ;; + -windowsnt*) + os=`echo $os | sed -e 's/windowsnt/winnt/'` + ;; + -psos*) + os=-psos + ;; + -mint | -mint[0-9]*) + basic_machine=m68k-atari + os=-mint + ;; +esac + +# Decode aliases for certain CPU-COMPANY combinations. +case $basic_machine in + # Recognize the basic CPU types without company name. + # Some are omitted here because they have special meanings below. + 1750a | 580 \ + | a29k \ + | alpha | alphaev[4-8] | alphaev56 | alphaev6[78] | alphapca5[67] \ + | alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] | alpha64pca5[67] \ + | am33_2.0 \ + | arc | arm | arm[bl]e | arme[lb] | armv[2345] | armv[345][lb] | avr | avr32 \ + | bfin \ + | c4x | clipper \ + | d10v | d30v | dlx | dsp16xx \ + | fido | fr30 | frv \ + | h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \ + | i370 | i860 | i960 | ia64 \ + | ip2k | iq2000 \ + | lm32 \ + | m32c | m32r | m32rle | m68000 | m68k | m88k \ + | maxq | mb | microblaze | mcore | mep | metag \ + | mips | mipsbe | mipseb | mipsel | mipsle \ + | mips16 \ + | mips64 | mips64el \ + | mips64octeon | mips64octeonel \ + | mips64orion | mips64orionel \ + | mips64r5900 | mips64r5900el \ + | mips64vr | mips64vrel \ + | mips64vr4100 | mips64vr4100el \ + | mips64vr4300 | mips64vr4300el \ + | mips64vr5000 | mips64vr5000el \ + | mips64vr5900 | mips64vr5900el \ + | mipsisa32 | mipsisa32el \ + | mipsisa32r2 | mipsisa32r2el \ + | mipsisa64 | mipsisa64el \ + | mipsisa64r2 | mipsisa64r2el \ + | mipsisa64sb1 | mipsisa64sb1el \ + | mipsisa64sr71k | mipsisa64sr71kel \ + | mipstx39 | mipstx39el \ + | mn10200 | mn10300 \ + | moxie \ + | mt \ + | msp430 \ + | nios | nios2 \ + | ns16k | ns32k \ + | or32 \ + | pdp10 | pdp11 | pj | pjl \ + | powerpc | powerpc64 | powerpc64le | powerpcle | ppcbe \ + | pyramid \ + | score \ + | sh | sh[1234] | sh[24]a | sh[24]aeb | sh[23]e | sh[34]eb | sheb | shbe | shle | sh[1234]le | sh3ele \ + | sh64 | sh64le \ + | sparc | sparc64 | sparc64b | sparc64v | sparc86x | sparclet | sparclite \ + | sparcv8 | sparcv9 | sparcv9b | sparcv9v \ + | spu | strongarm \ + | tahoe | thumb | tic4x | tic80 | tron \ + | v850 | v850e \ + | we32k \ + | x86 | xc16x | xscale | xscalee[bl] | xstormy16 | xtensa \ + | z8k | z80) + basic_machine=$basic_machine-unknown + ;; + m6811 | m68hc11 | m6812 | m68hc12) + # Motorola 68HC11/12. + basic_machine=$basic_machine-unknown + os=-none + ;; + m88110 | m680[12346]0 | m683?2 | m68360 | m5200 | v70 | w65 | z8k) + ;; + ms1) + basic_machine=mt-unknown + ;; + + # We use `pc' rather than `unknown' + # because (1) that's what they normally are, and + # (2) the word "unknown" tends to confuse beginning users. + i*86 | x86_64) + basic_machine=$basic_machine-pc + ;; + # Object if more than one company name word. + *-*-*) + echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2 + exit 1 + ;; + # Recognize the basic CPU types with company name. + 580-* \ + | a29k-* \ + | alpha-* | alphaev[4-8]-* | alphaev56-* | alphaev6[78]-* \ + | alpha64-* | alpha64ev[4-8]-* | alpha64ev56-* | alpha64ev6[78]-* \ + | alphapca5[67]-* | alpha64pca5[67]-* | arc-* \ + | arm-* | armbe-* | armle-* | armeb-* | armv*-* \ + | avr-* | avr32-* \ + | bfin-* | bs2000-* \ + | c[123]* | c30-* | [cjt]90-* | c4x-* | c54x-* | c55x-* | c6x-* \ + | clipper-* | craynv-* | cydra-* \ + | d10v-* | d30v-* | dlx-* \ + | elxsi-* \ + | f30[01]-* | f700-* | fido-* | fr30-* | frv-* | fx80-* \ + | h8300-* | h8500-* \ + | hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \ + | i*86-* | i860-* | i960-* | ia64-* \ + | ip2k-* | iq2000-* \ + | lm32-* \ + | m32c-* | m32r-* | m32rle-* \ + | m68000-* | m680[012346]0-* | m68360-* | m683?2-* | m68k-* \ + | m88110-* | m88k-* | maxq-* | mcore-* | metag-* | microblaze-* \ + | mips-* | mipsbe-* | mipseb-* | mipsel-* | mipsle-* \ + | mips16-* \ + | mips64-* | mips64el-* \ + | mips64octeon-* | mips64octeonel-* \ + | mips64orion-* | mips64orionel-* \ + | mips64r5900-* | mips64r5900el-* \ + | mips64vr-* | mips64vrel-* \ + | mips64vr4100-* | mips64vr4100el-* \ + | mips64vr4300-* | mips64vr4300el-* \ + | mips64vr5000-* | mips64vr5000el-* \ + | mips64vr5900-* | mips64vr5900el-* \ + | mipsisa32-* | mipsisa32el-* \ + | mipsisa32r2-* | mipsisa32r2el-* \ + | mipsisa64-* | mipsisa64el-* \ + | mipsisa64r2-* | mipsisa64r2el-* \ + | mipsisa64sb1-* | mipsisa64sb1el-* \ + | mipsisa64sr71k-* | mipsisa64sr71kel-* \ + | mipstx39-* | mipstx39el-* \ + | mmix-* \ + | mt-* \ + | msp430-* \ + | nios-* | nios2-* \ + | none-* | np1-* | ns16k-* | ns32k-* \ + | orion-* \ + | pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \ + | powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* | ppcbe-* \ + | pyramid-* \ + | romp-* | rs6000-* \ + | sh-* | sh[1234]-* | sh[24]a-* | sh[24]aeb-* | sh[23]e-* | sh[34]eb-* | sheb-* | shbe-* \ + | shle-* | sh[1234]le-* | sh3ele-* | sh64-* | sh64le-* \ + | sparc-* | sparc64-* | sparc64b-* | sparc64v-* | sparc86x-* | sparclet-* \ + | sparclite-* \ + | sparcv8-* | sparcv9-* | sparcv9b-* | sparcv9v-* | strongarm-* | sv1-* | sx?-* \ + | tahoe-* | thumb-* \ + | tic30-* | tic4x-* | tic54x-* | tic55x-* | tic6x-* | tic80-* | tile-* \ + | tron-* \ + | v850-* | v850e-* | vax-* \ + | we32k-* \ + | x86-* | x86_64-* | xc16x-* | xps100-* | xscale-* | xscalee[bl]-* \ + | xstormy16-* | xtensa*-* \ + | ymp-* \ + | z8k-* | z80-*) + ;; + # Recognize the basic CPU types without company name, with glob match. + xtensa*) + basic_machine=$basic_machine-unknown + ;; + # Recognize the various machine names and aliases which stand + # for a CPU type and a company and sometimes even an OS. + 386bsd) + basic_machine=i386-unknown + os=-bsd + ;; + 3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc) + basic_machine=m68000-att + ;; + 3b*) + basic_machine=we32k-att + ;; + a29khif) + basic_machine=a29k-amd + os=-udi + ;; + abacus) + basic_machine=abacus-unknown + ;; + adobe68k) + basic_machine=m68010-adobe + os=-scout + ;; + alliant | fx80) + basic_machine=fx80-alliant + ;; + altos | altos3068) + basic_machine=m68k-altos + ;; + am29k) + basic_machine=a29k-none + os=-bsd + ;; + amd64) + basic_machine=x86_64-pc + ;; + amd64-*) + basic_machine=x86_64-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + amdahl) + basic_machine=580-amdahl + os=-sysv + ;; + amiga | amiga-*) + basic_machine=m68k-unknown + ;; + amigaos | amigados) + basic_machine=m68k-unknown + os=-amigaos + ;; + amigaunix | amix) + basic_machine=m68k-unknown + os=-sysv4 + ;; + apollo68) + basic_machine=m68k-apollo + os=-sysv + ;; + apollo68bsd) + basic_machine=m68k-apollo + os=-bsd + ;; + aros) + basic_machine=i386-pc + os=-aros + ;; + aux) + basic_machine=m68k-apple + os=-aux + ;; + balance) + basic_machine=ns32k-sequent + os=-dynix + ;; + blackfin) + basic_machine=bfin-unknown + os=-linux + ;; + blackfin-*) + basic_machine=bfin-`echo $basic_machine | sed 's/^[^-]*-//'` + os=-linux + ;; + bluegene*) + basic_machine=powerpc-ibm + os=-cnk + ;; + c90) + basic_machine=c90-cray + os=-unicos + ;; + cegcc) + basic_machine=arm-unknown + os=-cegcc + ;; + convex-c1) + basic_machine=c1-convex + os=-bsd + ;; + convex-c2) + basic_machine=c2-convex + os=-bsd + ;; + convex-c32) + basic_machine=c32-convex + os=-bsd + ;; + convex-c34) + basic_machine=c34-convex + os=-bsd + ;; + convex-c38) + basic_machine=c38-convex + os=-bsd + ;; + cray | j90) + basic_machine=j90-cray + os=-unicos + ;; + craynv) + basic_machine=craynv-cray + os=-unicosmp + ;; + cr16) + basic_machine=cr16-unknown + os=-elf + ;; + crds | unos) + basic_machine=m68k-crds + ;; + crisv32 | crisv32-* | etraxfs*) + basic_machine=crisv32-axis + ;; + cris | cris-* | etrax*) + basic_machine=cris-axis + ;; + crx) + basic_machine=crx-unknown + os=-elf + ;; + da30 | da30-*) + basic_machine=m68k-da30 + ;; + decstation | decstation-3100 | pmax | pmax-* | pmin | dec3100 | decstatn) + basic_machine=mips-dec + ;; + decsystem10* | dec10*) + basic_machine=pdp10-dec + os=-tops10 + ;; + decsystem20* | dec20*) + basic_machine=pdp10-dec + os=-tops20 + ;; + delta | 3300 | motorola-3300 | motorola-delta \ + | 3300-motorola | delta-motorola) + basic_machine=m68k-motorola + ;; + delta88) + basic_machine=m88k-motorola + os=-sysv3 + ;; + dicos) + basic_machine=i686-pc + os=-dicos + ;; + djgpp) + basic_machine=i586-pc + os=-msdosdjgpp + ;; + dpx20 | dpx20-*) + basic_machine=rs6000-bull + os=-bosx + ;; + dpx2* | dpx2*-bull) + basic_machine=m68k-bull + os=-sysv3 + ;; + ebmon29k) + basic_machine=a29k-amd + os=-ebmon + ;; + elxsi) + basic_machine=elxsi-elxsi + os=-bsd + ;; + encore | umax | mmax) + basic_machine=ns32k-encore + ;; + es1800 | OSE68k | ose68k | ose | OSE) + basic_machine=m68k-ericsson + os=-ose + ;; + fx2800) + basic_machine=i860-alliant + ;; + genix) + basic_machine=ns32k-ns + ;; + gmicro) + basic_machine=tron-gmicro + os=-sysv + ;; + go32) + basic_machine=i386-pc + os=-go32 + ;; + h3050r* | hiux*) + basic_machine=hppa1.1-hitachi + os=-hiuxwe2 + ;; + h8300hms) + basic_machine=h8300-hitachi + os=-hms + ;; + h8300xray) + basic_machine=h8300-hitachi + os=-xray + ;; + h8500hms) + basic_machine=h8500-hitachi + os=-hms + ;; + harris) + basic_machine=m88k-harris + os=-sysv3 + ;; + hp300-*) + basic_machine=m68k-hp + ;; + hp300bsd) + basic_machine=m68k-hp + os=-bsd + ;; + hp300hpux) + basic_machine=m68k-hp + os=-hpux + ;; + hp3k9[0-9][0-9] | hp9[0-9][0-9]) + basic_machine=hppa1.0-hp + ;; + hp9k2[0-9][0-9] | hp9k31[0-9]) + basic_machine=m68000-hp + ;; + hp9k3[2-9][0-9]) + basic_machine=m68k-hp + ;; + hp9k6[0-9][0-9] | hp6[0-9][0-9]) + basic_machine=hppa1.0-hp + ;; + hp9k7[0-79][0-9] | hp7[0-79][0-9]) + basic_machine=hppa1.1-hp + ;; + hp9k78[0-9] | hp78[0-9]) + # FIXME: really hppa2.0-hp + basic_machine=hppa1.1-hp + ;; + hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893) + # FIXME: really hppa2.0-hp + basic_machine=hppa1.1-hp + ;; + hp9k8[0-9][13679] | hp8[0-9][13679]) + basic_machine=hppa1.1-hp + ;; + hp9k8[0-9][0-9] | hp8[0-9][0-9]) + basic_machine=hppa1.0-hp + ;; + hppa-next) + os=-nextstep3 + ;; + hppaosf) + basic_machine=hppa1.1-hp + os=-osf + ;; + hppro) + basic_machine=hppa1.1-hp + os=-proelf + ;; + i370-ibm* | ibm*) + basic_machine=i370-ibm + ;; +# I'm not sure what "Sysv32" means. Should this be sysv3.2? + i*86v32) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-sysv32 + ;; + i*86v4*) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-sysv4 + ;; + i*86v) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-sysv + ;; + i*86sol2) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-solaris2 + ;; + i386mach) + basic_machine=i386-mach + os=-mach + ;; + i386-vsta | vsta) + basic_machine=i386-unknown + os=-vsta + ;; + iris | iris4d) + basic_machine=mips-sgi + case $os in + -irix*) + ;; + *) + os=-irix4 + ;; + esac + ;; + isi68 | isi) + basic_machine=m68k-isi + os=-sysv + ;; + m68knommu) + basic_machine=m68k-unknown + os=-linux + ;; + m68knommu-*) + basic_machine=m68k-`echo $basic_machine | sed 's/^[^-]*-//'` + os=-linux + ;; + m88k-omron*) + basic_machine=m88k-omron + ;; + magnum | m3230) + basic_machine=mips-mips + os=-sysv + ;; + merlin) + basic_machine=ns32k-utek + os=-sysv + ;; + microblaze) + basic_machine=microblaze-xilinx + ;; + mingw32) + basic_machine=i386-pc + os=-mingw32 + ;; + mingw32ce) + basic_machine=arm-unknown + os=-mingw32ce + ;; + miniframe) + basic_machine=m68000-convergent + ;; + *mint | -mint[0-9]* | *MiNT | *MiNT[0-9]*) + basic_machine=m68k-atari + os=-mint + ;; + mips3*-*) + basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'` + ;; + mips3*) + basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`-unknown + ;; + monitor) + basic_machine=m68k-rom68k + os=-coff + ;; + morphos) + basic_machine=powerpc-unknown + os=-morphos + ;; + msdos) + basic_machine=i386-pc + os=-msdos + ;; + ms1-*) + basic_machine=`echo $basic_machine | sed -e 's/ms1-/mt-/'` + ;; + mvs) + basic_machine=i370-ibm + os=-mvs + ;; + ncr3000) + basic_machine=i486-ncr + os=-sysv4 + ;; + netbsd386) + basic_machine=i386-unknown + os=-netbsd + ;; + netwinder) + basic_machine=armv4l-rebel + os=-linux + ;; + news | news700 | news800 | news900) + basic_machine=m68k-sony + os=-newsos + ;; + news1000) + basic_machine=m68030-sony + os=-newsos + ;; + news-3600 | risc-news) + basic_machine=mips-sony + os=-newsos + ;; + necv70) + basic_machine=v70-nec + os=-sysv + ;; + next | m*-next ) + basic_machine=m68k-next + case $os in + -nextstep* ) + ;; + -ns2*) + os=-nextstep2 + ;; + *) + os=-nextstep3 + ;; + esac + ;; + nh3000) + basic_machine=m68k-harris + os=-cxux + ;; + nh[45]000) + basic_machine=m88k-harris + os=-cxux + ;; + nindy960) + basic_machine=i960-intel + os=-nindy + ;; + mon960) + basic_machine=i960-intel + os=-mon960 + ;; + nonstopux) + basic_machine=mips-compaq + os=-nonstopux + ;; + np1) + basic_machine=np1-gould + ;; + nsr-tandem) + basic_machine=nsr-tandem + ;; + op50n-* | op60c-*) + basic_machine=hppa1.1-oki + os=-proelf + ;; + openrisc | openrisc-*) + basic_machine=or32-unknown + ;; + os400) + basic_machine=powerpc-ibm + os=-os400 + ;; + OSE68000 | ose68000) + basic_machine=m68000-ericsson + os=-ose + ;; + os68k) + basic_machine=m68k-none + os=-os68k + ;; + pa-hitachi) + basic_machine=hppa1.1-hitachi + os=-hiuxwe2 + ;; + paragon) + basic_machine=i860-intel + os=-osf + ;; + parisc) + basic_machine=hppa-unknown + os=-linux + ;; + parisc-*) + basic_machine=hppa-`echo $basic_machine | sed 's/^[^-]*-//'` + os=-linux + ;; + pbd) + basic_machine=sparc-tti + ;; + pbb) + basic_machine=m68k-tti + ;; + pc532 | pc532-*) + basic_machine=ns32k-pc532 + ;; + pc98) + basic_machine=i386-pc + ;; + pc98-*) + basic_machine=i386-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pentium | p5 | k5 | k6 | nexgen | viac3) + basic_machine=i586-pc + ;; + pentiumpro | p6 | 6x86 | athlon | athlon_*) + basic_machine=i686-pc + ;; + pentiumii | pentium2 | pentiumiii | pentium3) + basic_machine=i686-pc + ;; + pentium4) + basic_machine=i786-pc + ;; + pentium-* | p5-* | k5-* | k6-* | nexgen-* | viac3-*) + basic_machine=i586-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pentiumpro-* | p6-* | 6x86-* | athlon-*) + basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pentiumii-* | pentium2-* | pentiumiii-* | pentium3-*) + basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pentium4-*) + basic_machine=i786-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pn) + basic_machine=pn-gould + ;; + power) basic_machine=power-ibm + ;; + ppc) basic_machine=powerpc-unknown + ;; + ppc-*) basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + ppcle | powerpclittle | ppc-le | powerpc-little) + basic_machine=powerpcle-unknown + ;; + ppcle-* | powerpclittle-*) + basic_machine=powerpcle-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + ppc64) basic_machine=powerpc64-unknown + ;; + ppc64-*) basic_machine=powerpc64-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + ppc64le | powerpc64little | ppc64-le | powerpc64-little) + basic_machine=powerpc64le-unknown + ;; + ppc64le-* | powerpc64little-*) + basic_machine=powerpc64le-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + ps2) + basic_machine=i386-ibm + ;; + pw32) + basic_machine=i586-unknown + os=-pw32 + ;; + rdos) + basic_machine=i386-pc + os=-rdos + ;; + rom68k) + basic_machine=m68k-rom68k + os=-coff + ;; + rm[46]00) + basic_machine=mips-siemens + ;; + rtpc | rtpc-*) + basic_machine=romp-ibm + ;; + s390 | s390-*) + basic_machine=s390-ibm + ;; + s390x | s390x-*) + basic_machine=s390x-ibm + ;; + sa29200) + basic_machine=a29k-amd + os=-udi + ;; + sb1) + basic_machine=mipsisa64sb1-unknown + ;; + sb1el) + basic_machine=mipsisa64sb1el-unknown + ;; + sde) + basic_machine=mipsisa32-sde + os=-elf + ;; + sei) + basic_machine=mips-sei + os=-seiux + ;; + sequent) + basic_machine=i386-sequent + ;; + sh) + basic_machine=sh-hitachi + os=-hms + ;; + sh5el) + basic_machine=sh5le-unknown + ;; + sh64) + basic_machine=sh64-unknown + ;; + sparclite-wrs | simso-wrs) + basic_machine=sparclite-wrs + os=-vxworks + ;; + sps7) + basic_machine=m68k-bull + os=-sysv2 + ;; + spur) + basic_machine=spur-unknown + ;; + st2000) + basic_machine=m68k-tandem + ;; + stratus) + basic_machine=i860-stratus + os=-sysv4 + ;; + sun2) + basic_machine=m68000-sun + ;; + sun2os3) + basic_machine=m68000-sun + os=-sunos3 + ;; + sun2os4) + basic_machine=m68000-sun + os=-sunos4 + ;; + sun3os3) + basic_machine=m68k-sun + os=-sunos3 + ;; + sun3os4) + basic_machine=m68k-sun + os=-sunos4 + ;; + sun4os3) + basic_machine=sparc-sun + os=-sunos3 + ;; + sun4os4) + basic_machine=sparc-sun + os=-sunos4 + ;; + sun4sol2) + basic_machine=sparc-sun + os=-solaris2 + ;; + sun3 | sun3-*) + basic_machine=m68k-sun + ;; + sun4) + basic_machine=sparc-sun + ;; + sun386 | sun386i | roadrunner) + basic_machine=i386-sun + ;; + sv1) + basic_machine=sv1-cray + os=-unicos + ;; + symmetry) + basic_machine=i386-sequent + os=-dynix + ;; + t3e) + basic_machine=alphaev5-cray + os=-unicos + ;; + t90) + basic_machine=t90-cray + os=-unicos + ;; + tic54x | c54x*) + basic_machine=tic54x-unknown + os=-coff + ;; + tic55x | c55x*) + basic_machine=tic55x-unknown + os=-coff + ;; + tic6x | c6x*) + basic_machine=tic6x-unknown + os=-coff + ;; + tile*) + basic_machine=tile-unknown + os=-linux-gnu + ;; + tx39) + basic_machine=mipstx39-unknown + ;; + tx39el) + basic_machine=mipstx39el-unknown + ;; + toad1) + basic_machine=pdp10-xkl + os=-tops20 + ;; + tower | tower-32) + basic_machine=m68k-ncr + ;; + tpf) + basic_machine=s390x-ibm + os=-tpf + ;; + udi29k) + basic_machine=a29k-amd + os=-udi + ;; + ultra3) + basic_machine=a29k-nyu + os=-sym1 + ;; + v810 | necv810) + basic_machine=v810-nec + os=-none + ;; + vaxv) + basic_machine=vax-dec + os=-sysv + ;; + vms) + basic_machine=vax-dec + os=-vms + ;; + vpp*|vx|vx-*) + basic_machine=f301-fujitsu + ;; + vxworks960) + basic_machine=i960-wrs + os=-vxworks + ;; + vxworks68) + basic_machine=m68k-wrs + os=-vxworks + ;; + vxworks29k) + basic_machine=a29k-wrs + os=-vxworks + ;; + w65*) + basic_machine=w65-wdc + os=-none + ;; + w89k-*) + basic_machine=hppa1.1-winbond + os=-proelf + ;; + xbox) + basic_machine=i686-pc + os=-mingw32 + ;; + xps | xps100) + basic_machine=xps100-honeywell + ;; + ymp) + basic_machine=ymp-cray + os=-unicos + ;; + z8k-*-coff) + basic_machine=z8k-unknown + os=-sim + ;; + z80-*-coff) + basic_machine=z80-unknown + os=-sim + ;; + none) + basic_machine=none-none + os=-none + ;; + +# Here we handle the default manufacturer of certain CPU types. It is in +# some cases the only manufacturer, in others, it is the most popular. + w89k) + basic_machine=hppa1.1-winbond + ;; + op50n) + basic_machine=hppa1.1-oki + ;; + op60c) + basic_machine=hppa1.1-oki + ;; + romp) + basic_machine=romp-ibm + ;; + mmix) + basic_machine=mmix-knuth + ;; + rs6000) + basic_machine=rs6000-ibm + ;; + vax) + basic_machine=vax-dec + ;; + pdp10) + # there are many clones, so DEC is not a safe bet + basic_machine=pdp10-unknown + ;; + pdp11) + basic_machine=pdp11-dec + ;; + we32k) + basic_machine=we32k-att + ;; + sh[1234] | sh[24]a | sh[24]aeb | sh[34]eb | sh[1234]le | sh[23]ele) + basic_machine=sh-unknown + ;; + sparc | sparcv8 | sparcv9 | sparcv9b | sparcv9v) + basic_machine=sparc-sun + ;; + cydra) + basic_machine=cydra-cydrome + ;; + orion) + basic_machine=orion-highlevel + ;; + orion105) + basic_machine=clipper-highlevel + ;; + mac | mpw | mac-mpw) + basic_machine=m68k-apple + ;; + pmac | pmac-mpw) + basic_machine=powerpc-apple + ;; + *-unknown) + # Make sure to match an already-canonicalized machine name. + ;; + *) + echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2 + exit 1 + ;; +esac + +# Here we canonicalize certain aliases for manufacturers. +case $basic_machine in + *-digital*) + basic_machine=`echo $basic_machine | sed 's/digital.*/dec/'` + ;; + *-commodore*) + basic_machine=`echo $basic_machine | sed 's/commodore.*/cbm/'` + ;; + *) + ;; +esac + +# Decode manufacturer-specific aliases for certain operating systems. + +if [ x"$os" != x"" ] +then +case $os in + # First match some system type aliases + # that might get confused with valid system types. + # -solaris* is a basic system type, with this one exception. + -solaris1 | -solaris1.*) + os=`echo $os | sed -e 's|solaris1|sunos4|'` + ;; + -solaris) + os=-solaris2 + ;; + -svr4*) + os=-sysv4 + ;; + -unixware*) + os=-sysv4.2uw + ;; + -gnu/linux*) + os=`echo $os | sed -e 's|gnu/linux|linux-gnu|'` + ;; + # First accept the basic system types. + # The portable systems comes first. + # Each alternative MUST END IN A *, to match a version number. + # -sysv* is not here because it comes later, after sysvr4. + -gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \ + | -*vms* | -sco* | -esix* | -isc* | -aix* | -cnk* | -sunos | -sunos[34]*\ + | -hpux* | -unos* | -osf* | -luna* | -dgux* | -solaris* | -sym* \ + | -kopensolaris* \ + | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \ + | -aos* | -aros* \ + | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \ + | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \ + | -hiux* | -386bsd* | -knetbsd* | -mirbsd* | -netbsd* \ + | -openbsd* | -solidbsd* \ + | -ekkobsd* | -kfreebsd* | -freebsd* | -riscix* | -lynxos* \ + | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \ + | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \ + | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \ + | -chorusos* | -chorusrdb* | -cegcc* \ + | -cygwin* | -pe* | -psos* | -moss* | -proelf* | -rtems* \ + | -mingw32* | -linux-gnu* | -linux-newlib* | -linux-uclibc* \ + | -uxpv* | -beos* | -mpeix* | -udk* \ + | -interix* | -uwin* | -mks* | -rhapsody* | -darwin* | -opened* \ + | -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \ + | -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* \ + | -os2* | -vos* | -palmos* | -uclinux* | -nucleus* \ + | -morphos* | -superux* | -rtmk* | -rtmk-nova* | -windiss* \ + | -powermax* | -dnix* | -nx6 | -nx7 | -sei* | -dragonfly* \ + | -skyos* | -haiku* | -rdos* | -toppers* | -drops* | -winmo*) + # Remember, each alternative MUST END IN *, to match a version number. + ;; + -qnx*) + case $basic_machine in + x86-* | i*86-*) + ;; + *) + os=-nto$os + ;; + esac + ;; + -nto-qnx*) + ;; + -nto*) + os=`echo $os | sed -e 's|nto|nto-qnx|'` + ;; + -sim | -es1800* | -hms* | -xray | -os68k* | -none* | -v88r* \ + | -windows* | -osx | -abug | -netware* | -os9* | -beos* | -haiku* \ + | -macos* | -mpw* | -magic* | -mmixware* | -mon960* | -lnews*) + ;; + -mac*) + os=`echo $os | sed -e 's|mac|macos|'` + ;; + -linux-dietlibc) + os=-linux-dietlibc + ;; + -linux*) + os=`echo $os | sed -e 's|linux|linux-gnu|'` + ;; + -sunos5*) + os=`echo $os | sed -e 's|sunos5|solaris2|'` + ;; + -sunos6*) + os=`echo $os | sed -e 's|sunos6|solaris3|'` + ;; + -opened*) + os=-openedition + ;; + -os400*) + os=-os400 + ;; + -wince-winmo*) + os=-wince-winmo + ;; + -wince*) + os=-wince + ;; + -osfrose*) + os=-osfrose + ;; + -osf*) + os=-osf + ;; + -utek*) + os=-bsd + ;; + -dynix*) + os=-bsd + ;; + -acis*) + os=-aos + ;; + -atheos*) + os=-atheos + ;; + -syllable*) + os=-syllable + ;; + -386bsd) + os=-bsd + ;; + -ctix* | -uts*) + os=-sysv + ;; + -nova*) + os=-rtmk-nova + ;; + -ns2 ) + os=-nextstep2 + ;; + -nsk*) + os=-nsk + ;; + # Preserve the version number of sinix5. + -sinix5.*) + os=`echo $os | sed -e 's|sinix|sysv|'` + ;; + -sinix*) + os=-sysv4 + ;; + -tpf*) + os=-tpf + ;; + -triton*) + os=-sysv3 + ;; + -oss*) + os=-sysv3 + ;; + -svr4) + os=-sysv4 + ;; + -svr3) + os=-sysv3 + ;; + -sysvr4) + os=-sysv4 + ;; + # This must come after -sysvr4. + -sysv*) + ;; + -ose*) + os=-ose + ;; + -es1800*) + os=-ose + ;; + -xenix) + os=-xenix + ;; + -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*) + os=-mint + ;; + -aros*) + os=-aros + ;; + -kaos*) + os=-kaos + ;; + -zvmoe) + os=-zvmoe + ;; + -dicos*) + os=-dicos + ;; + -android*) + os=-android + ;; + -none) + ;; + *) + # Get rid of the `-' at the beginning of $os. + os=`echo $os | sed 's/[^-]*-//'` + echo Invalid configuration \`$1\': system \`$os\' not recognized 1>&2 + exit 1 + ;; +esac +else + +# Here we handle the default operating systems that come with various machines. +# The value should be what the vendor currently ships out the door with their +# machine or put another way, the most popular os provided with the machine. + +# Note that if you're going to try to match "-MANUFACTURER" here (say, +# "-sun"), then you have to tell the case statement up towards the top +# that MANUFACTURER isn't an operating system. Otherwise, code above +# will signal an error saying that MANUFACTURER isn't an operating +# system, and we'll never get to this point. + +case $basic_machine in + score-*) + os=-elf + ;; + spu-*) + os=-elf + ;; + *-acorn) + os=-riscix1.2 + ;; + arm*-rebel) + os=-linux + ;; + arm*-semi) + os=-aout + ;; + c4x-* | tic4x-*) + os=-coff + ;; + # This must come before the *-dec entry. + pdp10-*) + os=-tops20 + ;; + pdp11-*) + os=-none + ;; + *-dec | vax-*) + os=-ultrix4.2 + ;; + m68*-apollo) + os=-domain + ;; + i386-sun) + os=-sunos4.0.2 + ;; + m68000-sun) + os=-sunos3 + # This also exists in the configure program, but was not the + # default. + # os=-sunos4 + ;; + m68*-cisco) + os=-aout + ;; + mep-*) + os=-elf + ;; + mips*-cisco) + os=-elf + ;; + mips*-*) + os=-elf + ;; + or32-*) + os=-coff + ;; + *-tti) # must be before sparc entry or we get the wrong os. + os=-sysv3 + ;; + sparc-* | *-sun) + os=-sunos4.1.1 + ;; + *-be) + os=-beos + ;; + *-haiku) + os=-haiku + ;; + *-ibm) + os=-aix + ;; + *-knuth) + os=-mmixware + ;; + *-wec) + os=-proelf + ;; + *-winbond) + os=-proelf + ;; + *-oki) + os=-proelf + ;; + *-hp) + os=-hpux + ;; + *-hitachi) + os=-hiux + ;; + i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent) + os=-sysv + ;; + *-cbm) + os=-amigaos + ;; + *-dg) + os=-dgux + ;; + *-dolphin) + os=-sysv3 + ;; + m68k-ccur) + os=-rtu + ;; + m88k-omron*) + os=-luna + ;; + *-next ) + os=-nextstep + ;; + *-sequent) + os=-ptx + ;; + *-crds) + os=-unos + ;; + *-ns) + os=-genix + ;; + i370-*) + os=-mvs + ;; + *-next) + os=-nextstep3 + ;; + *-gould) + os=-sysv + ;; + *-highlevel) + os=-bsd + ;; + *-encore) + os=-bsd + ;; + *-sgi) + os=-irix + ;; + *-siemens) + os=-sysv4 + ;; + *-masscomp) + os=-rtu + ;; + f30[01]-fujitsu | f700-fujitsu) + os=-uxpv + ;; + *-rom68k) + os=-coff + ;; + *-*bug) + os=-coff + ;; + *-apple) + os=-macos + ;; + *-atari*) + os=-mint + ;; + *) + os=-none + ;; +esac +fi + +# Here we handle the case where we know the os, and the CPU type, but not the +# manufacturer. We pick the logical manufacturer. +vendor=unknown +case $basic_machine in + *-unknown) + case $os in + -riscix*) + vendor=acorn + ;; + -sunos*) + vendor=sun + ;; + -cnk*|-aix*) + vendor=ibm + ;; + -beos*) + vendor=be + ;; + -hpux*) + vendor=hp + ;; + -mpeix*) + vendor=hp + ;; + -hiux*) + vendor=hitachi + ;; + -unos*) + vendor=crds + ;; + -dgux*) + vendor=dg + ;; + -luna*) + vendor=omron + ;; + -genix*) + vendor=ns + ;; + -mvs* | -opened*) + vendor=ibm + ;; + -os400*) + vendor=ibm + ;; + -ptx*) + vendor=sequent + ;; + -tpf*) + vendor=ibm + ;; + -vxsim* | -vxworks* | -windiss*) + vendor=wrs + ;; + -aux*) + vendor=apple + ;; + -hms*) + vendor=hitachi + ;; + -mpw* | -macos*) + vendor=apple + ;; + -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*) + vendor=atari + ;; + -vos*) + vendor=stratus + ;; + *-android*|*-linuxandroid*) + vendor=linux- + ;; + esac + basic_machine=`echo $basic_machine | sed "s/unknown/$vendor/"` + ;; +esac + +echo $basic_machine$os +exit + +# Local variables: +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "timestamp='" +# time-stamp-format: "%:y-%02m-%02d" +# time-stamp-end: "'" +# End: diff --git a/build/autoconf/expandlibs.m4 b/build/autoconf/expandlibs.m4 new file mode 100644 index 000000000..898e5ce01 --- /dev/null +++ b/build/autoconf/expandlibs.m4 @@ -0,0 +1,66 @@ +dnl This Source Code Form is subject to the terms of the Mozilla Public +dnl License, v. 2.0. If a copy of the MPL was not distributed with this +dnl file, You can obtain one at http://mozilla.org/MPL/2.0/. + +AC_DEFUN([MOZ_EXPAND_LIBS], +[ +dnl ======================================================== +dnl = +dnl = Check what kind of list files are supported by the +dnl = linker +dnl = +dnl ======================================================== + +AC_CACHE_CHECK(what kind of list files are supported by the linker, + EXPAND_LIBS_LIST_STYLE, + [echo "int main() {return 0;}" > conftest.${ac_ext} + if AC_TRY_COMMAND(${CC-cc} -o conftest.${OBJ_SUFFIX} -c $CFLAGS $CPPFLAGS conftest.${ac_ext} 1>&5) && test -s conftest.${OBJ_SUFFIX}; then + echo "INPUT(conftest.${OBJ_SUFFIX})" > conftest.list + if AC_TRY_COMMAND(${CC-cc} -o conftest${ac_exeext} $LDFLAGS conftest.list $LIBS 1>&5) && test -s conftest${ac_exeext}; then + EXPAND_LIBS_LIST_STYLE=linkerscript + else + echo "conftest.${OBJ_SUFFIX}" > conftest.list + dnl -filelist is for the OS X linker. We need to try -filelist + dnl first because clang understands @file, but may pass an + dnl oversized argument list to the linker depending on the + dnl contents of @file. + if AC_TRY_COMMAND(${CC-cc} -o conftest${ac_exeext} $LDFLAGS [-Wl,-filelist,conftest.list] $LIBS 1>&5) && test -s conftest${ac_exeext}; then + EXPAND_LIBS_LIST_STYLE=filelist + elif AC_TRY_COMMAND(${CC-cc} -o conftest${ac_exeext} $LDFLAGS @conftest.list $LIBS 1>&5) && test -s conftest${ac_exeext}; then + EXPAND_LIBS_LIST_STYLE=list + else + EXPAND_LIBS_LIST_STYLE=none + fi + fi + else + dnl We really don't expect to get here, but just in case + AC_ERROR([couldn't compile a simple C file]) + fi + rm -rf conftest*]) + +LIBS_DESC_SUFFIX=desc +AC_SUBST(LIBS_DESC_SUFFIX) +AC_SUBST(EXPAND_LIBS_LIST_STYLE) + +if test "$GCC_USE_GNU_LD"; then + AC_CACHE_CHECK(what kind of ordering can be done with the linker, + EXPAND_LIBS_ORDER_STYLE, + [> conftest.order + _SAVE_LDFLAGS="$LDFLAGS" + LDFLAGS="${LDFLAGS} -Wl,--section-ordering-file,conftest.order" + AC_TRY_LINK([], [], + EXPAND_LIBS_ORDER_STYLE=section-ordering-file, + EXPAND_LIBS_ORDER_STYLE=) + LDFLAGS="$_SAVE_LDFLAGS" + if test -z "$EXPAND_LIBS_ORDER_STYLE"; then + if AC_TRY_COMMAND(${CC-cc} ${DSO_LDOPTS} ${LDFLAGS} -o ${DLL_PREFIX}conftest${DLL_SUFFIX} -Wl,--verbose 2> /dev/null | sed -n '/^===/,/^===/p' | grep '\.text'); then + EXPAND_LIBS_ORDER_STYLE=linkerscript + else + EXPAND_LIBS_ORDER_STYLE=none + fi + rm -f ${DLL_PREFIX}conftest${DLL_SUFFIX} + fi]) +fi +AC_SUBST(EXPAND_LIBS_ORDER_STYLE) + +]) diff --git a/build/autoconf/frameptr.m4 b/build/autoconf/frameptr.m4 new file mode 100644 index 000000000..7f18fc5e5 --- /dev/null +++ b/build/autoconf/frameptr.m4 @@ -0,0 +1,38 @@ +dnl This Source Code Form is subject to the terms of the Mozilla Public +dnl License, v. 2.0. If a copy of the MPL was not distributed with this +dnl file, You can obtain one at http://mozilla.org/MPL/2.0/. + +dnl Set MOZ_FRAMEPTR_FLAGS to the flags that should be used for enabling or +dnl disabling frame pointers in this architecture based on the configure +dnl options + +AC_DEFUN([MOZ_SET_FRAMEPTR_FLAGS], [ + case "$target" in + *android*) + unwind_tables="-funwind-tables" + ;; + esac + if test "$GNU_CC"; then + MOZ_ENABLE_FRAME_PTR="-fno-omit-frame-pointer $unwind_tables" + MOZ_DISABLE_FRAME_PTR="-fomit-frame-pointer" + if test "$CPU_ARCH" = arm; then + # http://gcc.gnu.org/bugzilla/show_bug.cgi?id=54398 + MOZ_ENABLE_FRAME_PTR="$unwind_tables" + fi + else + case "$target" in + *-mingw*) + MOZ_ENABLE_FRAME_PTR="-Oy-" + MOZ_DISABLE_FRAME_PTR="-Oy" + ;; + esac + fi + + # if we are debugging or profiling, we want a frame pointer. + if test -z "$MOZ_OPTIMIZE" -o \ + -n "$MOZ_PROFILING" -o -n "$MOZ_DEBUG"; then + MOZ_FRAMEPTR_FLAGS="$MOZ_ENABLE_FRAME_PTR" + else + MOZ_FRAMEPTR_FLAGS="$MOZ_DISABLE_FRAME_PTR" + fi +]) diff --git a/build/autoconf/gcc-pr39608.m4 b/build/autoconf/gcc-pr39608.m4 new file mode 100644 index 000000000..02de9faec --- /dev/null +++ b/build/autoconf/gcc-pr39608.m4 @@ -0,0 +1,37 @@ +dnl This Source Code Form is subject to the terms of the Mozilla Public +dnl License, v. 2.0. If a copy of the MPL was not distributed with this +dnl file, You can obtain one at http://mozilla.org/MPL/2.0/. + +dnl Check if the compiler is gcc and has pr39608. If so +dnl disable vrp. + +AC_DEFUN([MOZ_GCC_PR39608], +[ +AC_MSG_CHECKING(for gcc pr39608) +ac_have_gcc_pr39608="yes" +AC_LANG_SAVE +AC_LANG_CPLUSPLUS + +AC_TRY_COMPILE([ +typedef void (*FuncType)(); +template<FuncType Impl> +void f(); +template<typename T> class C { + typedef C<T> ThisC; + template<int g()> + static void h() { + f<ThisC::h<g> >(); + } +}; +], true, + ac_have_gcc_pr39608="no", + true) + +AC_LANG_RESTORE + +AC_MSG_RESULT($ac_have_gcc_pr39608) +if test "$ac_have_gcc_pr39608" = "yes"; then + echo This compiler would fail to build firefox, plase upgrade. + exit 1 +fi +]) diff --git a/build/autoconf/gcc-pr49911.m4 b/build/autoconf/gcc-pr49911.m4 new file mode 100644 index 000000000..9bb762aa2 --- /dev/null +++ b/build/autoconf/gcc-pr49911.m4 @@ -0,0 +1,71 @@ +dnl This Source Code Form is subject to the terms of the Mozilla Public +dnl License, v. 2.0. If a copy of the MPL was not distributed with this +dnl file, You can obtain one at http://mozilla.org/MPL/2.0/. + +dnl Check if the compiler is gcc and has PR49911. If so +dnl disable vrp. + +AC_DEFUN([MOZ_GCC_PR49911], +[ +if test "$GNU_CC"; then + +AC_MSG_CHECKING(for gcc PR49911) +ac_have_gcc_pr49911="no" +AC_LANG_SAVE +AC_LANG_CPLUSPLUS + +_SAVE_CXXFLAGS=$CXXFLAGS +CXXFLAGS="-O2" +AC_TRY_RUN([ +extern "C" void abort(void); +typedef enum { +eax, ecx, edx, ebx, esp, ebp, +esi, edi } +RegisterID; +union StateRemat { + RegisterID reg_; + int offset_; +}; +static StateRemat FromRegister(RegisterID reg) { + StateRemat sr; + sr.reg_ = reg; + return sr; +} +static StateRemat FromAddress3(int address) { + StateRemat sr; + sr.offset_ = address; + if (address < 46 && address >= 0) { + abort(); + } + return sr; +} +struct FrameState { + StateRemat dataRematInfo2(bool y, int z) { + if (y) return FromRegister(RegisterID(1)); + return FromAddress3(z); + } +}; +FrameState frame; +StateRemat x; +__attribute__((noinline)) void jsop_setelem(bool y, int z) { + x = frame.dataRematInfo2(y, z); +} +int main(void) { + jsop_setelem(0, 47); +} +], true, + ac_have_gcc_pr49911="yes", + true) +CXXFLAGS="$_SAVE_CXXFLAGS" + +AC_LANG_RESTORE + +if test "$ac_have_gcc_pr49911" = "yes"; then + AC_MSG_RESULT(yes) + CFLAGS="$CFLAGS -fno-tree-vrp" + CXXFLAGS="$CXXFLAGS -fno-tree-vrp" +else + AC_MSG_RESULT(no) +fi +fi +]) diff --git a/build/autoconf/hooks.m4 b/build/autoconf/hooks.m4 new file mode 100644 index 000000000..64661dc7e --- /dev/null +++ b/build/autoconf/hooks.m4 @@ -0,0 +1,26 @@ +dnl This Source Code Form is subject to the terms of the Mozilla Public +dnl License, v. 2.0. If a copy of the MPL was not distributed with this +dnl file, You can obtain one at http://mozilla.org/MPL/2.0/. + +dnl Output the contents of config.log when configure exits with an +dnl error code. +define([MOZ_CONFIG_LOG_TRAP], +[changequote(<<<, >>>)dnl +trap '[ "$?" != 0 ] && echo "------ config.log ------" && tail -n 25 config.log' EXIT +changequote([, ])dnl +]) + +dnl Wrap AC_INIT_PREPARE to add the above trap. +define([_MOZ_AC_INIT_PREPARE], defn([AC_INIT_PREPARE])) +define([AC_INIT_PREPARE], +[_MOZ_AC_INIT_PREPARE($1) +MOZ_CONFIG_LOG_TRAP +]) + +dnl Disable the trap when running sub-configures. +define([_MOZ_AC_OUTPUT_SUBDIRS], defn([AC_OUTPUT_SUBDIRS])) +define([AC_OUTPUT_SUBDIRS], +[trap '' EXIT +_MOZ_AC_OUTPUT_SUBDIRS($1) +MOZ_CONFIG_LOG_TRAP +]) diff --git a/build/autoconf/install-sh b/build/autoconf/install-sh new file mode 100644 index 000000000..a4be13e59 --- /dev/null +++ b/build/autoconf/install-sh @@ -0,0 +1,123 @@ +#!/bin/sh +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + + +# +# install - install a program, script, or datafile +# This comes from X11R5; it is not part of GNU. +# +# $XConsortium: install.sh,v 1.2 89/12/18 14:47:22 jim Exp $ +# +# This script is compatible with the BSD install script, but was written +# from scratch. +# + + +# set DOITPROG to echo to test this script + +# Don't use :- since 4.3BSD and earlier shells don't like it. +doit="${DOITPROG-}" + + +# put in absolute paths if you don't have them in your path; or use env. vars. + +mvprog="${MVPROG-mv}" +cpprog="${CPPROG-cp}" +chmodprog="${CHMODPROG-chmod}" +chownprog="${CHOWNPROG-chown}" +chgrpprog="${CHGRPPROG-chgrp}" +stripprog="${STRIPPROG-strip}" +rmprog="${RMPROG-rm}" + +instcmd="$mvprog" +chmodcmd="" +chowncmd="" +chgrpcmd="" +stripcmd="" +rmcmd="$rmprog -f" +mvcmd="$mvprog" +src="" +dst="" + +while [ x"$1" != x ]; do + case $1 in + -c) instcmd="$cpprog" + shift + continue;; + + -m) chmodcmd="$chmodprog $2" + shift + shift + continue;; + + -o) chowncmd="$chownprog $2" + shift + shift + continue;; + + -g) chgrpcmd="$chgrpprog $2" + shift + shift + continue;; + + -s) stripcmd="$stripprog" + shift + continue;; + + *) if [ x"$src" = x ] + then + src=$1 + else + dst=$1 + fi + shift + continue;; + esac +done + +if [ x"$src" = x ] +then + echo "install: no input file specified" + exit 1 +fi + +if [ x"$dst" = x ] +then + echo "install: no destination specified" + exit 1 +fi + + +# If destination is a directory, append the input filename; if your system +# does not like double slashes in filenames, you may need to add some logic + +if [ -d $dst ] +then + dst="$dst"/`basename $src` +fi + +# Make a temp file name in the proper directory. + +dstdir=`dirname $dst` +dsttmp=$dstdir/#inst.$$# + +# Move or copy the file name to the temp name + +$doit $instcmd $src $dsttmp + +# and set any options; do chmod last to preserve setuid bits + +if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; fi +if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; fi +if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; fi +if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; fi + +# Now rename the file to the real destination. + +$doit $rmcmd $dst +$doit $mvcmd $dsttmp $dst + + +exit 0 diff --git a/build/autoconf/libstdcxx.py b/build/autoconf/libstdcxx.py new file mode 100644 index 000000000..f5d1f7605 --- /dev/null +++ b/build/autoconf/libstdcxx.py @@ -0,0 +1,77 @@ +#!/usr/bin/python +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + + +# This script find the version of libstdc++ and prints it as single number +# with 8 bits per element. For example, GLIBCXX_3.4.10 becomes +# 3 << 16 | 4 << 8 | 10 = 197642. This format is easy to use +# in the C preprocessor. + +# We find out both the host and target versions. Since the output +# will be used from shell, we just print the two assignments and evaluate +# them from shell. + +import os +import subprocess +import re + +re_for_ld = re.compile('.*\((.*)\).*') + +def parse_readelf_line(x): + """Return the version from a readelf line that looks like: + 0x00ec: Rev: 1 Flags: none Index: 8 Cnt: 2 Name: GLIBCXX_3.4.6 + """ + return x.split(':')[-1].split('_')[-1].strip() + +def parse_ld_line(x): + """Parse a line from the output of ld -t. The output of gold is just + the full path, gnu ld prints "-lstdc++ (path)". + """ + t = re_for_ld.match(x) + if t: + return t.groups()[0].strip() + return x.strip() + +def split_ver(v): + """Covert the string '1.2.3' into the list [1,2,3] + """ + return [int(x) for x in v.split('.')] + +def cmp_ver(a, b): + """Compare versions in the form 'a.b.c' + """ + for (i, j) in zip(split_ver(a), split_ver(b)): + if i != j: + return i - j + return 0 + +def encode_ver(v): + """Encode the version as a single number. + """ + t = split_ver(v) + return t[0] << 16 | t[1] << 8 | t[2] + +def find_version(e): + """Given the value of environment variable CXX or HOST_CXX, find the + version of the libstdc++ it uses. + """ + args = e.split() + args += ['-shared', '-Wl,-t'] + p = subprocess.Popen(args, stderr=subprocess.STDOUT, stdout=subprocess.PIPE) + candidates = [x for x in p.stdout if 'libstdc++.so' in x] + assert len(candidates) == 1 + libstdcxx = parse_ld_line(candidates[-1]) + + p = subprocess.Popen(['readelf', '-V', libstdcxx], stdout=subprocess.PIPE) + versions = [parse_readelf_line(x) + for x in p.stdout.readlines() if 'Name: GLIBCXX' in x] + last_version = sorted(versions, cmp = cmp_ver)[-1] + return encode_ver(last_version) + +if __name__ == '__main__': + cxx_env = os.environ['CXX'] + print 'MOZ_LIBSTDCXX_TARGET_VERSION=%s' % find_version(cxx_env) + host_cxx_env = os.environ.get('HOST_CXX', cxx_env) + print 'MOZ_LIBSTDCXX_HOST_VERSION=%s' % find_version(host_cxx_env) diff --git a/build/autoconf/linux.m4 b/build/autoconf/linux.m4 new file mode 100644 index 000000000..10a8cab85 --- /dev/null +++ b/build/autoconf/linux.m4 @@ -0,0 +1,39 @@ +dnl This Source Code Form is subject to the terms of the Mozilla Public +dnl License, v. 2.0. If a copy of the MPL was not distributed with this +dnl file, You can obtain one at http://mozilla.org/MPL/2.0/. + +AC_DEFUN([MOZ_LINUX_PERF_EVENT], +[ + +MOZ_ARG_WITH_STRING(linux-headers, +[ --with-linux-headers=DIR + location where the Linux kernel headers can be found], + linux_headers=$withval) + +LINUX_HEADERS_INCLUDES= + +if test "$linux_headers"; then + LINUX_HEADERS_INCLUDES="-I$linux_headers" +fi + +_SAVE_CFLAGS="$CFLAGS" +CFLAGS="$CFLAGS $LINUX_HEADERS_INCLUDES" + +dnl Performance measurement headers. +MOZ_CHECK_HEADER(linux/perf_event.h, + [AC_CACHE_CHECK(for perf_event_open system call,ac_cv_perf_event_open, + [AC_TRY_COMPILE([#include <asm/unistd.h>],[return sizeof(__NR_perf_event_open);], + ac_cv_perf_event_open=yes, + ac_cv_perf_event_open=no)])]) +if test "$ac_cv_perf_event_open" = "yes"; then + HAVE_LINUX_PERF_EVENT_H=1 +else + HAVE_LINUX_PERF_EVENT_H= + LINUX_HEADERS_INCLUDES= +fi +AC_SUBST(HAVE_LINUX_PERF_EVENT_H) +AC_SUBST(LINUX_HEADERS_INCLUDES) + +CFLAGS="$_SAVE_CFLAGS" + +]) diff --git a/build/autoconf/llvm-pr8927.m4 b/build/autoconf/llvm-pr8927.m4 new file mode 100644 index 000000000..0c3033ba0 --- /dev/null +++ b/build/autoconf/llvm-pr8927.m4 @@ -0,0 +1,52 @@ +dnl This Source Code Form is subject to the terms of the Mozilla Public +dnl License, v. 2.0. If a copy of the MPL was not distributed with this +dnl file, You can obtain one at http://mozilla.org/MPL/2.0/. + +dnl Check if the compiler suffers from http://llvm.org/pr8927. If so, ask the +dnl user to upgrade. + +AC_DEFUN([MOZ_LLVM_PR8927], +[ +AC_MSG_CHECKING(for llvm pr8927) +ac_have_llvm_pr8927="no" +AC_LANG_SAVE +AC_LANG_C + +_SAVE_CFLAGS=$CFLAGS +CFLAGS="-O2" +AC_TRY_RUN([ +struct foobar { + int x; +}; +static const struct foobar* foo() { + static const struct foobar d = { 0 }; + return &d; +} +static const struct foobar* bar() { + static const struct foobar d = { 0 }; + return &d; +} +__attribute__((noinline)) int zed(const struct foobar *a, + const struct foobar *b) { + return a == b; +} +int main() { + return zed(foo(), bar()); +} +], true, + ac_have_llvm_pr8927="yes", + true) +CFLAGS="$_SAVE_CFLAGS" + +AC_LANG_RESTORE + +if test "$ac_have_llvm_pr8927" = "yes"; then + AC_MSG_RESULT(yes) + echo This compiler would miscompile firefox, plase upgrade. + echo see http://developer.mozilla.org/en-US/docs/Developer_Guide/Build_Instructions/Mac_OS_X_Prerequisites + echo for more information. + exit 1 +else + AC_MSG_RESULT(no) +fi +]) diff --git a/build/autoconf/lto.m4 b/build/autoconf/lto.m4 new file mode 100644 index 000000000..572a04b48 --- /dev/null +++ b/build/autoconf/lto.m4 @@ -0,0 +1,19 @@ +dnl This Source Code Form is subject to the terms of the Mozilla Public +dnl License, v. 2.0. If a copy of the MPL was not distributed with this +dnl file, You can obtain one at http://mozilla.org/MPL/2.0/. + +dnl check if the build is using lto. This is really primitive and only detects llvm based +dnl compilers right now. +AC_DEFUN([MOZ_DOING_LTO], +[ + cat > conftest.c <<EOF + int foo = 1; +EOF + $1=no + if ${CC-cc} ${CFLAGS} -S conftest.c -o conftest.s >/dev/null 2>&1; then + if grep '^target triple =' conftest.s; then + $1=yes + fi + fi + rm -f conftest.[cs] +]) diff --git a/build/autoconf/match-dir.sh b/build/autoconf/match-dir.sh new file mode 100644 index 000000000..3c22809d9 --- /dev/null +++ b/build/autoconf/match-dir.sh @@ -0,0 +1,69 @@ +#!/bin/sh +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +# +# This script will match a dir with a set of dirs. +# +# Usage: match-dir.sh match [dir1 dir2 ... dirn] +# +# Send comments, improvements, bugs to ramiro@netscape.com +# + +if [ -f Makefile ]; then + MAKEFILE="Makefile" +else + if [ -f Makefile.in ]; then + MAKEFILE="Makefile.in" + else + echo + echo "There ain't no 'Makefile' or 'Makefile.in' over here: $pwd, dude." + echo + exit 1 + fi +fi + +# Use DEPTH in the Makefile.in to determine the depth +depth=`grep -w DEPTH ${MAKEFILE} | grep "\.\." | awk -F"=" '{ print $2; }'` +cwd=`pwd` + +# Determine the depth count +n=`echo $depth | tr '/' ' ' | wc -w` + +cd $depth +objdir=`pwd` + +path=`echo $cwd | sed "s|^${objdir}/||"` + +match=$path + +for i in $* +do +# echo "Looking for $match in $i" + + echo $i | grep -q -x $match + + if [ $? -eq 0 ] + then + echo "1" + + exit 0 + fi + +# echo "Looking for $i in $match" + + echo $match | grep -q $i + + if [ $? -eq 0 ] + then + echo "1" + + exit 0 + fi +done + +echo "0" + +exit 0 diff --git a/build/autoconf/mozcommonheader.m4 b/build/autoconf/mozcommonheader.m4 new file mode 100644 index 000000000..2765217bd --- /dev/null +++ b/build/autoconf/mozcommonheader.m4 @@ -0,0 +1,9 @@ +dnl This Source Code Form is subject to the terms of the Mozilla Public +dnl License, v. 2.0. If a copy of the MPL was not distributed with this +dnl file, You can obtain one at http://mozilla.org/MPL/2.0/. + +AC_DEFUN(MOZ_CHECK_COMMON_HEADERS, + MOZ_CHECK_HEADERS(sys/byteorder.h compat.h getopt.h sys/bitypes.h \ + memory.h unistd.h gnu/libc-version.h nl_types.h malloc.h \ + X11/XKBlib.h io.h cpuid.h) +) diff --git a/build/autoconf/mozconfig-find b/build/autoconf/mozconfig-find new file mode 100644 index 000000000..97dd90c35 --- /dev/null +++ b/build/autoconf/mozconfig-find @@ -0,0 +1,76 @@ +#! /bin/sh +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +# mozconfigfind - Loads options from .mozconfig onto configure's +# command-line. The .mozconfig file is searched for in the +# order: +# If $MOZCONFIG is set, use that. +# If one of $TOPSRCDIR/.mozconfig or $TOPSRCDIR/mozconfig exists, use it. +# If both exist, or if various legacy locations contain a mozconfig, error. +# Otherwise, use the default build options. +# +topsrcdir=$1 + +abspath() { + if uname -s | grep -q MINGW; then + # We have no way to figure out whether we're in gmake or pymake right + # now. gmake gives us Unix-style paths while pymake gives us Windows-style + # paths, so attempt to handle both. + regexes='^\([A-Za-z]:\|\\\\\|\/\) ^\/' + else + regexes='^\/' + fi + + for regex in $regexes; do + if echo $1 | grep -q $regex; then + echo $1 + return + fi + done + + # If we're at this point, we have a relative path + echo `pwd`/$1 +} + +if [ -n "$MOZCONFIG" ] && ! [ -f "$MOZCONFIG" ]; then + echo "Specified MOZCONFIG \"$MOZCONFIG\" does not exist!" 1>&2 + exit 1 +fi + +if [ -n "$MOZ_MYCONFIG" ]; then + echo "Your environment currently has the MOZ_MYCONFIG variable set to \"$MOZ_MYCONFIG\". MOZ_MYCONFIG is no longer supported. Please use MOZCONFIG instead." 1>&2 + exit 1 +fi + +if [ -z "$MOZCONFIG" ] && [ -f "$topsrcdir/.mozconfig" ] && [ -f "$topsrcdir/mozconfig" ]; then + echo "Both \$topsrcdir/.mozconfig and \$topsrcdir/mozconfig are supported, but you must choose only one. Please remove the other." 1>&2 + exit 1 +fi + +for _config in "$MOZCONFIG" \ + "$topsrcdir/.mozconfig" \ + "$topsrcdir/mozconfig" +do + if test -f "$_config"; then + abspath $_config + exit 0 + fi +done + +# We used to support a number of other implicit .mozconfig locations. We now +# detect if we were about to use any of these locations and issue an error if we +# find any. +for _config in "$topsrcdir/mozconfig.sh" \ + "$topsrcdir/myconfig.sh" \ + "$HOME/.mozconfig" \ + "$HOME/.mozconfig.sh" \ + "$HOME/.mozmyconfig.sh" +do + if test -f "$_config"; then + echo "You currently have a mozconfig at \"$_config\". This implicit location is no longer supported. Please move it to $topsrcdir/.mozconfig or specify it explicitly via \$MOZCONFIG." 1>&2 + exit 1 + fi +done diff --git a/build/autoconf/mozconfig2client-mk b/build/autoconf/mozconfig2client-mk new file mode 100644 index 000000000..1b632226d --- /dev/null +++ b/build/autoconf/mozconfig2client-mk @@ -0,0 +1,80 @@ +#! /bin/sh +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +# mozconfig2client-mk - Translates .mozconfig into options for client.mk. +# Prints defines to stdout. +# +# See mozconfig2configure for more details + +print_header() { + cat <<EOF +# gmake +# This file is automatically generated for client.mk. +# Do not edit. Edit $FOUND_MOZCONFIG instead. + +EOF +} + +ac_add_options() { + echo "# $* is used by configure (not client.mk)" +} + +ac_add_app_options() { + echo "# $* is used by configure (not client.mk)" +} + +mk_add_options() { + for _opt + do + # Escape shell characters, space, tab, dollar, quote, backslash, + # and substitute '@<word>@' with '$(<word>)'. + _opt=`echo "$_opt" | sed -e 's/\([\"\\]\)/\\\\\1/g; s/@\([^@]*\)@/\$(\1)/g;'` + echo $_opt; + opts="${opts:+$opts^}$_opt"; + done +} + +mk_echo_options() { + echo "Adding client.mk options from $FOUND_MOZCONFIG:" >&2 + IFS=^ + for _opt in $opts; do + echo " $_opt" >&2 + done +} + +# Main +#-------------------------------------------------- + +scriptdir=`dirname $0` +topsrcdir=$1 +opts="" + +# If the path changes, configure should be rerun +echo "# PATH=$PATH" + +# If FOUND_MOZCONFIG isn't set, look for it and make sure the script doesn't error out +isfoundset=${FOUND_MOZCONFIG+yes} +if [ -z $isfoundset ]; then + FOUND_MOZCONFIG=`$scriptdir/mozconfig-find $topsrcdir` + if [ $? -ne 0 ]; then + echo '$(error Fix above errors before continuing.)' + else + isfoundset=yes + fi +fi + +if [ -n $isfoundset ]; then + if [ "$FOUND_MOZCONFIG" ] + then + print_header + . "$FOUND_MOZCONFIG" + fi + echo "export FOUND_MOZCONFIG := $FOUND_MOZCONFIG" + + if [ "$opts" ]; then + mk_echo_options + fi +fi diff --git a/build/autoconf/mozconfig2configure b/build/autoconf/mozconfig2configure new file mode 100644 index 000000000..3e502c0b8 --- /dev/null +++ b/build/autoconf/mozconfig2configure @@ -0,0 +1,103 @@ +#! /bin/sh +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +# mozconfig2configure - Loads options from .mozconfig onto configure's +# command-line. See mozconfig-find for how the config file is +# found +# +# The options from .mozconfig are inserted into the command-line +# before the real command-line options. This way the real options +# will override any .mozconfig options. +# +# .mozconfig is a shell script. To add an option to configure's +# command-line use the pre-defined function, ac_add_options, +# +# ac_add_options <configure-option> [<configure-option> ... ] +# +# For example, +# +# ac_add_options --with-pthreads --enable-debug +# +# ac_add_options can be called multiple times in .mozconfig. +# Each call adds more options to configure's command-line. + +# Note: $_AUTOCONF_TOOLS_DIR must be defined in the script that includes this. + +ac_add_options() { + for _opt + do + # Escape shell characters, space, tab, dollar, quote, backslash, parentheses. + _opt=`echo $_opt | sed -e 's/\([\ \ \$\"\\\(\)]\)/\\\\\1/g;s/@\([^@]*\)@/\$\1/g;'` + _opt=`echo $_opt | sed -e 's/@\([^@]*\)@/\$(\1)/g'` + + # Avoid adding duplicates + case "$ac_options" in + # Note that all options in $ac_options are enclosed in quotes, + # so there will always be a last character to match [^-A-Za-z0-9_] + *"\"$_opt[^-A-Za-z0-9_]"* ) ;; + * ) mozconfig_ac_options="$mozconfig_ac_options $_opt" ;; + esac + done +} + +ac_add_app_options() { + APP=$1 + shift; + if [ "$APP" = "$MOZ_BUILD_APP" ]; then + ac_add_options "$*"; + fi +} + +mk_add_options() { + # These options are for client.mk + # configure can safely ignore them. + : +} + +ac_echo_options() { + echo "Adding configure options from $FOUND_MOZCONFIG:" + eval "set -- $mozconfig_ac_options" + for _opt + do + echo " $_opt" + done +} + +# Main +#-------------------------------------------------- +topsrcdir=`dirname $0` +ac_options= +mozconfig_ac_options= + +# Save the real command-line options +for _opt +do + # Escape shell characters, space, tab, dollar, quote, backslash. + _opt=`echo $_opt | sed -e 's/\([\ \ \$\"\\]\)/\\\\\1/g;'` + ac_options="$ac_options \"$_opt\"" +done + + +# If FOUND_MOZCONFIG isn't set, look for it and make sure the script doesn't error out +isfoundset=${FOUND_MOZCONFIG+yes} +if [ -z $isfoundset ]; then + FOUND_MOZCONFIG=`$_AUTOCONF_TOOLS_DIR/mozconfig-find $topsrcdir` + if [ $? -ne 0 ]; then + echo "Fix above errors before continuing." 1>&2 + exit 1 + fi +fi + +if [ "$FOUND_MOZCONFIG" ]; then + . "$FOUND_MOZCONFIG" +fi +export FOUND_MOZCONFIG + +if [ "$mozconfig_ac_options" ]; then + ac_echo_options 1>&2 +fi + +eval "set -- $mozconfig_ac_options $ac_options" diff --git a/build/autoconf/mozheader.m4 b/build/autoconf/mozheader.m4 new file mode 100644 index 000000000..e99e35a40 --- /dev/null +++ b/build/autoconf/mozheader.m4 @@ -0,0 +1,32 @@ +dnl This Source Code Form is subject to the terms of the Mozilla Public +dnl License, v. 2.0. If a copy of the MPL was not distributed with this +dnl file, You can obtain one at http://mozilla.org/MPL/2.0/. + +dnl MOZ_CHECK_HEADER(HEADER-FILE, [ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND [, INCLUDES]]]) +AC_DEFUN([MOZ_CHECK_HEADER], +[ dnl Do the transliteration at runtime so arg 1 can be a shell variable. + ac_safe=`echo "$1" | sed 'y%./+-%__p_%'` + AC_MSG_CHECKING([for $1]) + AC_CACHE_VAL(ac_cv_header_$ac_safe, + [ AC_TRY_COMPILE([$4 +#include <$1>], , + eval "ac_cv_header_$ac_safe=yes", + eval "ac_cv_header_$ac_safe=no") ]) + if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then + AC_MSG_RESULT(yes) + ifelse([$2], , :, [$2]) + else + AC_MSG_RESULT(no) + ifelse([$3], , , [$3]) + fi +]) + +dnl MOZ_CHECK_HEADERS(HEADER-FILE... [, ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND [, INCLUDES]]]) +AC_DEFUN([MOZ_CHECK_HEADERS], +[ for ac_hdr in $1 + do + MOZ_CHECK_HEADER($ac_hdr, + [ ac_tr_hdr=HAVE_`echo $ac_hdr | sed 'y%abcdefghijklmnopqrstuvwxyz./-%ABCDEFGHIJKLMNOPQRSTUVWXYZ___%'` + AC_DEFINE_UNQUOTED($ac_tr_hdr) $2], $3, [$4]) + done +]) diff --git a/build/autoconf/moznbytetype.m4 b/build/autoconf/moznbytetype.m4 new file mode 100644 index 000000000..dfecaa583 --- /dev/null +++ b/build/autoconf/moznbytetype.m4 @@ -0,0 +1,103 @@ +dnl This Source Code Form is subject to the terms of the Mozilla Public +dnl License, v. 2.0. If a copy of the MPL was not distributed with this +dnl file, You can obtain one at http://mozilla.org/MPL/2.0/. + +dnl MOZ_N_BYTE_TYPE(VARIABLE, SIZE, POSSIBLE-TYPES) +dnl +dnl Check to see which of POSSIBLE-TYPES has a size of SIZE. If we +dnl find one, define VARIABLE to be the size-BYTE type. If no type +dnl matches, exit the configure script with an error message. Types +dnl whose written form contains spaces should appear in POSSIBLE-TYPES +dnl enclosed by shell quotes. +dnl +dnl The cache variable moz_cv_n_byte_type_VARIABLE gets set to the +dnl type, if found. +dnl +dnl for example: +dnl MOZ_N_BYTE_TYPE([JS_INT32_T], [4], [int long 'long long' short]) +dnl +AC_DEFUN([MOZ_N_BYTE_TYPE], +[ +dnl The simplest approach would simply be to run a program that says +dnl printf ("%d\n", sizeof ($type)); +dnl But that won't work when cross-compiling; this will. +AC_CACHE_CHECK([for a $2-byte type], moz_cv_n_byte_type_$1, [ + moz_cv_n_byte_type_$1= + for type in $3; do + AC_TRY_COMPILE([], + [ + int a[sizeof ($type) == $2 ? 1 : -1]; + return 0; + ], + [moz_cv_n_byte_type_$1=$type; break], []) + done + if test ! "$moz_cv_n_byte_type_$1"; then + AC_MSG_ERROR([Couldn't find a $2-byte type]) + fi +]) +AC_DEFINE_UNQUOTED($1, [$moz_cv_n_byte_type_$1], + [a $2-byte type on the target machine]) +]) + +dnl MOZ_SIZE_OF_TYPE(VARIABLE, TYPE, POSSIBLE-SIZES) +dnl +dnl Check to see which of POSSIBLE-SIZES is the sizeof(TYPE). If we find one, +dnl define VARIABLE SIZE. If no size matches, exit the configure script with +dnl an error message. +dnl +dnl The cache variable moz_cv_size_of_VARIABLE gets set to the size, if +dnl found. +dnl +dnl for example: +dnl MOZ_SIZE_OF_TYPE([JS_BYTES_PER_WORD], [void*], [4 8]) +AC_DEFUN([MOZ_SIZE_OF_TYPE], +[ +AC_CACHE_CHECK([for the size of $2], moz_cv_size_of_$1, [ + moz_cv_size_of_$1= + for size in $3; do + AC_TRY_COMPILE([], + [ + int a[sizeof ($2) == $size ? 1 : -1]; + return 0; + ], + [moz_cv_size_of_$1=$size; break], []) + done + if test ! "$moz_cv_size_of_$1"; then + AC_MSG_ERROR([No size found for $2]) + fi +]) +AC_DEFINE_UNQUOTED($1, [$moz_cv_size_of_$1]) +]) + +dnl MOZ_ALIGN_OF_TYPE(VARIABLE, TYPE, POSSIBLE-ALIGNS) +dnl +dnl Check to see which of POSSIBLE-ALIGNS is the necessary alignment of TYPE. +dnl If we find one, define VARIABLE ALIGNMENT. If no alignment matches, exit +dnl the configure script with an error message. +dnl +dnl The cache variable moz_cv_align_of_VARIABLE gets set to the size, if +dnl found. +dnl +dnl for example: +dnl MOZ_ALIGN_OF_TYPE(JS_ALIGN_OF_POINTER, void*, 2 4 8 16) +AC_DEFUN([MOZ_ALIGN_OF_TYPE], +[ +AC_CACHE_CHECK([for the alignment of $2], moz_cv_align_of_$1, [ + moz_cv_align_of_$1= + for align in $3; do + AC_TRY_COMPILE([ + #include <stddef.h> + struct aligner { char c; $2 a; }; + ], + [ + int a[offsetof(struct aligner, a) == $align ? 1 : -1]; + return 0; + ], + [moz_cv_align_of_$1=$align; break], []) + done + if test ! "$moz_cv_align_of_$1"; then + AC_MSG_ERROR([No alignment found for $2]) + fi +]) +AC_DEFINE_UNQUOTED($1, [$moz_cv_align_of_$1]) +]) diff --git a/build/autoconf/mozprog.m4 b/build/autoconf/mozprog.m4 new file mode 100644 index 000000000..08747b495 --- /dev/null +++ b/build/autoconf/mozprog.m4 @@ -0,0 +1,42 @@ +dnl This Source Code Form is subject to the terms of the Mozilla Public +dnl License, v. 2.0. If a copy of the MPL was not distributed with this +dnl file, You can obtain one at http://mozilla.org/MPL/2.0/. + +AC_DEFUN([MOZ_PROG_CHECKMSYS], +[AC_REQUIRE([AC_INIT_BINSH])dnl +if test `uname -s | grep -c MINGW 2>/dev/null` != "0"; then + msyshost=1 +fi +]) + +AC_DEFUN([MOZ_PATH_PROG], +[ AC_PATH_PROG($1,$2,$3,$4) + if test "$msyshost"; then + case "[$]$1" in + /*) + tmp_DIRNAME=`dirname "[$]$1"` + tmp_BASENAME=`basename "[$]$1"` + tmp_PWD=`cd "$tmp_DIRNAME" && pwd -W` + $1="$tmp_PWD/$tmp_BASENAME" + if test -e "[$]$1.exe"; then + $1="[$]$1.exe" + fi + esac + fi +]) + +AC_DEFUN([MOZ_PATH_PROGS], +[ AC_PATH_PROGS($1,$2,$3,$4) + if test "$msyshost"; then + case "[$]$1" in + /*) + tmp_DIRNAME=`dirname "[$]$1"` + tmp_BASENAME=`basename "[$]$1"` + tmp_PWD=`cd "$tmp_DIRNAME" && pwd -W` + $1="$tmp_PWD/$tmp_BASENAME" + if test -e "[$]$1.exe"; then + $1="[$]$1.exe" + fi + esac + fi +]) diff --git a/build/autoconf/nspr.m4 b/build/autoconf/nspr.m4 new file mode 100644 index 000000000..68087b45c --- /dev/null +++ b/build/autoconf/nspr.m4 @@ -0,0 +1,102 @@ +# -*- tab-width: 4; -*- +# Configure paths for NSPR +# Public domain - Chris Seawood <cls@seawood.org> 2001-04-05 +# Based upon gtk.m4 (also PD) by Owen Taylor + +dnl AM_PATH_NSPR([MINIMUM-VERSION, [ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND]]]) +dnl Test for NSPR, and define NSPR_CFLAGS and NSPR_LIBS +dnl +dnl If the nspr-config script is available, use it to find the +dnl appropriate CFLAGS and LIBS, and to check for the required +dnl version, and run ACTION-IF-FOUND. +dnl +dnl Otherwise, if NO_NSPR_CONFIG_SYSTEM_VERSION is set, we use it, +dnl NO_NSPR_CONFIG_SYSTEM_CFLAGS, and NO_NSPR_CONFIG_SYSTEM_LIBS to +dnl provide default values, and run ACTION-IF-FOUND. (Some systems +dnl ship NSPR without nspr-config, but can glean the appropriate flags +dnl and version.) +dnl +dnl Otherwise, run ACTION-IF-NOT-FOUND. +AC_DEFUN([AM_PATH_NSPR], +[dnl + +AC_ARG_WITH(nspr-prefix, + [ --with-nspr-prefix=PFX Prefix where NSPR is installed], + nspr_config_prefix="$withval", + nspr_config_prefix="") + +AC_ARG_WITH(nspr-exec-prefix, + [ --with-nspr-exec-prefix=PFX + Exec prefix where NSPR is installed], + nspr_config_exec_prefix="$withval", + nspr_config_exec_prefix="") + + if test -n "$nspr_config_exec_prefix"; then + nspr_config_args="$nspr_config_args --exec-prefix=$nspr_config_exec_prefix" + if test -z "$NSPR_CONFIG"; then + NSPR_CONFIG=$nspr_config_exec_prefix/bin/nspr-config + fi + fi + if test -n "$nspr_config_prefix"; then + nspr_config_args="$nspr_config_args --prefix=$nspr_config_prefix" + if test -z "$NSPR_CONFIG"; then + NSPR_CONFIG=$nspr_config_prefix/bin/nspr-config + fi + fi + + unset ac_cv_path_NSPR_CONFIG + AC_PATH_PROG(NSPR_CONFIG, nspr-config, no) + min_nspr_version=ifelse([$1], ,4.0.0,$1) + AC_MSG_CHECKING(for NSPR - version >= $min_nspr_version) + + no_nspr="" + if test "$NSPR_CONFIG" != "no"; then + NSPR_CFLAGS=`$NSPR_CONFIG $nspr_config_args --cflags` + NSPR_LIBS=`$NSPR_CONFIG $nspr_config_args --libs` + NSPR_VERSION_STRING=`$NSPR_CONFIG $nspr_config_args --version` + elif test -n "${NO_NSPR_CONFIG_SYSTEM_VERSION}"; then + NSPR_CFLAGS="${NO_NSPR_CONFIG_SYSTEM_CFLAGS}" + NSPR_LIBS="${NO_NSPR_CONFIG_SYSTEM_LDFLAGS}" + NSPR_VERSION_STRING="$NO_NSPR_CONFIG_SYSTEM_VERSION" + else + no_nspr="yes" + fi + + if test -z "$no_nspr"; then + nspr_config_major_version=`echo $NSPR_VERSION_STRING | \ + sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\1/'` + nspr_config_minor_version=`echo $NSPR_VERSION_STRING | \ + sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\2/'` + nspr_config_micro_version=`echo $NSPR_VERSION_STRING | \ + sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\3/'` + min_nspr_major_version=`echo $min_nspr_version | \ + sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\1/'` + min_nspr_minor_version=`echo $min_nspr_version | \ + sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\2/'` + min_nspr_micro_version=`echo $min_nspr_version | \ + sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\3/'` + if test "$nspr_config_major_version" -ne "$min_nspr_major_version"; then + no_nspr="yes" + elif test "$nspr_config_major_version" -eq "$min_nspr_major_version" && + test "$nspr_config_minor_version" -lt "$min_nspr_minor_version"; then + no_nspr="yes" + elif test "$nspr_config_major_version" -eq "$min_nspr_major_version" && + test "$nspr_config_minor_version" -eq "$min_nspr_minor_version" && + test "$nspr_config_micro_version" -lt "$min_nspr_micro_version"; then + no_nspr="yes" + fi + fi + + if test -z "$no_nspr"; then + AC_MSG_RESULT(yes) + ifelse([$2], , :, [$2]) + else + AC_MSG_RESULT(no) + ifelse([$3], , :, [$3]) + fi + + + AC_SUBST(NSPR_CFLAGS) + AC_SUBST(NSPR_LIBS) + +]) diff --git a/build/autoconf/nss.m4 b/build/autoconf/nss.m4 new file mode 100644 index 000000000..14e585746 --- /dev/null +++ b/build/autoconf/nss.m4 @@ -0,0 +1,83 @@ +# -*- tab-width: 4; -*- +# Configure paths for NSS +# Public domain - Chris Seawood <cls@seawood.org> 2001-04-05 +# Based upon gtk.m4 (also PD) by Owen Taylor + +dnl AM_PATH_NSS([MINIMUM-VERSION, [ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND]]]) +dnl Test for NSS, and define NSS_CFLAGS and NSS_LIBS +AC_DEFUN([AM_PATH_NSS], +[dnl + +AC_ARG_WITH(nss-prefix, + [ --with-nss-prefix=PFX Prefix where NSS is installed], + nss_config_prefix="$withval", + nss_config_prefix="") + +AC_ARG_WITH(nss-exec-prefix, + [ --with-nss-exec-prefix=PFX + Exec prefix where NSS is installed], + nss_config_exec_prefix="$withval", + nss_config_exec_prefix="") + + if test -n "$nss_config_exec_prefix"; then + nss_config_args="$nss_config_args --exec-prefix=$nss_config_exec_prefix" + if test -z "$NSS_CONFIG"; then + NSS_CONFIG=$nss_config_exec_prefix/bin/nss-config + fi + fi + if test -n "$nss_config_prefix"; then + nss_config_args="$nss_config_args --prefix=$nss_config_prefix" + if test -z "$NSS_CONFIG"; then + NSS_CONFIG=$nss_config_prefix/bin/nss-config + fi + fi + + unset ac_cv_path_NSS_CONFIG + AC_PATH_PROG(NSS_CONFIG, nss-config, no) + min_nss_version=ifelse([$1], ,3.0.0,$1) + AC_MSG_CHECKING(for NSS - version >= $min_nss_version) + + no_nss="" + if test "$NSS_CONFIG" = "no"; then + no_nss="yes" + else + NSS_CFLAGS=`$NSS_CONFIG $nss_config_args --cflags` + NSS_LIBS=`$NSS_CONFIG $nss_config_args --libs` + + nss_config_major_version=`$NSS_CONFIG $nss_config_args --version | \ + sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\1/'` + nss_config_minor_version=`$NSS_CONFIG $nss_config_args --version | \ + sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\2/'` + nss_config_micro_version=`$NSS_CONFIG $nss_config_args --version | \ + sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\3/'` + min_nss_major_version=`echo $min_nss_version | \ + sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\1/'` + min_nss_minor_version=`echo $min_nss_version | \ + sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\2/'` + min_nss_micro_version=`echo $min_nss_version | \ + sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\3/'` + if test "$nss_config_major_version" -lt "$min_nss_major_version"; then + no_nss="yes" + elif test "$nss_config_major_version" -eq "$min_nss_major_version" && + test "$nss_config_minor_version" -lt "$min_nss_minor_version"; then + no_nss="yes" + elif test "$nss_config_major_version" -eq "$min_nss_major_version" && + test "$nss_config_minor_version" -eq "$min_nss_minor_version" && + test "$nss_config_micro_version" -lt "$min_nss_micro_version"; then + no_nss="yes" + fi + fi + + if test -z "$no_nss"; then + AC_MSG_RESULT(yes) + ifelse([$2], , :, [$2]) + else + AC_MSG_RESULT(no) + ifelse([$3], , :, [$3]) + fi + + + AC_SUBST(NSS_CFLAGS) + AC_SUBST(NSS_LIBS) + +]) diff --git a/build/autoconf/pkg.m4 b/build/autoconf/pkg.m4 new file mode 100644 index 000000000..4b0083072 --- /dev/null +++ b/build/autoconf/pkg.m4 @@ -0,0 +1,63 @@ +dnl This Source Code Form is subject to the terms of the Mozilla Public +dnl License, v. 2.0. If a copy of the MPL was not distributed with this +dnl file, You can obtain one at http://mozilla.org/MPL/2.0/. + +# PKG_CHECK_MODULES(GSTUFF, gtk+-2.0 >= 1.3 glib = 1.3.4, action-if, action-not) +# defines GSTUFF_LIBS, GSTUFF_CFLAGS, see pkg-config man page +# also defines GSTUFF_PKG_ERRORS on error +AC_DEFUN([PKG_CHECK_MODULES], +[succeeded=no + + if test -z "$PKG_CONFIG"; then + AC_PATH_PROG(PKG_CONFIG, pkg-config, no) + fi + + if test "$PKG_CONFIG" = "no" ; then + echo "*** The pkg-config script could not be found. Make sure it is" + echo "*** in your path, or set the PKG_CONFIG environment variable" + echo "*** to the full path to pkg-config." + echo "*** Or see http://www.freedesktop.org/software/pkgconfig to get pkg-config." + else + PKG_CONFIG_MIN_VERSION=0.9.0 + if $PKG_CONFIG --atleast-pkgconfig-version $PKG_CONFIG_MIN_VERSION; then + AC_MSG_CHECKING(for $2) + + if $PKG_CONFIG --exists "$2" ; then + AC_MSG_RESULT(yes) + succeeded=yes + + AC_MSG_CHECKING($1_CFLAGS) + $1_CFLAGS=`$PKG_CONFIG --cflags "$2"` + AC_MSG_RESULT($$1_CFLAGS) + + AC_MSG_CHECKING($1_LIBS) + ## Remove evil flags like -Wl,--export-dynamic + $1_LIBS="`$PKG_CONFIG --libs \"$2\" |sed s/-Wl,--export-dynamic//g`" + AC_MSG_RESULT($$1_LIBS) + else + $1_CFLAGS="" + $1_LIBS="" + ## If we have a custom action on failure, don't print errors, but + ## do set a variable so people can do so. + $1_PKG_ERRORS=`$PKG_CONFIG --errors-to-stdout --print-errors "$2"` + ifelse([$4], ,echo $$1_PKG_ERRORS,) + fi + + AC_SUBST($1_CFLAGS) + AC_SUBST($1_LIBS) + else + echo "*** Your version of pkg-config is too old. You need version $PKG_CONFIG_MIN_VERSION or newer." + echo "*** See http://www.freedesktop.org/software/pkgconfig" + fi + fi + + if test $succeeded = yes; then + ifelse([$3], , :, [$3]) + else + if test "$COMPILE_ENVIRONMENT"; then + ifelse([$4], , AC_MSG_ERROR([Library requirements ($2) not met; consider adjusting the PKG_CONFIG_PATH environment variable if your libraries are in a nonstandard prefix so pkg-config can find them.]), [$4]) + fi + fi +]) + + diff --git a/build/autoconf/python-virtualenv.m4 b/build/autoconf/python-virtualenv.m4 new file mode 100644 index 000000000..ef8e476a8 --- /dev/null +++ b/build/autoconf/python-virtualenv.m4 @@ -0,0 +1,77 @@ +dnl This Source Code Form is subject to the terms of the Mozilla Public +dnl License, v. 2.0. If a copy of the MPL was not distributed with this +dnl file, You can obtain one at http://mozilla.org/MPL/2.0/. + +AC_DEFUN([MOZ_PYTHON], +[ + +dnl We honor the Python path defined in an environment variable. This is used +dnl to pass the virtualenv's Python from the main configure to SpiderMonkey's +dnl configure, for example. +if test -z "$PYTHON"; then + MOZ_PATH_PROGS(PYTHON, $PYTHON python2.7 python) + if test -z "$PYTHON"; then + AC_MSG_ERROR([python was not found in \$PATH]) + fi +else + AC_MSG_RESULT([Using Python from environment variable \$PYTHON]) +fi + +_virtualenv_topsrcdir= +_virtualenv_populate_path= + +dnl If this is a mozilla-central, we'll find the virtualenv in the top +dnl source directory. If this is a SpiderMonkey build, we assume we're at +dnl js/src and try to find the virtualenv from the mozilla-central root. +for base in $MOZILLA_CENTRAL_PATH $_topsrcdir $_topsrcdir/../..; do + possible=$base/build/virtualenv/populate_virtualenv.py + + if test -e $possible; then + _virtualenv_topsrcdir=$base + _virtualenv_populate_path=$possible + break + fi +done + +if test -z $_virtualenv_populate_path; then + AC_MSG_ERROR([Unable to find Virtualenv population script. In order +to build, you will need mozilla-central's virtualenv. + +If you are building from a mozilla-central checkout, you should never see this +message. If you are building from a source archive, the source archive was +likely not created properly (it is missing the virtualenv files). + +If you have a copy of mozilla-central available, define the +MOZILLA_CENTRAL_PATH environment variable to the top source directory of +mozilla-central and relaunch configure.]) + +fi + +if test -z $DONT_POPULATE_VIRTUALENV; then + AC_MSG_RESULT([Creating Python environment]) + dnl This verifies our Python version is sane and ensures the Python + dnl virtualenv is present and up to date. It sanitizes the environment + dnl for us, so we don't need to clean anything out. + $PYTHON $_virtualenv_populate_path \ + $_virtualenv_topsrcdir $MOZ_BUILD_ROOT $MOZ_BUILD_ROOT/_virtualenv || exit 1 + + case "$host_os" in + mingw*) + PYTHON=`cd $MOZ_BUILD_ROOT && pwd -W`/_virtualenv/Scripts/python.exe + ;; + *) + PYTHON=$MOZ_BUILD_ROOT/_virtualenv/bin/python + ;; + esac +fi + +AC_SUBST(PYTHON) + +AC_MSG_CHECKING([Python environment is Mozilla virtualenv]) +$PYTHON -c "import mozbuild.base" +if test "$?" != 0; then + AC_MSG_ERROR([Python environment does not appear to be sane.]) +fi +AC_MSG_RESULT([yes]) +]) + diff --git a/build/autoconf/subconfigure.m4 b/build/autoconf/subconfigure.m4 new file mode 100644 index 000000000..56c00cfce --- /dev/null +++ b/build/autoconf/subconfigure.m4 @@ -0,0 +1,46 @@ +dnl We are not running in a real autoconf environment. So we're using real m4 +dnl here, not the crazier environment that autoconf provides. + +dnl Autoconf expects [] for quotes; give it to them +changequote([, ]) + +dnl AC_DEFUN is provided to use instead of define in autoconf. Provide it too. +define([AC_DEFUN], [define($1, [$2])]) + +dnl AC_ARG_ENABLE(FEATURE, HELP-STRING, IF-TRUE[, IF-FALSE]) +dnl We have to ignore the help string due to how help works in autoconf... +AC_DEFUN([AC_ARG_ENABLE], +[#] Check whether --enable-[$1] or --disable-[$1] was given. +[if test "[${enable_]patsubst([$1], -, _)+set}" = set; then + enableval="[$enable_]patsubst([$1], -, _)" + $3 +ifelse([$4], , , [else + $4 +])dnl +fi +]) + +dnl AC_MSG_ERROR(error-description) +AC_DEFUN([AC_MSG_ERROR], [{ echo "configure: error: $1" 1>&2; exit 1; }]) + +AC_DEFUN([AC_MSG_WARN], [ echo "configure: warning: $1" 1>&2 ]) + +dnl Add the variable to the list of substitution variables +AC_DEFUN([AC_SUBST], +[ +_subconfigure_ac_subst_args="$_subconfigure_ac_subst_args $1" +]) + +dnl Override for AC_DEFINE. +AC_DEFUN([AC_DEFINE], +[ +cat >>confdefs.h <<\EOF +[#define] $1 ifelse($#, 2, [$2], $#, 3, [$2], 1) +EOF +cat >> confdefs.pytmp <<\EOF + (''' $1 ''', ifelse($#, 2, [r''' $2 '''], $#, 3, [r''' $2 '''], ' 1 ')) +EOF +]) + +dnl AC_OUTPUT_SUBDIRS(subdirectory) +AC_DEFUN([AC_OUTPUT_SUBDIRS], [do_output_subdirs "$1"]) diff --git a/build/autoconf/toolchain.m4 b/build/autoconf/toolchain.m4 new file mode 100644 index 000000000..95d43e527 --- /dev/null +++ b/build/autoconf/toolchain.m4 @@ -0,0 +1,66 @@ +dnl This Source Code Form is subject to the terms of the Mozilla Public +dnl License, v. 2.0. If a copy of the MPL was not distributed with this +dnl file, You can obtain one at http://mozilla.org/MPL/2.0/. + +AC_DEFUN([MOZ_TOOL_VARIABLES], +[ +GNU_AS= +GNU_LD= +GNU_CC= +GNU_CXX= +CC_VERSION='N/A' +CXX_VERSION='N/A' +if test "$GCC" = "yes"; then + GNU_CC=1 + CC_VERSION=`$CC -v 2>&1 | grep 'gcc version'` +fi +if test "$GXX" = "yes"; then + GNU_CXX=1 + CXX_VERSION=`$CXX -v 2>&1 | grep 'gcc version'` + changequote(<<,>>) + GCC_VERSION_FULL=`echo "$CXX_VERSION" | $PERL -pe 's/^.*gcc version ([^ ]*).*/<<$>>1/'` + GCC_VERSION=`echo "$GCC_VERSION_FULL" | $PERL -pe '(split(/\./))[0]>=4&&s/(^\d*\.\d*).*/<<$>>1/;'` + + GCC_MAJOR_VERSION=`echo ${GCC_VERSION} | $AWK -F\. '{ print <<$>>1 }'` + GCC_MINOR_VERSION=`echo ${GCC_VERSION} | $AWK -F\. '{ print <<$>>2 }'` + changequote([,]) +fi + +if test "`echo | $AS -o conftest.out -v 2>&1 | grep -c GNU`" != "0"; then + GNU_AS=1 +fi +rm -f conftest.out +if test "`echo | $LD -v 2>&1 | grep -c GNU`" != "0"; then + GNU_LD=1 +fi +if test "$GNU_CC"; then + if `$CC -print-prog-name=ld` -v 2>&1 | grep -c GNU >/dev/null; then + GCC_USE_GNU_LD=1 + fi +fi + +INTEL_CC= +INTEL_CXX= +if test "$GCC" = yes; then + if test "`$CC -help 2>&1 | grep -c 'Intel(R) C++ Compiler'`" != "0"; then + INTEL_CC=1 + fi +fi + +if test "$GXX" = yes; then + if test "`$CXX -help 2>&1 | grep -c 'Intel(R) C++ Compiler'`" != "0"; then + INTEL_CXX=1 + fi +fi + +CLANG_CC= +CLANG_CXX= +if test "`$CC -v 2>&1 | egrep -c '(clang version|Apple.*clang)'`" != "0"; then + CLANG_CC=1 +fi + +if test "`$CXX -v 2>&1 | egrep -c '(clang version|Apple.*clang)'`" != "0"; then + CLANG_CXX=1 +fi +AC_SUBST(CLANG_CXX) +]) diff --git a/build/autoconf/winsdk.m4 b/build/autoconf/winsdk.m4 new file mode 100644 index 000000000..f2d01d14a --- /dev/null +++ b/build/autoconf/winsdk.m4 @@ -0,0 +1,37 @@ +dnl This Source Code Form is subject to the terms of the Mozilla Public +dnl License, v. 2.0. If a copy of the MPL was not distributed with this +dnl file, You can obtain one at http://mozilla.org/MPL/2.0/. + +dnl Identify which version of the SDK we're building with +dnl Windows Server 2008 and newer SDKs have WinSDKVer.h, get the version +dnl from there +AC_DEFUN([MOZ_FIND_WINSDK_VERSION], [ + MOZ_CHECK_HEADERS([winsdkver.h]) + if test "$ac_cv_header_winsdkver_h" = "yes"; then + dnl Get the highest _WIN32_WINNT and NTDDI versions supported + dnl Take the higher of the two + dnl This is done because the Windows 7 beta SDK reports its + dnl NTDDI_MAXVER to be 0x06000100 instead of 0x06010000, as it should + AC_CACHE_CHECK(for highest Windows version supported by this SDK, + ac_cv_winsdk_maxver, + [cat > conftest.h <<EOF +#include <winsdkver.h> +#include <sdkddkver.h> + +#if (NTDDI_VERSION_FROM_WIN32_WINNT(_WIN32_WINNT_MAXVER) > NTDDI_MAXVER) +#define WINSDK_MAXVER NTDDI_VERSION_FROM_WIN32_WINNT(_WIN32_WINNT_MAXVER) +#else +#define WINSDK_MAXVER NTDDI_MAXVER +#endif + +WINSDK_MAXVER +EOF + ac_cv_winsdk_maxver=`$CPP conftest.h 2>/dev/null | tail -n1` + rm -f conftest.h + ]) + MOZ_WINSDK_MAXVER=${ac_cv_winsdk_maxver} + else + dnl Any SDK which doesn't have WinSDKVer.h is too old. + AC_MSG_ERROR([Your SDK does not have WinSDKVer.h. It is probably too old. Please upgrade to a newer SDK or try running the Windows SDK Configuration Tool and selecting a newer SDK. See https://developer.mozilla.org/En/Windows_SDK_versions for more details on fixing this.]) + fi +]) diff --git a/build/autoconf/wrapper.m4 b/build/autoconf/wrapper.m4 new file mode 100644 index 000000000..179db068a --- /dev/null +++ b/build/autoconf/wrapper.m4 @@ -0,0 +1,22 @@ +dnl This Source Code Form is subject to the terms of the Mozilla Public +dnl License, v. 2.0. If a copy of the MPL was not distributed with this +dnl file, You can obtain one at http://mozilla.org/MPL/2.0/. + +dnl ======================================================================= +dnl = Enable compiling with various compiler wrappers (distcc, ccache, etc) +dnl ======================================================================= +AC_DEFUN([MOZ_CHECK_COMPILER_WRAPPER], +[ +MOZ_ARG_WITH_STRING(compiler_wrapper, +[ --with-compiler-wrapper[=path/to/wrapper] + Enable compiling with wrappers such as distcc and ccache], + COMPILER_WRAPPER=$withval, COMPILER_WRAPPER="no") + +if test "$COMPILER_WRAPPER" != "no"; then + CC="$COMPILER_WRAPPER $CC" + CXX="$COMPILER_WRAPPER $CXX" + MOZ_USING_COMPILER_WRAPPER=1 +fi + +AC_SUBST(MOZ_USING_COMPILER_WRAPPER) +]) diff --git a/build/autoconf/zlib.m4 b/build/autoconf/zlib.m4 new file mode 100644 index 000000000..59c0b758f --- /dev/null +++ b/build/autoconf/zlib.m4 @@ -0,0 +1,54 @@ +dnl This Source Code Form is subject to the terms of the Mozilla Public +dnl License, v. 2.0. If a copy of the MPL was not distributed with this +dnl file, You can obtain one at http://mozilla.org/MPL/2.0/. + +dnl Usage: MOZ_ZLIB_CHECK([version]) + +AC_DEFUN([MOZ_ZLIB_CHECK], +[ + +MOZZLIB=$1 + +MOZ_ARG_WITH_STRING(system-zlib, +[ --with-system-zlib[=PFX] + Use system libz [installed at prefix PFX]], + ZLIB_DIR=$withval) + +if test -z "$MOZ_ZLIB_LIBS$MOZ_ZLIB_CFLAGS$SKIP_LIBRARY_CHECKS"; then + _SAVE_CFLAGS=$CFLAGS + _SAVE_LDFLAGS=$LDFLAGS + _SAVE_LIBS=$LIBS + + if test -n "${ZLIB_DIR}" -a "${ZLIB_DIR}" != "yes"; then + MOZ_ZLIB_CFLAGS="-I${ZLIB_DIR}/include" + MOZ_ZLIB_LIBS="-L${ZLIB_DIR}/lib" + CFLAGS="$MOZ_ZLIB_CFLAGS $CFLAGS" + LDFLAGS="$MOZ_ZLIB_LIBS $LDFLAGS" + fi + if test -z "$ZLIB_DIR" -o "$ZLIB_DIR" = no; then + MOZ_NATIVE_ZLIB= + else + AC_CHECK_LIB(z, gzread, [MOZ_NATIVE_ZLIB=1 MOZ_ZLIB_LIBS="$MOZ_ZLIB_LIBS -lz"], + [MOZ_NATIVE_ZLIB=]) + if test "$MOZ_NATIVE_ZLIB" = 1; then + MOZZLIBNUM=`echo $MOZZLIB | awk -F. changequote(<<, >>)'{printf "0x%x\n", (((<<$>>1 * 16 + <<$>>2) * 16) + <<$>>3) * 16 + <<$>>4}'changequote([, ])` + AC_TRY_COMPILE([ #include <stdio.h> + #include <string.h> + #include <zlib.h> ], + [ #if ZLIB_VERNUM < $MOZZLIBNUM + #error "Insufficient zlib version ($MOZZLIBNUM required)." + #endif ], + MOZ_NATIVE_ZLIB=1, + AC_MSG_ERROR([Insufficient zlib version for --with-system-zlib ($MOZZLIB required)])) + fi + fi + CFLAGS=$_SAVE_CFLAGS + LDFLAGS=$_SAVE_LDFLAGS + LIBS=$_SAVE_LIBS +fi + +AC_SUBST(MOZ_ZLIB_CFLAGS) +AC_SUBST(MOZ_ZLIB_LIBS) +AC_SUBST(MOZ_NATIVE_ZLIB) + +]) diff --git a/build/automation-build.mk b/build/automation-build.mk new file mode 100644 index 000000000..1d1004212 --- /dev/null +++ b/build/automation-build.mk @@ -0,0 +1,73 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +include $(MOZILLA_DIR)/build/binary-location.mk + +browser_path := \"$(browser_path)\" + +_PROFILE_DIR = $(TARGET_DEPTH)/_profile/pgo + +ABSOLUTE_TOPSRCDIR = $(call core_abspath,$(MOZILLA_DIR)) +_CERTS_SRC_DIR = $(ABSOLUTE_TOPSRCDIR)/build/pgo/certs + +AUTOMATION_PPARGS = \ + -DBROWSER_PATH=$(browser_path) \ + -DXPC_BIN_PATH=\"$(LIBXUL_DIST)/bin\" \ + -DBIN_SUFFIX=\"$(BIN_SUFFIX)\" \ + -DPROFILE_DIR=\"$(_PROFILE_DIR)\" \ + -DCERTS_SRC_DIR=\"$(_CERTS_SRC_DIR)\" \ + -DPERL="\"$(PERL)\"" \ + $(NULL) + +ifeq ($(OS_ARCH),Darwin) +AUTOMATION_PPARGS += -DIS_MAC=1 +else +AUTOMATION_PPARGS += -DIS_MAC=0 +endif + +ifeq ($(OS_ARCH),Linux) +AUTOMATION_PPARGS += -DIS_LINUX=1 +else +AUTOMATION_PPARGS += -DIS_LINUX=0 +endif + +ifeq ($(MOZ_BUILD_APP),camino) +AUTOMATION_PPARGS += -DIS_CAMINO=1 +else +AUTOMATION_PPARGS += -DIS_CAMINO=0 +endif + +ifeq ($(host_os), cygwin) +AUTOMATION_PPARGS += -DIS_CYGWIN=1 +endif + +ifeq ($(ENABLE_TESTS), 1) +AUTOMATION_PPARGS += -DIS_TEST_BUILD=1 +else +AUTOMATION_PPARGS += -DIS_TEST_BUILD=0 +endif + +ifeq ($(MOZ_DEBUG), 1) +AUTOMATION_PPARGS += -DIS_DEBUG_BUILD=1 +else +AUTOMATION_PPARGS += -DIS_DEBUG_BUILD=0 +endif + +ifdef MOZ_CRASHREPORTER +AUTOMATION_PPARGS += -DCRASHREPORTER=1 +else +AUTOMATION_PPARGS += -DCRASHREPORTER=0 +endif + +ifdef MOZ_ASAN +AUTOMATION_PPARGS += -DIS_ASAN=1 +else +AUTOMATION_PPARGS += -DIS_ASAN=0 +endif + +automation.py: $(MOZILLA_DIR)/build/automation.py.in $(MOZILLA_DIR)/build/automation-build.mk + $(PYTHON) $(MOZILLA_DIR)/config/Preprocessor.py \ + $(AUTOMATION_PPARGS) $(DEFINES) $(ACDEFINES) $< > $@ + +GARBAGE += automation.py automation.pyc diff --git a/build/automation.py.in b/build/automation.py.in new file mode 100644 index 000000000..f3d75590c --- /dev/null +++ b/build/automation.py.in @@ -0,0 +1,1232 @@ +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +from __future__ import with_statement +import codecs +from datetime import datetime, timedelta +import itertools +import logging +import os +import re +import select +import shutil +import signal +import subprocess +import sys +import threading +import tempfile +import sqlite3 +from string import Template + +SCRIPT_DIR = os.path.abspath(os.path.realpath(os.path.dirname(sys.argv[0]))) +sys.path.insert(0, SCRIPT_DIR) +import automationutils + +# -------------------------------------------------------------- +# TODO: this is a hack for mozbase without virtualenv, remove with bug 849900 +# +here = os.path.dirname(__file__) +mozbase = os.path.realpath(os.path.join(os.path.dirname(here), 'mozbase')) + +if os.path.isdir(mozbase): + for package in os.listdir(mozbase): + sys.path.append(os.path.join(mozbase, package)) + +import mozcrash + +# --------------------------------------------------------------- + +_DEFAULT_PREFERENCE_FILE = os.path.join(SCRIPT_DIR, 'prefs_general.js') + +_DEFAULT_WEB_SERVER = "127.0.0.1" +_DEFAULT_HTTP_PORT = 8888 +_DEFAULT_SSL_PORT = 4443 +_DEFAULT_WEBSOCKET_PORT = 9988 + +# from nsIPrincipal.idl +_APP_STATUS_NOT_INSTALLED = 0 +_APP_STATUS_INSTALLED = 1 +_APP_STATUS_PRIVILEGED = 2 +_APP_STATUS_CERTIFIED = 3 + +#expand _DIST_BIN = __XPC_BIN_PATH__ +#expand _IS_WIN32 = len("__WIN32__") != 0 +#expand _IS_MAC = __IS_MAC__ != 0 +#expand _IS_LINUX = __IS_LINUX__ != 0 +#ifdef IS_CYGWIN +#expand _IS_CYGWIN = __IS_CYGWIN__ == 1 +#else +_IS_CYGWIN = False +#endif +#expand _IS_CAMINO = __IS_CAMINO__ != 0 +#expand _BIN_SUFFIX = __BIN_SUFFIX__ +#expand _PERL = __PERL__ + +#expand _DEFAULT_APP = "./" + __BROWSER_PATH__ +#expand _CERTS_SRC_DIR = __CERTS_SRC_DIR__ +#expand _IS_TEST_BUILD = __IS_TEST_BUILD__ +#expand _IS_DEBUG_BUILD = __IS_DEBUG_BUILD__ +#expand _CRASHREPORTER = __CRASHREPORTER__ == 1 +#expand _IS_ASAN = __IS_ASAN__ == 1 + + +if _IS_WIN32: + import ctypes, ctypes.wintypes, time, msvcrt +else: + import errno + + +def getGlobalLog(): + return _log + +def resetGlobalLog(log): + while _log.handlers: + _log.removeHandler(_log.handlers[0]) + handler = logging.StreamHandler(log) + _log.setLevel(logging.INFO) + _log.addHandler(handler) + +# We use the logging system here primarily because it'll handle multiple +# threads, which is needed to process the output of the server and application +# processes simultaneously. +_log = logging.getLogger() +resetGlobalLog(sys.stdout) + + +################# +# PROFILE SETUP # +################# + +class SyntaxError(Exception): + "Signifies a syntax error on a particular line in server-locations.txt." + + def __init__(self, lineno, msg = None): + self.lineno = lineno + self.msg = msg + + def __str__(self): + s = "Syntax error on line " + str(self.lineno) + if self.msg: + s += ": %s." % self.msg + else: + s += "." + return s + + +class Location: + "Represents a location line in server-locations.txt." + + def __init__(self, scheme, host, port, options): + self.scheme = scheme + self.host = host + self.port = port + self.options = options + +class Automation(object): + """ + Runs the browser from a script, and provides useful utilities + for setting up the browser environment. + """ + + DIST_BIN = _DIST_BIN + IS_WIN32 = _IS_WIN32 + IS_MAC = _IS_MAC + IS_LINUX = _IS_LINUX + IS_CYGWIN = _IS_CYGWIN + IS_CAMINO = _IS_CAMINO + BIN_SUFFIX = _BIN_SUFFIX + PERL = _PERL + + UNIXISH = not IS_WIN32 and not IS_MAC + + DEFAULT_APP = _DEFAULT_APP + CERTS_SRC_DIR = _CERTS_SRC_DIR + IS_TEST_BUILD = _IS_TEST_BUILD + IS_DEBUG_BUILD = _IS_DEBUG_BUILD + CRASHREPORTER = _CRASHREPORTER + IS_ASAN = _IS_ASAN + + # timeout, in seconds + DEFAULT_TIMEOUT = 60.0 + DEFAULT_WEB_SERVER = _DEFAULT_WEB_SERVER + DEFAULT_HTTP_PORT = _DEFAULT_HTTP_PORT + DEFAULT_SSL_PORT = _DEFAULT_SSL_PORT + DEFAULT_WEBSOCKET_PORT = _DEFAULT_WEBSOCKET_PORT + + def __init__(self): + self.log = _log + self.lastTestSeen = "automation.py" + self.haveDumpedScreen = False + + def setServerInfo(self, + webServer = _DEFAULT_WEB_SERVER, + httpPort = _DEFAULT_HTTP_PORT, + sslPort = _DEFAULT_SSL_PORT, + webSocketPort = _DEFAULT_WEBSOCKET_PORT): + self.webServer = webServer + self.httpPort = httpPort + self.sslPort = sslPort + self.webSocketPort = webSocketPort + + @property + def __all__(self): + return [ + "UNIXISH", + "IS_WIN32", + "IS_MAC", + "log", + "runApp", + "Process", + "addCommonOptions", + "initializeProfile", + "DIST_BIN", + "DEFAULT_APP", + "CERTS_SRC_DIR", + "environment", + "IS_TEST_BUILD", + "IS_DEBUG_BUILD", + "DEFAULT_TIMEOUT", + ] + + class Process(subprocess.Popen): + """ + Represents our view of a subprocess. + It adds a kill() method which allows it to be stopped explicitly. + """ + + def __init__(self, + args, + bufsize=0, + executable=None, + stdin=None, + stdout=None, + stderr=None, + preexec_fn=None, + close_fds=False, + shell=False, + cwd=None, + env=None, + universal_newlines=False, + startupinfo=None, + creationflags=0): + args = automationutils.wrapCommand(args) + print "args: %s" % args + subprocess.Popen.__init__(self, args, bufsize, executable, + stdin, stdout, stderr, + preexec_fn, close_fds, + shell, cwd, env, + universal_newlines, startupinfo, creationflags) + self.log = _log + + def kill(self): + if Automation().IS_WIN32: + import platform + pid = "%i" % self.pid + if platform.release() == "2000": + # Windows 2000 needs 'kill.exe' from the + #'Windows 2000 Resource Kit tools'. (See bug 475455.) + try: + subprocess.Popen(["kill", "-f", pid]).wait() + except: + self.log.info("TEST-UNEXPECTED-FAIL | automation.py | Missing 'kill' utility to kill process with pid=%s. Kill it manually!", pid) + else: + # Windows XP and later. + subprocess.Popen(["taskkill", "/F", "/PID", pid]).wait() + else: + os.kill(self.pid, signal.SIGKILL) + + def readLocations(self, locationsPath = "server-locations.txt"): + """ + Reads the locations at which the Mochitest HTTP server is available from + server-locations.txt. + """ + + locationFile = codecs.open(locationsPath, "r", "UTF-8") + + # Perhaps more detail than necessary, but it's the easiest way to make sure + # we get exactly the format we want. See server-locations.txt for the exact + # format guaranteed here. + lineRe = re.compile(r"^(?P<scheme>[a-z][-a-z0-9+.]*)" + r"://" + r"(?P<host>" + r"\d+\.\d+\.\d+\.\d+" + r"|" + r"(?:[a-z0-9](?:[-a-z0-9]*[a-z0-9])?\.)*" + r"[a-z](?:[-a-z0-9]*[a-z0-9])?" + r")" + r":" + r"(?P<port>\d+)" + r"(?:" + r"\s+" + r"(?P<options>\S+(?:,\S+)*)" + r")?$") + locations = [] + lineno = 0 + seenPrimary = False + for line in locationFile: + lineno += 1 + if line.startswith("#") or line == "\n": + continue + + match = lineRe.match(line) + if not match: + raise SyntaxError(lineno) + + options = match.group("options") + if options: + options = options.split(",") + if "primary" in options: + if seenPrimary: + raise SyntaxError(lineno, "multiple primary locations") + seenPrimary = True + else: + options = [] + + locations.append(Location(match.group("scheme"), match.group("host"), + match.group("port"), options)) + + if not seenPrimary: + raise SyntaxError(lineno + 1, "missing primary location") + + return locations + + def setupPermissionsDatabase(self, profileDir, permissions): + # Open database and create table + permDB = sqlite3.connect(os.path.join(profileDir, "permissions.sqlite")) + cursor = permDB.cursor(); + + cursor.execute("PRAGMA user_version=3"); + + # SQL copied from nsPermissionManager.cpp + cursor.execute("""CREATE TABLE IF NOT EXISTS moz_hosts ( + id INTEGER PRIMARY KEY, + host TEXT, + type TEXT, + permission INTEGER, + expireType INTEGER, + expireTime INTEGER, + appId INTEGER, + isInBrowserElement INTEGER)""") + + # Insert desired permissions + for perm in permissions.keys(): + for host,allow in permissions[perm]: + cursor.execute("INSERT INTO moz_hosts values(NULL, ?, ?, ?, 0, 0, 0, 0)", + (host, perm, 1 if allow else 2)) + + # Commit and close + permDB.commit() + cursor.close() + + def setupTestApps(self, profileDir, apps): + webappJSONTemplate = Template(""""$id": { + "origin": "$origin", + "installOrigin": "$origin", + "receipt": null, + "installTime": 132333986000, + "manifestURL": "$manifestURL", + "localId": $localId, + "id": "$id", + "appStatus": $appStatus, + "csp": "$csp" +}""") + + manifestTemplate = Template("""{ + "name": "$name", + "csp": "$csp", + "description": "$description", + "launch_path": "/", + "developer": { + "name": "Mozilla", + "url": "https://mozilla.org/" + }, + "permissions": [ + ], + "locales": { + "en-US": { + "name": "$name", + "description": "$description" + } + }, + "default_locale": "en-US", + "icons": { + } +} +""") + + # Create webapps/webapps.json + webappsDir = os.path.join(profileDir, "webapps") + if not os.access(webappsDir, os.F_OK): + os.mkdir(webappsDir) + + lineRe = re.compile(r'(.*?)"(.*?)": (.*)') + + webappsJSONFilename = os.path.join(webappsDir, "webapps.json") + webappsJSON = [] + if os.access(webappsJSONFilename, os.F_OK): + # If there is an existing webapps.json file (which will be the case for + # b2g), we parse the data in the existing file before appending test + # test apps to it. + startId = 1 + webappsJSONFile = open(webappsJSONFilename, "r") + contents = webappsJSONFile.read() + + for app_content in contents.split('},'): + app = {} + # ghetto json parser needed due to lack of json/simplejson on test slaves + for line in app_content.split('\n'): + m = lineRe.match(line) + if m: + value = m.groups()[2] + # remove any trailing commas + if value[-1:] == ',': + value = value[:-1] + # set the app name from a line that looks like this: + # "name.gaiamobile.org": { + if value == '{': + app['id'] = m.groups()[1] + # parse string, None, bool and int types + elif value[0:1] == '"': + app[m.groups()[1]] = value[1:-1] + elif value == "null": + app[m.groups()[1]] = None + elif value == "true": + app[m.groups()[1]] = True + elif value == "false": + app[m.groups()[1]] = False + else: + app[m.groups()[1]] = int(value) + if app: + webappsJSON.append(app) + + webappsJSONFile.close() + + startId = 1 + for app in webappsJSON: + if app['localId'] >= startId: + startId = app['localId'] + 1 + if not app.get('csp'): + app['csp'] = '' + if not app.get('appStatus'): + app['appStatus'] = 3 + + for localId, app in enumerate(apps): + app['localId'] = localId + startId # localId must be from 1..N + if not app.get('id'): + app['id'] = app['name'] + webappsJSON.append(app) + + contents = [] + for app in webappsJSON: + contents.append(webappJSONTemplate.substitute(app)) + contents = '{\n' + ',\n'.join(contents) + '\n}\n' + + webappsJSONFile = open(webappsJSONFilename, "w") + webappsJSONFile.write(contents) + webappsJSONFile.close() + + # Create manifest file for each app. + for app in apps: + manifest = manifestTemplate.substitute(app) + + manifestDir = os.path.join(webappsDir, app['name']) + os.mkdir(manifestDir) + + manifestFile = open(os.path.join(manifestDir, "manifest.webapp"), "a") + manifestFile.write(manifest) + manifestFile.close() + + def initializeProfile(self, profileDir, + extraPrefs=None, + useServerLocations=False, + initialProfile=None, + prefsPath=_DEFAULT_PREFERENCE_FILE): + " Sets up the standard testing profile." + + extraPrefs = extraPrefs or [] + prefs = [] + # Start with a clean slate. + shutil.rmtree(profileDir, True) + + if initialProfile: + shutil.copytree(initialProfile, profileDir) + else: + os.mkdir(profileDir) + + # Set up permissions database + locations = self.readLocations() + self.setupPermissionsDatabase(profileDir, + {'allowXULXBL':[(l.host, 'noxul' not in l.options) for l in locations]}); + + f = open(prefsPath, 'r') + part = f.read() % {"server" : "%s:%s" % (self.webServer, self.httpPort)} + f.close() + prefs.append(part) + + if useServerLocations: + # We need to proxy every server but the primary one. + origins = ["'%s://%s:%s'" % (l.scheme, l.host, l.port) + for l in filter(lambda l: "primary" not in l.options, locations)] + origins = ", ".join(origins) + + pacURL = """data:text/plain, +function FindProxyForURL(url, host) +{ + var origins = [%(origins)s]; + var regex = new RegExp('^([a-z][-a-z0-9+.]*)' + + '://' + + '(?:[^/@]*@)?' + + '(.*?)' + + '(?::(\\\\\\\\d+))?/'); + var matches = regex.exec(url); + if (!matches) + return 'DIRECT'; + var isHttp = matches[1] == 'http'; + var isHttps = matches[1] == 'https'; + var isWebSocket = matches[1] == 'ws'; + var isWebSocketSSL = matches[1] == 'wss'; + if (!matches[3]) + { + if (isHttp | isWebSocket) matches[3] = '80'; + if (isHttps | isWebSocketSSL) matches[3] = '443'; + } + if (isWebSocket) + matches[1] = 'http'; + if (isWebSocketSSL) + matches[1] = 'https'; + + var origin = matches[1] + '://' + matches[2] + ':' + matches[3]; + if (origins.indexOf(origin) < 0) + return 'DIRECT'; + if (isHttp) + return 'PROXY %(remote)s:%(httpport)s'; + if (isHttps || isWebSocket || isWebSocketSSL) + return 'PROXY %(remote)s:%(sslport)s'; + return 'DIRECT'; +}""" % { "origins": origins, + "remote": self.webServer, + "httpport":self.httpPort, + "sslport": self.sslPort } + pacURL = "".join(pacURL.splitlines()) + + part = """ +user_pref("network.proxy.type", 2); +user_pref("network.proxy.autoconfig_url", "%(pacURL)s"); + +user_pref("camino.use_system_proxy_settings", false); // Camino-only, harmless to others +""" % {"pacURL": pacURL} + else: + part = 'user_pref("network.proxy.type", 0);\n' + prefs.append(part) + + for v in extraPrefs: + thispref = v.split("=", 1) + if len(thispref) < 2: + print "Error: syntax error in --setpref=" + v + sys.exit(1) + part = 'user_pref("%s", %s);\n' % (thispref[0], thispref[1]) + prefs.append(part) + + # write the preferences + prefsFile = open(profileDir + "/" + "user.js", "a") + prefsFile.write("".join(prefs)) + prefsFile.close() + + apps = [ + { + 'name': 'http_example_org', + 'csp': '', + 'origin': 'http://example.org', + 'manifestURL': 'http://example.org/manifest.webapp', + 'description': 'http://example.org App', + 'appStatus': _APP_STATUS_INSTALLED + }, + { + 'name': 'https_example_com', + 'csp': '', + 'origin': 'https://example.com', + 'manifestURL': 'https://example.com/manifest.webapp', + 'description': 'https://example.com App', + 'appStatus': _APP_STATUS_INSTALLED + }, + { + 'name': 'http_test1_example_org', + 'csp': '', + 'origin': 'http://test1.example.org', + 'manifestURL': 'http://test1.example.org/manifest.webapp', + 'description': 'http://test1.example.org App', + 'appStatus': _APP_STATUS_INSTALLED + }, + { + 'name': 'http_test1_example_org_8000', + 'csp': '', + 'origin': 'http://test1.example.org:8000', + 'manifestURL': 'http://test1.example.org:8000/manifest.webapp', + 'description': 'http://test1.example.org:8000 App', + 'appStatus': _APP_STATUS_INSTALLED + }, + { + 'name': 'http_sub1_test1_example_org', + 'csp': '', + 'origin': 'http://sub1.test1.example.org', + 'manifestURL': 'http://sub1.test1.example.org/manifest.webapp', + 'description': 'http://sub1.test1.example.org App', + 'appStatus': _APP_STATUS_INSTALLED + }, + { + 'name': 'https_example_com_privileged', + 'csp': '', + 'origin': 'https://example.com', + 'manifestURL': 'https://example.com/manifest_priv.webapp', + 'description': 'https://example.com Privileged App', + 'appStatus': _APP_STATUS_PRIVILEGED + }, + { + 'name': 'https_example_com_certified', + 'csp': '', + 'origin': 'https://example.com', + 'manifestURL': 'https://example.com/manifest_cert.webapp', + 'description': 'https://example.com Certified App', + 'appStatus': _APP_STATUS_CERTIFIED + }, + { + 'name': 'https_example_csp_certified', + 'csp': "default-src *; script-src 'self'; object-src 'none'; style-src 'self' 'unsafe-inline'", + 'origin': 'https://example.com', + 'manifestURL': 'https://example.com/manifest_csp_cert.webapp', + 'description': 'https://example.com Certified App with manifest policy', + 'appStatus': _APP_STATUS_CERTIFIED + }, + { + 'name': 'https_example_csp_installed', + 'csp': "default-src *; script-src 'self'; object-src 'none'; style-src 'self' 'unsafe-inline'", + 'origin': 'https://example.com', + 'manifestURL': 'https://example.com/manifest_csp_inst.webapp', + 'description': 'https://example.com Installed App with manifest policy', + 'appStatus': _APP_STATUS_INSTALLED + }, + { + 'name': 'https_example_csp_privileged', + 'csp': "default-src *; script-src 'self'; object-src 'none'; style-src 'self' 'unsafe-inline'", + 'origin': 'https://example.com', + 'manifestURL': 'https://example.com/manifest_csp_priv.webapp', + 'description': 'https://example.com Privileged App with manifest policy', + 'appStatus': _APP_STATUS_PRIVILEGED + }, + { + 'name': 'https_a_domain_certified', + 'csp': "", + 'origin': 'https://acertified.com', + 'manifestURL': 'https://acertified.com/manifest.webapp', + 'description': 'https://acertified.com Certified App', + 'appStatus': _APP_STATUS_CERTIFIED + }, + { + 'name': 'https_a_domain_privileged', + 'csp': "", + 'origin': 'https://aprivileged.com', + 'manifestURL': 'https://aprivileged.com/manifest.webapp', + 'description': 'https://aprivileged.com Privileged App ', + 'appStatus': _APP_STATUS_PRIVILEGED + }, + ]; + self.setupTestApps(profileDir, apps) + + def addCommonOptions(self, parser): + "Adds command-line options which are common to mochitest and reftest." + + parser.add_option("--setpref", + action = "append", type = "string", + default = [], + dest = "extraPrefs", metavar = "PREF=VALUE", + help = "defines an extra user preference") + + def fillCertificateDB(self, profileDir, certPath, utilityPath, xrePath): + pwfilePath = os.path.join(profileDir, ".crtdbpw") + + pwfile = open(pwfilePath, "w") + pwfile.write("\n") + pwfile.close() + + # Create head of the ssltunnel configuration file + sslTunnelConfigPath = os.path.join(profileDir, "ssltunnel.cfg") + sslTunnelConfig = open(sslTunnelConfigPath, "w") + + sslTunnelConfig.write("httpproxy:1\n") + sslTunnelConfig.write("certdbdir:%s\n" % certPath) + sslTunnelConfig.write("forward:127.0.0.1:%s\n" % self.httpPort) + sslTunnelConfig.write("websocketserver:%s:%s\n" % (self.webServer, self.webSocketPort)) + sslTunnelConfig.write("listen:*:%s:pgo server certificate\n" % self.sslPort) + + # Configure automatic certificate and bind custom certificates, client authentication + locations = self.readLocations() + locations.pop(0) + for loc in locations: + if loc.scheme == "https" and "nocert" not in loc.options: + customCertRE = re.compile("^cert=(?P<nickname>[0-9a-zA-Z_ ]+)") + clientAuthRE = re.compile("^clientauth=(?P<clientauth>[a-z]+)") + redirRE = re.compile("^redir=(?P<redirhost>[0-9a-zA-Z_ .]+)") + for option in loc.options: + match = customCertRE.match(option) + if match: + customcert = match.group("nickname"); + sslTunnelConfig.write("listen:%s:%s:%s:%s\n" % + (loc.host, loc.port, self.sslPort, customcert)) + + match = clientAuthRE.match(option) + if match: + clientauth = match.group("clientauth"); + sslTunnelConfig.write("clientauth:%s:%s:%s:%s\n" % + (loc.host, loc.port, self.sslPort, clientauth)) + + match = redirRE.match(option) + if match: + redirhost = match.group("redirhost") + sslTunnelConfig.write("redirhost:%s:%s:%s:%s\n" % + (loc.host, loc.port, self.sslPort, redirhost)) + + sslTunnelConfig.close() + + # Pre-create the certification database for the profile + env = self.environment(xrePath = xrePath) + certutil = os.path.join(utilityPath, "certutil" + self.BIN_SUFFIX) + pk12util = os.path.join(utilityPath, "pk12util" + self.BIN_SUFFIX) + + status = self.Process([certutil, "-N", "-d", profileDir, "-f", pwfilePath], env = env).wait() + if status != 0: + return status + + # Walk the cert directory and add custom CAs and client certs + files = os.listdir(certPath) + for item in files: + root, ext = os.path.splitext(item) + if ext == ".ca": + trustBits = "CT,," + if root.endswith("-object"): + trustBits = "CT,,CT" + self.Process([certutil, "-A", "-i", os.path.join(certPath, item), + "-d", profileDir, "-f", pwfilePath, "-n", root, "-t", trustBits], + env = env).wait() + if ext == ".client": + self.Process([pk12util, "-i", os.path.join(certPath, item), "-w", + pwfilePath, "-d", profileDir], + env = env).wait() + + os.unlink(pwfilePath) + return 0 + + def environment(self, env = None, xrePath = None, crashreporter = True): + if xrePath == None: + xrePath = self.DIST_BIN + if env == None: + env = dict(os.environ) + + ldLibraryPath = os.path.abspath(os.path.join(SCRIPT_DIR, xrePath)) + if self.UNIXISH or self.IS_MAC: + envVar = "LD_LIBRARY_PATH" + if self.IS_MAC: + envVar = "DYLD_LIBRARY_PATH" + else: # unixish + env['MOZILLA_FIVE_HOME'] = xrePath + if envVar in env: + ldLibraryPath = ldLibraryPath + ":" + env[envVar] + env[envVar] = ldLibraryPath + elif self.IS_WIN32: + env["PATH"] = env["PATH"] + ";" + ldLibraryPath + + if crashreporter: + env['MOZ_CRASHREPORTER_NO_REPORT'] = '1' + env['MOZ_CRASHREPORTER'] = '1' + else: + env['MOZ_CRASHREPORTER_DISABLE'] = '1' + + env['GNOME_DISABLE_CRASH_DIALOG'] = '1' + env['XRE_NO_WINDOWS_CRASH_DIALOG'] = '1' + env['NS_TRACE_MALLOC_DISABLE_STACKS'] = '1' + + # Additional temporary logging while we try to debug some intermittent + # WebRTC conditions. This is necessary to troubleshoot bugs 841496, + # 841150, and 839677 (at least) + # Also (temporary) bug 870002 (mediastreamgraph) + if 'NSPR_LOG_MODULES' not in env: + env['NSPR_LOG_MODULES'] = 'signaling:5,mtransport:3' + env['R_LOG_LEVEL'] = '5' + env['R_LOG_DESTINATION'] = 'stderr' + env['R_LOG_VERBOSE'] = '1' + + # ASan specific environment stuff + if self.IS_ASAN and (self.IS_LINUX or self.IS_MAC): + try: + totalMemory = int(os.popen("free").readlines()[1].split()[1]) + + # Only 2 GB RAM or less available? Use custom ASan options to reduce + # the amount of resources required to do the tests. Standard options + # will otherwise lead to OOM conditions on the current test slaves. + if totalMemory <= 1024 * 1024 * 2: + self.log.info("INFO | automation.py | ASan running in low-memory configuration") + env["ASAN_OPTIONS"] = "quarantine_size=50331648:redzone=64" + except OSError,err: + self.log.info("Failed determine available memory, disabling ASan low-memory configuration: %s", err.strerror) + except: + self.log.info("Failed determine available memory, disabling ASan low-memory configuration") + + return env + + if IS_WIN32: + PeekNamedPipe = ctypes.windll.kernel32.PeekNamedPipe + GetLastError = ctypes.windll.kernel32.GetLastError + + def readWithTimeout(self, f, timeout): + """Try to read a line of output from the file object |f|. + |f| must be a pipe, like the |stdout| member of a subprocess.Popen + object created with stdout=PIPE. If no output + is received within |timeout| seconds, return a blank line. + Returns a tuple (line, did_timeout), where |did_timeout| is True + if the read timed out, and False otherwise.""" + if timeout is None: + # shortcut to allow callers to pass in "None" for no timeout. + return (f.readline(), False) + x = msvcrt.get_osfhandle(f.fileno()) + l = ctypes.c_long() + done = time.time() + timeout + while time.time() < done: + if self.PeekNamedPipe(x, None, 0, None, ctypes.byref(l), None) == 0: + err = self.GetLastError() + if err == 38 or err == 109: # ERROR_HANDLE_EOF || ERROR_BROKEN_PIPE + return ('', False) + else: + self.log.error("readWithTimeout got error: %d", err) + if l.value > 0: + # we're assuming that the output is line-buffered, + # which is not unreasonable + return (f.readline(), False) + time.sleep(0.01) + return ('', True) + + def isPidAlive(self, pid): + STILL_ACTIVE = 259 + PROCESS_QUERY_LIMITED_INFORMATION = 0x1000 + pHandle = ctypes.windll.kernel32.OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, 0, pid) + if not pHandle: + return False + pExitCode = ctypes.wintypes.DWORD() + ctypes.windll.kernel32.GetExitCodeProcess(pHandle, ctypes.byref(pExitCode)) + ctypes.windll.kernel32.CloseHandle(pHandle) + return pExitCode.value == STILL_ACTIVE + + def killPid(self, pid): + PROCESS_TERMINATE = 0x0001 + pHandle = ctypes.windll.kernel32.OpenProcess(PROCESS_TERMINATE, 0, pid) + if not pHandle: + return + ctypes.windll.kernel32.TerminateProcess(pHandle, 1) + ctypes.windll.kernel32.CloseHandle(pHandle) + + else: + + def readWithTimeout(self, f, timeout): + """Try to read a line of output from the file object |f|. If no output + is received within |timeout| seconds, return a blank line. + Returns a tuple (line, did_timeout), where |did_timeout| is True + if the read timed out, and False otherwise.""" + (r, w, e) = select.select([f], [], [], timeout) + if len(r) == 0: + return ('', True) + return (f.readline(), False) + + def isPidAlive(self, pid): + try: + # kill(pid, 0) checks for a valid PID without actually sending a signal + # The method throws OSError if the PID is invalid, which we catch below. + os.kill(pid, 0) + + # Wait on it to see if it's a zombie. This can throw OSError.ECHILD if + # the process terminates before we get to this point. + wpid, wstatus = os.waitpid(pid, os.WNOHANG) + return wpid == 0 + except OSError, err: + # Catch the errors we might expect from os.kill/os.waitpid, + # and re-raise any others + if err.errno == errno.ESRCH or err.errno == errno.ECHILD: + return False + raise + + def killPid(self, pid): + os.kill(pid, signal.SIGKILL) + + def dumpScreen(self, utilityPath): + if self.haveDumpedScreen: + self.log.info("Not taking screenshot here: see the one that was previously logged") + return + + self.haveDumpedScreen = True; + + # Need to figure out what tool and whether it write to a file or stdout + if self.UNIXISH: + utility = [os.path.join(utilityPath, "screentopng")] + imgoutput = 'stdout' + elif self.IS_MAC: + utility = ['/usr/sbin/screencapture', '-C', '-x', '-t', 'png'] + imgoutput = 'file' + elif self.IS_WIN32: + utility = [os.path.join(utilityPath, "screenshot.exe")] + imgoutput = 'file' + + # Run the capture correctly for the type of capture + try: + if imgoutput == 'file': + tmpfd, imgfilename = tempfile.mkstemp(prefix='mozilla-test-fail_') + os.close(tmpfd) + dumper = self.Process(utility + [imgfilename]) + elif imgoutput == 'stdout': + dumper = self.Process(utility, bufsize=-1, + stdout=subprocess.PIPE, close_fds=True) + except OSError, err: + self.log.info("Failed to start %s for screenshot: %s", + utility[0], err.strerror) + return + + # Check whether the capture utility ran successfully + dumper_out, dumper_err = dumper.communicate() + if dumper.returncode != 0: + self.log.info("%s exited with code %d", utility, dumper.returncode) + return + + try: + if imgoutput == 'stdout': + image = dumper_out + elif imgoutput == 'file': + with open(imgfilename, 'rb') as imgfile: + image = imgfile.read() + except IOError, err: + self.log.info("Failed to read image from %s", imgoutput) + + import base64 + encoded = base64.b64encode(image) + self.log.info("SCREENSHOT: data:image/png;base64,%s", encoded) + + def killAndGetStack(self, processPID, utilityPath, debuggerInfo): + """Kill the process, preferrably in a way that gets us a stack trace. + Also attempts to obtain a screenshot before killing the process.""" + if not debuggerInfo: + self.dumpScreen(utilityPath) + self.killAndGetStackNoScreenshot(processPID, utilityPath, debuggerInfo) + + def killAndGetStackNoScreenshot(self, processPID, utilityPath, debuggerInfo): + """Kill the process, preferrably in a way that gets us a stack trace.""" + if self.CRASHREPORTER and not debuggerInfo: + if self.UNIXISH: + # ABRT will get picked up by Breakpad's signal handler + os.kill(processPID, signal.SIGABRT) + return + elif self.IS_WIN32: + # We should have a "crashinject" program in our utility path + crashinject = os.path.normpath(os.path.join(utilityPath, "crashinject.exe")) + if os.path.exists(crashinject) and subprocess.Popen([crashinject, str(processPID)]).wait() == 0: + return + #TODO: kill the process such that it triggers Breakpad on OS X (bug 525296) + self.log.info("Can't trigger Breakpad, just killing process") + self.killPid(processPID) + + def waitForFinish(self, proc, utilityPath, timeout, maxTime, startTime, debuggerInfo, symbolsPath): + """ Look for timeout or crashes and return the status after the process terminates """ + stackFixerProcess = None + stackFixerFunction = None + didTimeout = False + hitMaxTime = False + if proc.stdout is None: + self.log.info("TEST-INFO: Not logging stdout or stderr due to debugger connection") + else: + logsource = proc.stdout + + if self.IS_DEBUG_BUILD and symbolsPath and os.path.exists(symbolsPath): + # Run each line through a function in fix_stack_using_bpsyms.py (uses breakpad symbol files) + # This method is preferred for Tinderbox builds, since native symbols may have been stripped. + sys.path.insert(0, utilityPath) + import fix_stack_using_bpsyms as stackFixerModule + stackFixerFunction = lambda line: stackFixerModule.fixSymbols(line, symbolsPath) + del sys.path[0] + elif self.IS_DEBUG_BUILD and self.IS_MAC and False: + # Run each line through a function in fix_macosx_stack.py (uses atos) + sys.path.insert(0, utilityPath) + import fix_macosx_stack as stackFixerModule + stackFixerFunction = lambda line: stackFixerModule.fixSymbols(line) + del sys.path[0] + elif self.IS_DEBUG_BUILD and self.IS_LINUX: + # Run logsource through fix-linux-stack.pl (uses addr2line) + # This method is preferred for developer machines, so we don't have to run "make buildsymbols". + stackFixerProcess = self.Process([self.PERL, os.path.join(utilityPath, "fix-linux-stack.pl")], + stdin=logsource, + stdout=subprocess.PIPE) + logsource = stackFixerProcess.stdout + + (line, didTimeout) = self.readWithTimeout(logsource, timeout) + while line != "" and not didTimeout: + if stackFixerFunction: + line = stackFixerFunction(line) + self.log.info(line.rstrip().decode("UTF-8", "ignore")) + if "TEST-START" in line and "|" in line: + self.lastTestSeen = line.split("|")[1].strip() + if not debuggerInfo and "TEST-UNEXPECTED-FAIL" in line and "Test timed out" in line: + self.dumpScreen(utilityPath) + + (line, didTimeout) = self.readWithTimeout(logsource, timeout) + if not hitMaxTime and maxTime and datetime.now() - startTime > timedelta(seconds = maxTime): + # Kill the application, but continue reading from stack fixer so as not to deadlock on stackFixerProcess.wait(). + hitMaxTime = True + self.log.info("TEST-UNEXPECTED-FAIL | %s | application ran for longer than allowed maximum time of %d seconds", self.lastTestSeen, int(maxTime)) + self.killAndGetStack(proc.pid, utilityPath, debuggerInfo) + if didTimeout: + self.log.info("TEST-UNEXPECTED-FAIL | %s | application timed out after %d seconds with no output", self.lastTestSeen, int(timeout)) + self.killAndGetStack(proc.pid, utilityPath, debuggerInfo) + + status = proc.wait() + if status == 0: + self.lastTestSeen = "Main app process exited normally" + if status != 0 and not didTimeout and not hitMaxTime: + self.log.info("TEST-UNEXPECTED-FAIL | %s | Exited with code %d during test run", self.lastTestSeen, status) + if stackFixerProcess is not None: + fixerStatus = stackFixerProcess.wait() + if fixerStatus != 0 and not didTimeout and not hitMaxTime: + self.log.info("TEST-UNEXPECTED-FAIL | automation.py | Stack fixer process exited with code %d during test run", fixerStatus) + return status + + def buildCommandLine(self, app, debuggerInfo, profileDir, testURL, extraArgs): + """ build the application command line """ + + cmd = os.path.abspath(app) + if self.IS_MAC and not self.IS_CAMINO and os.path.exists(cmd + "-bin"): + # Prefer 'app-bin' in case 'app' is a shell script. + # We can remove this hack once bug 673899 etc are fixed. + cmd += "-bin" + + args = [] + + if debuggerInfo: + args.extend(debuggerInfo["args"]) + args.append(cmd) + cmd = os.path.abspath(debuggerInfo["path"]) + + if self.IS_MAC: + args.append("-foreground") + + if self.IS_CYGWIN: + profileDirectory = commands.getoutput("cygpath -w \"" + profileDir + "/\"") + else: + profileDirectory = profileDir + "/" + + args.extend(("-no-remote", "-profile", profileDirectory)) + if testURL is not None: + if self.IS_CAMINO: + args.extend(("-url", testURL)) + else: + args.append((testURL)) + args.extend(extraArgs) + return cmd, args + + def checkForZombies(self, processLog, utilityPath, debuggerInfo): + """ Look for hung processes """ + if not os.path.exists(processLog): + self.log.info('Automation Error: PID log not found: %s', processLog) + # Whilst no hung process was found, the run should still display as a failure + return True + + foundZombie = False + self.log.info('INFO | zombiecheck | Reading PID log: %s', processLog) + processList = [] + pidRE = re.compile(r'launched child process (\d+)$') + processLogFD = open(processLog) + for line in processLogFD: + self.log.info(line.rstrip()) + m = pidRE.search(line) + if m: + processList.append(int(m.group(1))) + processLogFD.close() + + for processPID in processList: + self.log.info("INFO | zombiecheck | Checking for orphan process with PID: %d", processPID) + if self.isPidAlive(processPID): + foundZombie = True + self.log.info("TEST-UNEXPECTED-FAIL | zombiecheck | child process %d still alive after shutdown", processPID) + self.killAndGetStack(processPID, utilityPath, debuggerInfo) + return foundZombie + + def checkForCrashes(self, minidumpDir, symbolsPath): + return mozcrash.check_for_crashes(minidumpDir, symbolsPath, test_name=self.lastTestSeen) + + def runApp(self, testURL, env, app, profileDir, extraArgs, + runSSLTunnel = False, utilityPath = None, + xrePath = None, certPath = None, + debuggerInfo = None, symbolsPath = None, + timeout = -1, maxTime = None, onLaunch = None): + """ + Run the app, log the duration it took to execute, return the status code. + Kills the app if it runs for longer than |maxTime| seconds, or outputs nothing for |timeout| seconds. + """ + + if utilityPath == None: + utilityPath = self.DIST_BIN + if xrePath == None: + xrePath = self.DIST_BIN + if certPath == None: + certPath = self.CERTS_SRC_DIR + if timeout == -1: + timeout = self.DEFAULT_TIMEOUT + + # copy env so we don't munge the caller's environment + env = dict(env); + env["NO_EM_RESTART"] = "1" + tmpfd, processLog = tempfile.mkstemp(suffix='pidlog') + os.close(tmpfd) + env["MOZ_PROCESS_LOG"] = processLog + + if self.IS_TEST_BUILD and runSSLTunnel: + # create certificate database for the profile + certificateStatus = self.fillCertificateDB(profileDir, certPath, utilityPath, xrePath) + if certificateStatus != 0: + self.log.info("TEST-UNEXPECTED-FAIL | automation.py | Certificate integration failed") + return certificateStatus + + # start ssltunnel to provide https:// URLs capability + ssltunnel = os.path.join(utilityPath, "ssltunnel" + self.BIN_SUFFIX) + ssltunnelProcess = self.Process([ssltunnel, + os.path.join(profileDir, "ssltunnel.cfg")], + env = self.environment(xrePath = xrePath)) + self.log.info("INFO | automation.py | SSL tunnel pid: %d", ssltunnelProcess.pid) + + cmd, args = self.buildCommandLine(app, debuggerInfo, profileDir, testURL, extraArgs) + startTime = datetime.now() + + if debuggerInfo and debuggerInfo["interactive"]: + # If an interactive debugger is attached, don't redirect output, + # don't use timeouts, and don't capture ctrl-c. + timeout = None + maxTime = None + outputPipe = None + signal.signal(signal.SIGINT, lambda sigid, frame: None) + else: + outputPipe = subprocess.PIPE + + self.lastTestSeen = "automation.py" + proc = self.Process([cmd] + args, + env = self.environment(env, xrePath = xrePath, + crashreporter = not debuggerInfo), + stdout = outputPipe, + stderr = subprocess.STDOUT) + self.log.info("INFO | automation.py | Application pid: %d", proc.pid) + + if onLaunch is not None: + # Allow callers to specify an onLaunch callback to be fired after the + # app is launched. + onLaunch() + + status = self.waitForFinish(proc, utilityPath, timeout, maxTime, startTime, debuggerInfo, symbolsPath) + self.log.info("INFO | automation.py | Application ran for: %s", str(datetime.now() - startTime)) + + # Do a final check for zombie child processes. + zombieProcesses = self.checkForZombies(processLog, utilityPath, debuggerInfo) + + crashed = self.checkForCrashes(os.path.join(profileDir, "minidumps"), symbolsPath) + + if crashed or zombieProcesses: + status = 1 + + if os.path.exists(processLog): + os.unlink(processLog) + + if self.IS_TEST_BUILD and runSSLTunnel: + ssltunnelProcess.kill() + + return status + + def getExtensionIDFromRDF(self, rdfSource): + """ + Retrieves the extension id from an install.rdf file (or string). + """ + from xml.dom.minidom import parse, parseString, Node + + if isinstance(rdfSource, file): + document = parse(rdfSource) + else: + document = parseString(rdfSource) + + # Find the <em:id> element. There can be multiple <em:id> tags + # within <em:targetApplication> tags, so we have to check this way. + for rdfChild in document.documentElement.childNodes: + if rdfChild.nodeType == Node.ELEMENT_NODE and rdfChild.tagName == "Description": + for descChild in rdfChild.childNodes: + if descChild.nodeType == Node.ELEMENT_NODE and descChild.tagName == "em:id": + return descChild.childNodes[0].data + + return None + + def installExtension(self, extensionSource, profileDir, extensionID = None): + """ + Copies an extension into the extensions directory of the given profile. + extensionSource - the source location of the extension files. This can be either + a directory or a path to an xpi file. + profileDir - the profile directory we are copying into. We will create the + "extensions" directory there if it doesn't exist. + extensionID - the id of the extension to be used as the containing directory for the + extension, if extensionSource is a directory, i.e. + this is the name of the folder in the <profileDir>/extensions/<extensionID> + """ + if not os.path.isdir(profileDir): + self.log.info("INFO | automation.py | Cannot install extension, invalid profileDir at: %s", profileDir) + return + + installRDFFilename = "install.rdf" + + extensionsRootDir = os.path.join(profileDir, "extensions", "staged") + if not os.path.isdir(extensionsRootDir): + os.makedirs(extensionsRootDir) + + if os.path.isfile(extensionSource): + reader = automationutils.ZipFileReader(extensionSource) + + for filename in reader.namelist(): + # Sanity check the zip file. + if os.path.isabs(filename): + self.log.info("INFO | automation.py | Cannot install extension, bad files in xpi") + return + + # We may need to dig the extensionID out of the zip file... + if extensionID is None and filename == installRDFFilename: + extensionID = self.getExtensionIDFromRDF(reader.read(filename)) + + # We must know the extensionID now. + if extensionID is None: + self.log.info("INFO | automation.py | Cannot install extension, missing extensionID") + return + + # Make the extension directory. + extensionDir = os.path.join(extensionsRootDir, extensionID) + os.mkdir(extensionDir) + + # Extract all files. + reader.extractall(extensionDir) + + elif os.path.isdir(extensionSource): + if extensionID is None: + filename = os.path.join(extensionSource, installRDFFilename) + if os.path.isfile(filename): + with open(filename, "r") as installRDF: + extensionID = self.getExtensionIDFromRDF(installRDF) + + if extensionID is None: + self.log.info("INFO | automation.py | Cannot install extension, missing extensionID") + return + + # Copy extension tree into its own directory. + # "destination directory must not already exist". + shutil.copytree(extensionSource, os.path.join(extensionsRootDir, extensionID)) + + else: + self.log.info("INFO | automation.py | Cannot install extension, invalid extensionSource at: %s", extensionSource) + + def elf_arm(self, filename): + data = open(filename, 'rb').read(20) + return data[:4] == "\x7fELF" and ord(data[18]) == 40 # EM_ARM + diff --git a/build/automationutils.py b/build/automationutils.py new file mode 100644 index 000000000..cd0368ac6 --- /dev/null +++ b/build/automationutils.py @@ -0,0 +1,347 @@ +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +from __future__ import with_statement +import glob, logging, os, platform, shutil, subprocess, sys, tempfile, urllib2, zipfile +import re +from urlparse import urlparse + +__all__ = [ + "ZipFileReader", + "addCommonOptions", + "dumpLeakLog", + "isURL", + "processLeakLog", + "getDebuggerInfo", + "DEBUGGER_INFO", + "replaceBackSlashes", + "wrapCommand", + ] + +# Map of debugging programs to information about them, like default arguments +# and whether or not they are interactive. +DEBUGGER_INFO = { + # gdb requires that you supply the '--args' flag in order to pass arguments + # after the executable name to the executable. + "gdb": { + "interactive": True, + "args": "-q --args" + }, + + "cgdb": { + "interactive": True, + "args": "-q --args" + }, + "cgdb": { + "interactive": True, + "args": "-q --args" + }, + + "lldb": { + "interactive": True, + "args": "--" + }, + + # valgrind doesn't explain much about leaks unless you set the + # '--leak-check=full' flag. + "valgrind": { + "interactive": False, + "args": "--leak-check=full" + } +} + +class ZipFileReader(object): + """ + Class to read zip files in Python 2.5 and later. Limited to only what we + actually use. + """ + + def __init__(self, filename): + self._zipfile = zipfile.ZipFile(filename, "r") + + def __del__(self): + self._zipfile.close() + + def _getnormalizedpath(self, path): + """ + Gets a normalized path from 'path' (or the current working directory if + 'path' is None). Also asserts that the path exists. + """ + if path is None: + path = os.curdir + path = os.path.normpath(os.path.expanduser(path)) + assert os.path.isdir(path) + return path + + def _extractname(self, name, path): + """ + Extracts a file with the given name from the zip file to the given path. + Also creates any directories needed along the way. + """ + filename = os.path.normpath(os.path.join(path, name)) + if name.endswith("/"): + os.makedirs(filename) + else: + path = os.path.split(filename)[0] + if not os.path.isdir(path): + os.makedirs(path) + with open(filename, "wb") as dest: + dest.write(self._zipfile.read(name)) + + def namelist(self): + return self._zipfile.namelist() + + def read(self, name): + return self._zipfile.read(name) + + def extract(self, name, path = None): + if hasattr(self._zipfile, "extract"): + return self._zipfile.extract(name, path) + + # This will throw if name is not part of the zip file. + self._zipfile.getinfo(name) + + self._extractname(name, self._getnormalizedpath(path)) + + def extractall(self, path = None): + if hasattr(self._zipfile, "extractall"): + return self._zipfile.extractall(path) + + path = self._getnormalizedpath(path) + + for name in self._zipfile.namelist(): + self._extractname(name, path) + +log = logging.getLogger() + +def isURL(thing): + """Return True if |thing| looks like a URL.""" + # We want to download URLs like http://... but not Windows paths like c:\... + return len(urlparse(thing).scheme) >= 2 + +def addCommonOptions(parser, defaults={}): + parser.add_option("--xre-path", + action = "store", type = "string", dest = "xrePath", + # individual scripts will set a sane default + default = None, + help = "absolute path to directory containing XRE (probably xulrunner)") + if 'SYMBOLS_PATH' not in defaults: + defaults['SYMBOLS_PATH'] = None + parser.add_option("--symbols-path", + action = "store", type = "string", dest = "symbolsPath", + default = defaults['SYMBOLS_PATH'], + help = "absolute path to directory containing breakpad symbols, or the URL of a zip file containing symbols") + parser.add_option("--debugger", + action = "store", dest = "debugger", + help = "use the given debugger to launch the application") + parser.add_option("--debugger-args", + action = "store", dest = "debuggerArgs", + help = "pass the given args to the debugger _before_ " + "the application on the command line") + parser.add_option("--debugger-interactive", + action = "store_true", dest = "debuggerInteractive", + help = "prevents the test harness from redirecting " + "stdout and stderr for interactive debuggers") + +def getFullPath(directory, path): + "Get an absolute path relative to 'directory'." + return os.path.normpath(os.path.join(directory, os.path.expanduser(path))) + +def searchPath(directory, path): + "Go one step beyond getFullPath and try the various folders in PATH" + # Try looking in the current working directory first. + newpath = getFullPath(directory, path) + if os.path.isfile(newpath): + return newpath + + # At this point we have to fail if a directory was given (to prevent cases + # like './gdb' from matching '/usr/bin/./gdb'). + if not os.path.dirname(path): + for dir in os.environ['PATH'].split(os.pathsep): + newpath = os.path.join(dir, path) + if os.path.isfile(newpath): + return newpath + return None + +def getDebuggerInfo(directory, debugger, debuggerArgs, debuggerInteractive = False): + + debuggerInfo = None + + if debugger: + debuggerPath = searchPath(directory, debugger) + if not debuggerPath: + print "Error: Path %s doesn't exist." % debugger + sys.exit(1) + + debuggerName = os.path.basename(debuggerPath).lower() + + def getDebuggerInfo(type, default): + if debuggerName in DEBUGGER_INFO and type in DEBUGGER_INFO[debuggerName]: + return DEBUGGER_INFO[debuggerName][type] + return default + + debuggerInfo = { + "path": debuggerPath, + "interactive" : getDebuggerInfo("interactive", False), + "args": getDebuggerInfo("args", "").split() + } + + if debuggerArgs: + debuggerInfo["args"] = debuggerArgs.split() + if debuggerInteractive: + debuggerInfo["interactive"] = debuggerInteractive + + return debuggerInfo + + +def dumpLeakLog(leakLogFile, filter = False): + """Process the leak log, without parsing it. + + Use this function if you want the raw log only. + Use it preferably with the |XPCOM_MEM_LEAK_LOG| environment variable. + """ + + # Don't warn (nor "info") if the log file is not there. + if not os.path.exists(leakLogFile): + return + + with open(leakLogFile, "r") as leaks: + leakReport = leaks.read() + + # Only |XPCOM_MEM_LEAK_LOG| reports can be actually filtered out. + # Only check whether an actual leak was reported. + if filter and not "0 TOTAL " in leakReport: + return + + # Simply copy the log. + log.info(leakReport.rstrip("\n")) + +def processSingleLeakFile(leakLogFileName, processType, leakThreshold): + """Process a single leak log. + """ + + # Per-Inst Leaked Total Rem ... + # 0 TOTAL 17 192 419115886 2 ... + # 833 nsTimerImpl 60 120 24726 2 ... + lineRe = re.compile(r"^\s*\d+\s+(?P<name>\S+)\s+" + r"(?P<size>-?\d+)\s+(?P<bytesLeaked>-?\d+)\s+" + r"-?\d+\s+(?P<numLeaked>-?\d+)") + + processString = "" + if processType: + # eg 'plugin' + processString = " %s process:" % processType + + crashedOnPurpose = False + totalBytesLeaked = None + leakAnalysis = [] + leakedObjectNames = [] + with open(leakLogFileName, "r") as leaks: + for line in leaks: + if line.find("purposefully crash") > -1: + crashedOnPurpose = True + matches = lineRe.match(line) + if not matches: + # eg: the leak table header row + log.info(line.rstrip()) + continue + name = matches.group("name") + size = int(matches.group("size")) + bytesLeaked = int(matches.group("bytesLeaked")) + numLeaked = int(matches.group("numLeaked")) + # Output the raw line from the leak log table if it is the TOTAL row, + # or is for an object row that has been leaked. + if numLeaked != 0 or name == "TOTAL": + log.info(line.rstrip()) + # Analyse the leak log, but output later or it will interrupt the leak table + if name == "TOTAL": + totalBytesLeaked = bytesLeaked + if size < 0 or bytesLeaked < 0 or numLeaked < 0: + leakAnalysis.append("TEST-UNEXPECTED-FAIL | leakcheck |%s negative leaks caught!" + % processString) + continue + if name != "TOTAL" and numLeaked != 0: + leakedObjectNames.append(name) + leakAnalysis.append("TEST-INFO | leakcheck |%s leaked %d %s (%s bytes)" + % (processString, numLeaked, name, bytesLeaked)) + log.info('\n'.join(leakAnalysis)) + + if totalBytesLeaked is None: + # We didn't see a line with name 'TOTAL' + if crashedOnPurpose: + log.info("TEST-INFO | leakcheck |%s deliberate crash and thus no leak log" + % processString) + else: + # TODO: This should be a TEST-UNEXPECTED-FAIL, but was changed to a warning + # due to too many intermittent failures (see bug 831223). + log.info("WARNING | leakcheck |%s missing output line for total leaks!" + % processString) + return + + if totalBytesLeaked == 0: + log.info("TEST-PASS | leakcheck |%s no leaks detected!" % processString) + return + + # totalBytesLeaked was seen and is non-zero. + if totalBytesLeaked > leakThreshold: + # Fail the run if we're over the threshold (which defaults to 0) + prefix = "TEST-UNEXPECTED-FAIL" + else: + prefix = "WARNING" + # Create a comma delimited string of the first N leaked objects found, + # to aid with bug summary matching in TBPL. Note: The order of the objects + # had no significance (they're sorted alphabetically). + maxSummaryObjects = 5 + leakedObjectSummary = ', '.join(leakedObjectNames[:maxSummaryObjects]) + if len(leakedObjectNames) > maxSummaryObjects: + leakedObjectSummary += ', ...' + log.info("%s | leakcheck |%s %d bytes leaked (%s)" + % (prefix, processString, totalBytesLeaked, leakedObjectSummary)) + +def processLeakLog(leakLogFile, leakThreshold = 0): + """Process the leak log, including separate leak logs created + by child processes. + + Use this function if you want an additional PASS/FAIL summary. + It must be used with the |XPCOM_MEM_BLOAT_LOG| environment variable. + """ + + if not os.path.exists(leakLogFile): + log.info("WARNING | leakcheck | refcount logging is off, so leaks can't be detected!") + return + + if leakThreshold != 0: + log.info("TEST-INFO | leakcheck | threshold set at %d bytes" % leakThreshold) + + (leakLogFileDir, leakFileBase) = os.path.split(leakLogFile) + fileNameRegExp = re.compile(r".*?_([a-z]*)_pid\d*$") + if leakFileBase[-4:] == ".log": + leakFileBase = leakFileBase[:-4] + fileNameRegExp = re.compile(r".*?_([a-z]*)_pid\d*.log$") + + for fileName in os.listdir(leakLogFileDir): + if fileName.find(leakFileBase) != -1: + thisFile = os.path.join(leakLogFileDir, fileName) + processType = None + m = fileNameRegExp.search(fileName) + if m: + processType = m.group(1) + processSingleLeakFile(thisFile, processType, leakThreshold) + +def replaceBackSlashes(input): + return input.replace('\\', '/') + +def wrapCommand(cmd): + """ + If running on OS X 10.5 or older, wrap |cmd| so that it will + be executed as an i386 binary, in case it's a 32-bit/64-bit universal + binary. + """ + if platform.system() == "Darwin" and \ + hasattr(platform, 'mac_ver') and \ + platform.mac_ver()[0][:4] < '10.6': + return ["arch", "-arch", "i386"] + cmd + # otherwise just execute the command normally + return cmd diff --git a/build/binary-location.mk b/build/binary-location.mk new file mode 100644 index 000000000..7713e1a0c --- /dev/null +++ b/build/binary-location.mk @@ -0,0 +1,23 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +# finds the location of the browser and puts it in the variable $(browser_path) + +ifneq (,$(filter OS2 WINNT,$(OS_ARCH))) +PROGRAM = $(MOZ_APP_NAME)$(BIN_SUFFIX) +else +PROGRAM = $(MOZ_APP_NAME)-bin$(BIN_SUFFIX) +endif + +TARGET_DIST = $(TARGET_DEPTH)/dist + +ifeq ($(MOZ_BUILD_APP),camino) +browser_path = $(TARGET_DIST)/Camino.app/Contents/MacOS/Camino +else +ifeq ($(OS_ARCH),Darwin) +browser_path = $(TARGET_DIST)/$(MOZ_MACBUNDLE_NAME)/Contents/MacOS/$(PROGRAM) +else +browser_path = $(TARGET_DIST)/bin/$(PROGRAM) +endif +endif diff --git a/build/bloatcycle.html b/build/bloatcycle.html new file mode 100644 index 000000000..dd605e459 --- /dev/null +++ b/build/bloatcycle.html @@ -0,0 +1,164 @@ +<script> +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + From mozilla/toolkit/content + These files did not have a license +*/ + +function quitHook() +{ + var xhr = new XMLHttpRequest(); + xhr.open("GET", "http://" + location.host + "/server/shutdown", true); + xhr.onreadystatechange = function (event) + { + if (xhr.readyState == 4) + goQuitApplication(); + }; + xhr.send(null); +} + +function canQuitApplication() +{ + var os = Components.classes["@mozilla.org/observer-service;1"] + .getService(Components.interfaces.nsIObserverService); + if (!os) + { + return true; + } + + try + { + var cancelQuit = Components.classes["@mozilla.org/supports-PRBool;1"] + .createInstance(Components.interfaces.nsISupportsPRBool); + os.notifyObservers(cancelQuit, "quit-application-requested", null); + + // Something aborted the quit process. + if (cancelQuit.data) + { + return false; + } + } + catch (ex) + { + } + os.notifyObservers(null, "quit-application-granted", null); + return true; +} + +function goQuitApplication() +{ + const privs = 'UniversalXPConnect'; + + try + { + netscape.security.PrivilegeManager.enablePrivilege(privs); + } + catch(ex) + { + throw('goQuitApplication: privilege failure ' + ex); + } + + if (!canQuitApplication()) + { + return false; + } + + const kAppStartup = '@mozilla.org/toolkit/app-startup;1'; + const kAppShell = '@mozilla.org/appshell/appShellService;1'; + var appService; + var forceQuit; + + if (kAppStartup in Components.classes) + { + appService = Components.classes[kAppStartup]. + getService(Components.interfaces.nsIAppStartup); + forceQuit = Components.interfaces.nsIAppStartup.eForceQuit; + + } + else if (kAppShell in Components.classes) + { + appService = Components.classes[kAppShell]. + getService(Components.interfaces.nsIAppShellService); + forceQuit = Components.interfaces.nsIAppShellService.eForceQuit; + } + else + { + throw 'goQuitApplication: no AppStartup/appShell'; + } + + var windowManager = Components.classes['@mozilla.org/appshell/window-mediator;1'].getService(); + + var windowManagerInterface = windowManager. + QueryInterface(Components.interfaces.nsIWindowMediator); + + var enumerator = windowManagerInterface.getEnumerator(null); + + while (enumerator.hasMoreElements()) + { + var domWindow = enumerator.getNext(); + if (("tryToClose" in domWindow) && !domWindow.tryToClose()) + { + return false; + } + domWindow.close(); + } + + try + { + appService.quit(forceQuit); + } + catch(ex) + { + throw('goQuitApplication: ' + ex); + } + + return true; +} + +// bloat test driver for TestGtkEmbed +// +// set |user_pref("dom.allow_scripts_to_close_windows", true);| to +// allow this to close the window when the loop is done. +// +// "ftp://ftp.mozilla.org", // not supported for TestGtkEmbed +// + +var list = + [ + "sample.html", + "forms.html", + "grid.html", + "forms.html", + "elements.html" + ]; +var interval = 5000; // 15000 +var idx = 0; +var w; + +window.onload = function () { + w = window.open("about:blank"); + window.setTimeout(loadURL, interval); +}; +function loadURL () { + w.location.href = list[idx++]; + if (idx < list.length) { + window.setTimeout(loadURL, interval); + } else { + if (navigator.platform.indexOf('Mac') != -1) { + goQuitApplication(); + } else { + window.setTimeout("w.close();", interval); + window.setTimeout("window.close();", interval*2); + } + } +} +var i; + +for(i=0; i < list.length;i++) { + document.write(list[i]); + document.write("<br>"); +} +</script> diff --git a/build/buildconfig.py b/build/buildconfig.py new file mode 100644 index 000000000..8a1865b60 --- /dev/null +++ b/build/buildconfig.py @@ -0,0 +1,30 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +import imp +import os +import sys + +path = os.path.dirname(__file__) +while not os.path.exists(os.path.join(path, 'config.status')): + parent = os.path.normpath(os.path.join(path, os.pardir)) + if parent == path: + raise Exception("Can't find config.status") + path = parent + +path = os.path.join(path, 'config.status') +config = imp.load_module('_buildconfig', open(path), path, ('', 'r', imp.PY_SOURCE)) + +# Copy values from the config.status namespace into this module namespace. +# This effectively imports topsrcdir, topobjdir, defines, substs, files, +# headers and non_global_defines +for var in config.__all__: + value = getattr(config, var) + if isinstance(value, list) and value and isinstance(value[0], tuple): + value = dict(value) + setattr(sys.modules[__name__], var, value) + +for var in os.environ: + if var != 'SHELL' and var in substs: + substs[var] = os.environ[var] diff --git a/build/checksums.py b/build/checksums.py new file mode 100644 index 000000000..9b13b50cb --- /dev/null +++ b/build/checksums.py @@ -0,0 +1,158 @@ +#!/usr/bin/python +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +from __future__ import with_statement + +from optparse import OptionParser +import logging +import os +try: + import hashlib +except: + hashlib = None + +def digest_file(filename, digest, chunk_size=1024): + '''Produce a checksum for the file specified by 'filename'. 'filename' + is a string path to a file that is opened and read in this function. The + checksum algorithm is specified by 'digest' and is a valid OpenSSL + algorithm. If the digest used is not valid or Python's hashlib doesn't + work, the None object will be returned instead. The size of blocks + that this function will read from the file object it opens based on + 'filename' can be specified by 'chunk_size', which defaults to 1K''' + assert not os.path.isdir(filename), 'this function only works with files' + logger = logging.getLogger('checksums.py') + if hashlib is not None: + logger.debug('Creating new %s object' % digest) + h = hashlib.new(digest) + with open(filename, 'rb') as f: + while True: + data = f.read(chunk_size) + if not data: + logger.debug('Finished reading in file') + break + h.update(data) + hash = h.hexdigest() + logger.debug('Hash for %s is %s' % (filename, hash)) + return hash + else: + # In this case we could subprocess.Popen and .communicate with + # sha1sum or md5sum + logger.warn('The python module for hashlib is missing!') + return None + + +def process_files(files, output_filename, digests, strip): + '''This function takes a list of file names, 'files'. It will then + compute the checksum for each of the files by opening the files. + Once each file is read and its checksum is computed, this function + will write the information to the file specified by 'output_filename'. + The path written in the output file will have anything specified by 'strip' + removed from the path. The output file is closed before returning nothing + The algorithm to compute checksums with can be specified by 'digests' + and needs to be a list of valid OpenSSL algorithms. + + The output file is written in the format: + <hash> <algorithm> <filesize> <filepath> + Example: + d1fa09a<snip>e4220 sha1 14250744 firefox-4.0b6pre.en-US.mac64.dmg + ''' + + logger = logging.getLogger('checksums.py') + if os.path.exists(output_filename): + logger.debug('Overwriting existing checksums file "%s"' % + output_filename) + else: + logger.debug('Creating a new checksums file "%s"' % output_filename) + with open(output_filename, 'w+') as output: + for file in files: + if os.path.isdir(file): + logger.warn('%s is a directory, skipping' % file) + else: + for digest in digests: + hash = digest_file(file, digest) + if hash is None: + logger.warn('Unable to generate a hash for %s. ' + + 'Skipping.' % file) + continue + if file.startswith(strip): + short_file = file[len(strip):] + short_file = short_file.lstrip('/') + else: + short_file = file + print >>output, '%s %s %s %s' % (hash, digest, + os.path.getsize(file), + short_file) + +def setup_logging(level=logging.DEBUG): + '''This function sets up the logging module using a speficiable logging + module logging level. The default log level is DEBUG. + + The output is in the format: + <level> - <message> + Example: + DEBUG - Finished reading in file +''' + + logger = logging.getLogger('checksums.py') + logger.setLevel(logging.DEBUG) + handler = logging.StreamHandler() + handler.setLevel(level) + formatter = logging.Formatter("%(levelname)s - %(message)s") + handler.setFormatter(formatter) + logger.addHandler(handler) + +def main(): + '''This is a main function that parses arguments, sets up logging + and generates a checksum file''' + # Parse command line arguments + parser = OptionParser() + parser.add_option('-d', '--digest', help='checksum algorithm to use', + action='append', dest='digests') + parser.add_option('-o', '--output', help='output file to use', + action='store', dest='outfile', default='checksums') + parser.add_option('-v', '--verbose', + help='Be noisy (takes precedence over quiet)', + action='store_true', dest='verbose', default=False) + parser.add_option('-q', '--quiet', help='Be quiet', action='store_true', + dest='quiet', default=False) + parser.add_option('-s', '--strip', + help='strip this path from the filenames', + dest='strip', default=os.getcwd()) + options, args = parser.parse_args() + + #Figure out which logging level to use + if options.verbose: + loglevel = logging.DEBUG + elif options.quiet: + loglevel = logging.ERROR + else: + loglevel = logging.INFO + + #Set up logging + setup_logging(loglevel) + logger = logging.getLogger('checksums.py') + + # Validate the digest type to use + if not options.digests: + options.digests = ['sha1'] + try: + for digest in options.digests: + hashlib.new(digest) + except ValueError, ve: + logger.error('Could not create a "%s" hash object (%s)' % + (digest, ve.args[0])) + exit(1) + + # Validate the files to checksum + files = [] + for i in args: + if os.path.exists(i): + files.append(i) + else: + logger.info('File "%s" was not found on the filesystem' % i) + process_files(files, options.outfile, options.digests, options.strip) + +if __name__ == '__main__': + main() diff --git a/build/cl.py b/build/cl.py new file mode 100644 index 000000000..319d768bd --- /dev/null +++ b/build/cl.py @@ -0,0 +1,105 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +import ctypes +import os, os.path +import subprocess +import sys + +CL_INCLUDES_PREFIX = os.environ.get("CL_INCLUDES_PREFIX", "Note: including file:") + +GetShortPathName = ctypes.windll.kernel32.GetShortPathNameW +GetLongPathName = ctypes.windll.kernel32.GetLongPathNameW + + +# cl.exe likes to print inconsistent paths in the showIncludes output +# (some lowercased, some not, with different directions of slashes), +# and we need the original file case for make/pymake to be happy. +# As this is slow and needs to be called a lot of times, use a cache +# to speed things up. +_normcase_cache = {} + +def normcase(path): + # Get*PathName want paths with backslashes + path = path.replace('/', os.sep) + dir = os.path.dirname(path) + # name is fortunately always going to have the right case, + # so we can use a cache for the directory part only. + name = os.path.basename(path) + if dir in _normcase_cache: + result = _normcase_cache[dir] + else: + path = ctypes.create_unicode_buffer(dir) + length = GetShortPathName(path, None, 0) + shortpath = ctypes.create_unicode_buffer(length) + GetShortPathName(path, shortpath, length) + length = GetLongPathName(shortpath, None, 0) + if length > len(path): + path = ctypes.create_unicode_buffer(length) + GetLongPathName(shortpath, path, length) + result = _normcase_cache[dir] = path.value + return os.path.join(result, name) + + +def InvokeClWithDependencyGeneration(cmdline): + target = "" + # Figure out what the target is + for arg in cmdline: + if arg.startswith("-Fo"): + target = arg[3:] + break + + if target == None: + print >>sys.stderr, "No target set" and sys.exit(1) + + # Assume the source file is the last argument + source = cmdline[-1] + assert not source.startswith('-') + + # The deps target lives here + depstarget = os.path.basename(target) + ".pp" + + cmdline += ['-showIncludes'] + cl = subprocess.Popen(cmdline, stdout=subprocess.PIPE) + + deps = set([normcase(source).replace(os.sep, '/')]) + for line in cl.stdout: + # cl -showIncludes prefixes every header with "Note: including file:" + # and an indentation corresponding to the depth (which we don't need) + if line.startswith(CL_INCLUDES_PREFIX): + dep = line[len(CL_INCLUDES_PREFIX):].strip() + # We can't handle pathes with spaces properly in mddepend.pl, but + # we can assume that anything in a path with spaces is a system + # header and throw it away. + dep = normcase(dep) + if ' ' not in dep: + deps.add(dep.replace(os.sep, '/')) + else: + sys.stdout.write(line) # Make sure we preserve the relevant output + # from cl + + ret = cl.wait() + if ret != 0 or target == "": + sys.exit(ret) + + depsdir = os.path.normpath(os.path.join(os.curdir, ".deps")) + depstarget = os.path.join(depsdir, depstarget) + if not os.path.isdir(depsdir): + try: + os.makedirs(depsdir) + except OSError: + pass # This suppresses the error we get when the dir exists, at the + # cost of masking failure to create the directory. We'll just + # die on the next line though, so it's not that much of a loss. + + with open(depstarget, "w") as f: + f.write("%s: %s" % (target, source)) + for dep in sorted(deps): + f.write(" \\\n%s" % dep) + f.write('\n') + for dep in sorted(deps): + f.write("%s:\n" % dep) + +if __name__ == "__main__": + InvokeClWithDependencyGeneration(sys.argv[1:]) diff --git a/build/clang-plugin/Makefile.in b/build/clang-plugin/Makefile.in new file mode 100644 index 000000000..a83561270 --- /dev/null +++ b/build/clang-plugin/Makefile.in @@ -0,0 +1,42 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +CXX := @CXX@ +CXXFLAGS := @CXXFLAGS@ +LDFLAGS := @LDFLAGS@ +VPATH := @srcdir@ + +# Helper for end +NULL := + +CPPSRCS := \ + clang-plugin.cpp \ + $(NULL) + +TESTSRCS := \ + TestMustOverride.cpp \ + TestNonHeapClass.cpp \ + TestStackClass.cpp \ + $(NULL) + +OBJS := $(patsubst %.cpp,%.o,$(CPPSRCS)) +TESTS := $(patsubst %.cpp,test-%,$(TESTSRCS)) + +PLUGIN := libclang-plugin.so + +all: $(PLUGIN) $(TESTS) + +$(OBJS): %.o: %.cpp Makefile + $(CXX) -o $@ -c $(CXXFLAGS) $< + +$(PLUGIN): $(OBJS) + rm -f $@ + $(CXX) -shared -o $@ $(CXXFLAGS) $(LDFLAGS) $(OBJS) -lclangASTMatchers + +TESTFLAGS := -fsyntax-only -Xclang -verify \ + -Xclang -load -Xclang $(CURDIR)/$(PLUGIN) \ + -Xclang -add-plugin -Xclang moz-check + +$(TESTS): test-%: tests/%.cpp $(PLUGIN) + $(CXX) $(TESTFLAGS) $< diff --git a/build/clang-plugin/clang-plugin.cpp b/build/clang-plugin/clang-plugin.cpp new file mode 100644 index 000000000..2106214c1 --- /dev/null +++ b/build/clang-plugin/clang-plugin.cpp @@ -0,0 +1,358 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#include "clang/AST/ASTConsumer.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/RecursiveASTVisitor.h" +#include "clang/ASTMatchers/ASTMatchers.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/Basic/Version.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/FrontendPluginRegistry.h" +#include "clang/Frontend/MultiplexConsumer.h" +#include "clang/Sema/Sema.h" +#include "llvm/ADT/DenseMap.h" + +#define CLANG_VERSION_FULL (CLANG_VERSION_MAJOR * 100 + CLANG_VERSION_MINOR) + +using namespace llvm; +using namespace clang; + +namespace { + +using namespace clang::ast_matchers; +class DiagnosticsMatcher { +public: + DiagnosticsMatcher(); + + ASTConsumer *makeASTConsumer() { + return astMatcher.newASTConsumer(); + } + +private: + class StackClassChecker : public MatchFinder::MatchCallback { + public: + virtual void run(const MatchFinder::MatchResult &Result); + void noteInferred(QualType T, DiagnosticsEngine &Diag); + }; + + class NonHeapClassChecker : public MatchFinder::MatchCallback { + public: + virtual void run(const MatchFinder::MatchResult &Result); + void noteInferred(QualType T, DiagnosticsEngine &Diag); + }; + + StackClassChecker stackClassChecker; + NonHeapClassChecker nonheapClassChecker; + MatchFinder astMatcher; +}; + +class MozChecker : public ASTConsumer, public RecursiveASTVisitor<MozChecker> { + DiagnosticsEngine &Diag; + const CompilerInstance &CI; + DiagnosticsMatcher matcher; +public: + MozChecker(const CompilerInstance &CI) : Diag(CI.getDiagnostics()), CI(CI) {} + + ASTConsumer *getOtherConsumer() { + return matcher.makeASTConsumer(); + } + + virtual void HandleTranslationUnit(ASTContext &ctx) { + TraverseDecl(ctx.getTranslationUnitDecl()); + } + + static bool hasCustomAnnotation(const Decl *d, const char *spelling) { + AnnotateAttr *attr = d->getAttr<AnnotateAttr>(); + if (!attr) + return false; + + return attr->getAnnotation() == spelling; + } + + bool VisitCXXRecordDecl(CXXRecordDecl *d) { + // We need definitions, not declarations + if (!d->isThisDeclarationADefinition()) return true; + + // Look through all of our immediate bases to find methods that need to be + // overridden + typedef std::vector<CXXMethodDecl *> OverridesVector; + OverridesVector must_overrides; + for (CXXRecordDecl::base_class_iterator base = d->bases_begin(), + e = d->bases_end(); base != e; ++base) { + // The base is either a class (CXXRecordDecl) or it's a templated class... + CXXRecordDecl *parent = base->getType() + .getDesugaredType(d->getASTContext())->getAsCXXRecordDecl(); + // The parent might not be resolved to a type yet. In this case, we can't + // do any checking here. For complete correctness, we should visit + // template instantiations, but this case is likely to be rare, so we will + // ignore it until it becomes important. + if (!parent) { + continue; + } + parent = parent->getDefinition(); + for (CXXRecordDecl::method_iterator M = parent->method_begin(); + M != parent->method_end(); ++M) { + if (hasCustomAnnotation(*M, "moz_must_override")) + must_overrides.push_back(*M); + } + } + + for (OverridesVector::iterator it = must_overrides.begin(); + it != must_overrides.end(); ++it) { + bool overridden = false; + for (CXXRecordDecl::method_iterator M = d->method_begin(); + !overridden && M != d->method_end(); ++M) { + // The way that Clang checks if a method M overrides its parent method + // is if the method has the same name but would not overload. + if (M->getName() == (*it)->getName() && + !CI.getSema().IsOverload(*M, (*it), false)) + overridden = true; + } + if (!overridden) { + unsigned overrideID = Diag.getDiagnosticIDs()->getCustomDiagID( + DiagnosticIDs::Error, "%0 must override %1"); + unsigned overrideNote = Diag.getDiagnosticIDs()->getCustomDiagID( + DiagnosticIDs::Note, "function to override is here"); + Diag.Report(d->getLocation(), overrideID) << d->getDeclName() << + (*it)->getDeclName(); + Diag.Report((*it)->getLocation(), overrideNote); + } + } + return true; + } +}; + +/** + * Where classes may be allocated. Regular classes can be allocated anywhere, + * non-heap classes on the stack or as static variables, and stack classes only + * on the stack. Note that stack classes subsumes non-heap classes. + */ +enum ClassAllocationNature { + RegularClass = 0, + NonHeapClass = 1, + StackClass = 2 +}; + +/// A cached data of whether classes are stack classes, non-heap classes, or +/// neither. +DenseMap<const CXXRecordDecl *, + std::pair<const Decl *, ClassAllocationNature> > inferredAllocCauses; + +ClassAllocationNature getClassAttrs(QualType T); + +ClassAllocationNature getClassAttrs(CXXRecordDecl *D) { + // Normalize so that D points to the definition if it exists. If it doesn't, + // then we can't allocate it anyways. + if (!D->hasDefinition()) + return RegularClass; + D = D->getDefinition(); + // Base class: anyone with this annotation is obviously a stack class + if (MozChecker::hasCustomAnnotation(D, "moz_stack_class")) + return StackClass; + + // See if we cached the result. + DenseMap<const CXXRecordDecl *, + std::pair<const Decl *, ClassAllocationNature> >::iterator it = + inferredAllocCauses.find(D); + if (it != inferredAllocCauses.end()) { + return it->second.second; + } + + // Continue looking, we might be a stack class yet. Even if we're a nonheap + // class, it might be possible that we've inferred to be a stack class. + ClassAllocationNature type = RegularClass; + if (MozChecker::hasCustomAnnotation(D, "moz_nonheap_class")) { + type = NonHeapClass; + } + inferredAllocCauses.insert(std::make_pair(D, + std::make_pair((const Decl *)0, type))); + + // Look through all base cases to figure out if the parent is a stack class or + // a non-heap class. Since we might later infer to also be a stack class, keep + // going. + for (CXXRecordDecl::base_class_iterator base = D->bases_begin(), + e = D->bases_end(); base != e; ++base) { + ClassAllocationNature super = getClassAttrs(base->getType()); + if (super == StackClass) { + inferredAllocCauses[D] = std::make_pair( + base->getType()->getAsCXXRecordDecl(), StackClass); + return StackClass; + } else if (super == NonHeapClass) { + inferredAllocCauses[D] = std::make_pair( + base->getType()->getAsCXXRecordDecl(), NonHeapClass); + type = NonHeapClass; + } + } + + // Maybe it has a member which is a stack class. + for (RecordDecl::field_iterator field = D->field_begin(), e = D->field_end(); + field != e; ++field) { + ClassAllocationNature fieldType = getClassAttrs(field->getType()); + if (fieldType == StackClass) { + inferredAllocCauses[D] = std::make_pair(*field, StackClass); + return StackClass; + } else if (fieldType == NonHeapClass) { + inferredAllocCauses[D] = std::make_pair(*field, NonHeapClass); + type = NonHeapClass; + } + } + + return type; +} + +ClassAllocationNature getClassAttrs(QualType T) { + while (const ArrayType *arrTy = T->getAsArrayTypeUnsafe()) + T = arrTy->getElementType(); + CXXRecordDecl *clazz = T->getAsCXXRecordDecl(); + return clazz ? getClassAttrs(clazz) : RegularClass; +} + +} + +namespace clang { +namespace ast_matchers { + +/// This matcher will match any class with the stack class assertion or an +/// array of such classes. +AST_MATCHER(QualType, stackClassAggregate) { + return getClassAttrs(Node) == StackClass; +} + +/// This matcher will match any class with the stack class assertion or an +/// array of such classes. +AST_MATCHER(QualType, nonheapClassAggregate) { + return getClassAttrs(Node) == NonHeapClass; +} +} +} + +namespace { + +DiagnosticsMatcher::DiagnosticsMatcher() { + // Stack class assertion: non-local variables of a stack class are forbidden + // (non-localness checked in the callback) + astMatcher.addMatcher(varDecl(hasType(stackClassAggregate())).bind("node"), + &stackClassChecker); + // Stack class assertion: new stack class is forbidden (unless placement new) + astMatcher.addMatcher(newExpr(hasType(pointerType( + pointee(stackClassAggregate()) + ))).bind("node"), &stackClassChecker); + // Non-heap class assertion: new non-heap class is forbidden (unless placement + // new) + astMatcher.addMatcher(newExpr(hasType(pointerType( + pointee(nonheapClassAggregate()) + ))).bind("node"), &nonheapClassChecker); +} + +void DiagnosticsMatcher::StackClassChecker::run( + const MatchFinder::MatchResult &Result) { + DiagnosticsEngine &Diag = Result.Context->getDiagnostics(); + unsigned stackID = Diag.getDiagnosticIDs()->getCustomDiagID( + DiagnosticIDs::Error, "variable of type %0 only valid on the stack"); + if (const VarDecl *d = Result.Nodes.getNodeAs<VarDecl>("node")) { + // Ignore the match if it's a local variable. + if (d->hasLocalStorage()) + return; + + Diag.Report(d->getLocation(), stackID) << d->getType(); + noteInferred(d->getType(), Diag); + } else if (const CXXNewExpr *expr = + Result.Nodes.getNodeAs<CXXNewExpr>("node")) { + // If it's placement new, then this match doesn't count. + if (expr->getNumPlacementArgs() > 0) + return; + Diag.Report(expr->getStartLoc(), stackID) << expr->getAllocatedType(); + noteInferred(expr->getAllocatedType(), Diag); + } +} + +void DiagnosticsMatcher::StackClassChecker::noteInferred(QualType T, + DiagnosticsEngine &Diag) { + unsigned inheritsID = Diag.getDiagnosticIDs()->getCustomDiagID( + DiagnosticIDs::Note, + "%0 is a stack class because it inherits from a stack class %1"); + unsigned memberID = Diag.getDiagnosticIDs()->getCustomDiagID( + DiagnosticIDs::Note, + "%0 is a stack class because member %1 is a stack class %2"); + + // Find the CXXRecordDecl that is the stack class of interest + while (const ArrayType *arrTy = T->getAsArrayTypeUnsafe()) + T = arrTy->getElementType(); + CXXRecordDecl *clazz = T->getAsCXXRecordDecl(); + + // Direct result, we're done. + if (MozChecker::hasCustomAnnotation(clazz, "moz_stack_class")) + return; + + const Decl *cause = inferredAllocCauses[clazz].first; + if (const CXXRecordDecl *CRD = dyn_cast<CXXRecordDecl>(cause)) { + Diag.Report(clazz->getLocation(), inheritsID) << T << CRD->getDeclName(); + } else if (const FieldDecl *FD = dyn_cast<FieldDecl>(cause)) { + Diag.Report(FD->getLocation(), memberID) << T << FD << FD->getType(); + } + + // Recursively follow this back. + noteInferred(cast<ValueDecl>(cause)->getType(), Diag); +} + +void DiagnosticsMatcher::NonHeapClassChecker::run( + const MatchFinder::MatchResult &Result) { + DiagnosticsEngine &Diag = Result.Context->getDiagnostics(); + unsigned stackID = Diag.getDiagnosticIDs()->getCustomDiagID( + DiagnosticIDs::Error, "variable of type %0 is not valid on the heap"); + const CXXNewExpr *expr = Result.Nodes.getNodeAs<CXXNewExpr>("node"); + // If it's placement new, then this match doesn't count. + if (expr->getNumPlacementArgs() > 0) + return; + Diag.Report(expr->getStartLoc(), stackID) << expr->getAllocatedType(); + noteInferred(expr->getAllocatedType(), Diag); +} + +void DiagnosticsMatcher::NonHeapClassChecker::noteInferred(QualType T, + DiagnosticsEngine &Diag) { + unsigned inheritsID = Diag.getDiagnosticIDs()->getCustomDiagID( + DiagnosticIDs::Note, + "%0 is a non-heap class because it inherits from a non-heap class %1"); + unsigned memberID = Diag.getDiagnosticIDs()->getCustomDiagID( + DiagnosticIDs::Note, + "%0 is a non-heap class because member %1 is a non-heap class %2"); + + // Find the CXXRecordDecl that is the stack class of interest + while (const ArrayType *arrTy = T->getAsArrayTypeUnsafe()) + T = arrTy->getElementType(); + CXXRecordDecl *clazz = T->getAsCXXRecordDecl(); + + // Direct result, we're done. + if (MozChecker::hasCustomAnnotation(clazz, "moz_nonheap_class")) + return; + + const Decl *cause = inferredAllocCauses[clazz].first; + if (const CXXRecordDecl *CRD = dyn_cast<CXXRecordDecl>(cause)) { + Diag.Report(clazz->getLocation(), inheritsID) << T << CRD->getDeclName(); + } else if (const FieldDecl *FD = dyn_cast<FieldDecl>(cause)) { + Diag.Report(FD->getLocation(), memberID) << T << FD << FD->getType(); + } + + // Recursively follow this back. + noteInferred(cast<ValueDecl>(cause)->getType(), Diag); +} + +class MozCheckAction : public PluginASTAction { +public: + ASTConsumer *CreateASTConsumer(CompilerInstance &CI, StringRef fileName) { + MozChecker *checker = new MozChecker(CI); + + ASTConsumer *consumers[] = { checker, checker->getOtherConsumer() }; + return new MultiplexConsumer(consumers); + } + + bool ParseArgs(const CompilerInstance &CI, + const std::vector<std::string> &args) { + return true; + } +}; +} + +static FrontendPluginRegistry::Add<MozCheckAction> +X("moz-check", "check moz action"); diff --git a/build/clang-plugin/configure b/build/clang-plugin/configure new file mode 100644 index 000000000..c658f0463 --- /dev/null +++ b/build/clang-plugin/configure @@ -0,0 +1,48 @@ +#!/bin/sh + +# Default srcdir to this directory +srcdir= + +for option; do + case "$option" in + -*=*) optarg=`echo "$option" | sed 's/[-_a-zA-Z0-9]*=//'` ;; + *) optarg= ;; + esac + + case "$option" in + --srcdir=*) srcdir="$optarg";; + esac +done + +if test -z "$CXX"; then + CXX=`which clang++` +fi + +echo -n "checking for llvm-config... " + +if test -z "$LLVMCONFIG"; then + LLVMCONFIG=`which llvm-config` +fi + +if test -z "$LLVMCONFIG"; then + LLVMCONFIG=`dirname $CXX`/llvm-config +fi + +if test ! -x "$LLVMCONFIG"; then + echo "configure: error: Cannot find an llvm-config binary for building a clang plugin" 1>&2 + exit 1 +fi + +echo "$LLVMCONFIG" + +LLVMCXXFLAGS=`$LLVMCONFIG --cxxflags` +LLVMLDFLAGS=`$LLVMCONFIG --ldflags` +CXXFLAGS="$CXXFLAGS $LLVMCXXFLAGS -fno-rtti -fno-exceptions" +LDFLAGS="$LDFLAGS $LLVMLDFLAGS" + +cat $srcdir/Makefile.in | sed \ + -e "s%@CXX@%$CXX%" \ + -e "s%@CXXFLAGS@%$CXXFLAGS%" \ + -e "s%@LDFLAGS@%$LDFLAGS%" \ + -e "s%@srcdir@%$srcdir%" \ + > Makefile diff --git a/build/clang-plugin/tests/TestMustOverride.cpp b/build/clang-plugin/tests/TestMustOverride.cpp new file mode 100644 index 000000000..8e053f6c2 --- /dev/null +++ b/build/clang-plugin/tests/TestMustOverride.cpp @@ -0,0 +1,63 @@ +#define MOZ_MUST_OVERRIDE __attribute__((annotate("moz_must_override"))) +// Ignore warnings not related to static analysis here +#pragma GCC diagnostic ignored "-Woverloaded-virtual" + +struct S { + virtual void f() MOZ_MUST_OVERRIDE; // expected-note {{function to override is here}} + virtual void g() MOZ_MUST_OVERRIDE; + virtual void h() MOZ_MUST_OVERRIDE; // expected-note {{function to override is here}} +}; +struct C : S { // expected-error {{'C' must override 'f'}} expected-error {{'C' must override 'h'}} + virtual void g() MOZ_MUST_OVERRIDE; // expected-note {{function to override is here}} + virtual void h(int); + void q() MOZ_MUST_OVERRIDE; // expected-note {{function to override is here}} +}; +struct D : C { // expected-error {{'D' must override 'g'}} expected-error {{'D' must override 'q'}} + virtual void f(); +}; + +struct Base { + virtual void VirtMethod() MOZ_MUST_OVERRIDE; // expected-note {{function to override is here}} + void NonVirtMethod() MOZ_MUST_OVERRIDE; // expected-note {{function to override is here}} + static void StaticMethod() MOZ_MUST_OVERRIDE; +}; + +struct DoesNotPropagate : Base { + virtual void VirtMethod(); + void NonVirtMethod(); + static void StaticMethod(); +}; + +struct Final : DoesNotPropagate { }; + +struct Propagates : Base { + virtual void VirtMethod() MOZ_MUST_OVERRIDE; // expected-note {{function to override is here}} + void NonVirtMethod() MOZ_MUST_OVERRIDE; // expected-note {{function to override is here}} + static void StaticMethod() MOZ_MUST_OVERRIDE; // expected-note {{function to override is here}} +}; + +struct FailsFinal : Propagates { }; // expected-error {{'FailsFinal' must override 'VirtMethod'}} expected-error {{'FailsFinal' must override 'NonVirtMethod'}} expected-error {{'FailsFinal' must override 'StaticMethod'}} + +struct WrongOverload : Base { // expected-error {{'WrongOverload' must override 'VirtMethod'}} expected-error {{'WrongOverload' must override 'NonVirtMethod'}} + virtual void VirtMethod() const; + void NonVirtMethod(int param); + static void StaticMethod(); +}; + +namespace A { namespace B { namespace C { + struct Param {}; + struct Base { + void f(Param p) MOZ_MUST_OVERRIDE; // expected-note {{function to override is here}} + }; +}}} + +struct Param {}; + +struct Derived : A::B::C::Base { + typedef A::B::C::Param Typedef; + void f(Typedef t); +}; + +struct BadDerived : A::B::C::Base { // expected-error {{'BadDerived' must override 'f'}} + void f(Param p); +}; diff --git a/build/clang-plugin/tests/TestNonHeapClass.cpp b/build/clang-plugin/tests/TestNonHeapClass.cpp new file mode 100644 index 000000000..65fdb35ee --- /dev/null +++ b/build/clang-plugin/tests/TestNonHeapClass.cpp @@ -0,0 +1,62 @@ +#define MOZ_NONHEAP_CLASS __attribute__((annotate("moz_nonheap_class"))) +#define MOZ_STACK_CLASS __attribute__((annotate("moz_stack_class"))) +#include <stddef.h> + +struct MOZ_NONHEAP_CLASS NonHeap { + int i; + void *operator new(size_t x) { return 0; } + void *operator new(size_t blah, char *buffer) { return buffer; } +}; + +template <class T> +struct MOZ_NONHEAP_CLASS TemplateClass { + T i; +}; + +void gobble(void *) { } + +void misuseNonHeapClass(int len) { + NonHeap valid; + NonHeap alsoValid[2]; + static NonHeap validStatic; + static NonHeap alsoValidStatic[2]; + + gobble(&valid); + gobble(&validStatic); + gobble(&alsoValid[0]); + + gobble(new NonHeap); // expected-error {{variable of type 'NonHeap' is not valid on the heap}} + gobble(new NonHeap[10]); // expected-error {{variable of type 'NonHeap' is not valid on the heap}} + gobble(new TemplateClass<int>); // expected-error {{variable of type 'TemplateClass<int>' is not valid on the heap}} + gobble(len <= 5 ? &valid : new NonHeap); // expected-error {{variable of type 'NonHeap' is not valid on the heap}} + + char buffer[sizeof(NonHeap)]; + gobble(new (buffer) NonHeap); +} + +NonHeap validStatic; +struct RandomClass { + NonHeap nonstaticMember; // expected-note {{'RandomClass' is a non-heap class because member 'nonstaticMember' is a non-heap class 'NonHeap'}} + static NonHeap staticMember; +}; +struct MOZ_NONHEAP_CLASS RandomNonHeapClass { + NonHeap nonstaticMember; + static NonHeap staticMember; +}; + +struct BadInherit : NonHeap {}; // expected-note {{'BadInherit' is a non-heap class because it inherits from a non-heap class 'NonHeap'}} +struct MOZ_NONHEAP_CLASS GoodInherit : NonHeap {}; + +void useStuffWrongly() { + gobble(new BadInherit); // expected-error {{variable of type 'BadInherit' is not valid on the heap}} + gobble(new RandomClass); // expected-error {{variable of type 'RandomClass' is not valid on the heap}} +} + +// Stack class overrides non-heap classes. +struct MOZ_STACK_CLASS StackClass {}; +struct MOZ_NONHEAP_CLASS InferredStackClass : GoodInherit { + NonHeap nonstaticMember; + StackClass stackClass; // expected-note {{'InferredStackClass' is a stack class because member 'stackClass' is a stack class 'StackClass'}} +}; + +InferredStackClass global; // expected-error {{variable of type 'InferredStackClass' only valid on the stack}} diff --git a/build/clang-plugin/tests/TestStackClass.cpp b/build/clang-plugin/tests/TestStackClass.cpp new file mode 100644 index 000000000..1cce9f4ab --- /dev/null +++ b/build/clang-plugin/tests/TestStackClass.cpp @@ -0,0 +1,50 @@ +#define MOZ_STACK_CLASS __attribute__((annotate("moz_stack_class"))) +#include <stddef.h> + +struct MOZ_STACK_CLASS Stack { + int i; + void *operator new(size_t x) { return 0; } + void *operator new(size_t blah, char *buffer) { return buffer; } +}; + +template <class T> +struct MOZ_STACK_CLASS TemplateClass { + T i; +}; + +void gobble(void *) { } + +void misuseStackClass(int len) { + Stack valid; + Stack alsoValid[2]; + static Stack notValid; // expected-error {{variable of type 'Stack' only valid on the stack}} + static Stack alsoNotValid[2]; // expected-error {{variable of type 'Stack [2]' only valid on the stack}} + + gobble(&valid); + gobble(¬Valid); + gobble(&alsoValid[0]); + + gobble(new Stack); // expected-error {{variable of type 'Stack' only valid on the stack}} + gobble(new Stack[10]); // expected-error {{variable of type 'Stack' only valid on the stack}} + gobble(new TemplateClass<int>); // expected-error {{variable of type 'TemplateClass<int>' only valid on the stack}} + gobble(len <= 5 ? &valid : new Stack); // expected-error {{variable of type 'Stack' only valid on the stack}} + + char buffer[sizeof(Stack)]; + gobble(new (buffer) Stack); +} + +Stack notValid; // expected-error {{variable of type 'Stack' only valid on the stack}} +struct RandomClass { + Stack nonstaticMember; // expected-note {{'RandomClass' is a stack class because member 'nonstaticMember' is a stack class 'Stack'}} + static Stack staticMember; // expected-error {{variable of type 'Stack' only valid on the stack}} +}; +struct MOZ_STACK_CLASS RandomStackClass { + Stack nonstaticMember; + static Stack staticMember; // expected-error {{variable of type 'Stack' only valid on the stack}} +}; + +struct BadInherit : Stack {}; // expected-note {{'BadInherit' is a stack class because it inherits from a stack class 'Stack'}} +struct MOZ_STACK_CLASS GoodInherit : Stack {}; + +BadInherit moreInvalid; // expected-error {{variable of type 'BadInherit' only valid on the stack}} +RandomClass evenMoreInvalid; // expected-error {{variable of type 'RandomClass' only valid on the stack}} diff --git a/build/defines.sh b/build/defines.sh new file mode 100644 index 000000000..9de47c446 --- /dev/null +++ b/build/defines.sh @@ -0,0 +1,3 @@ +# Define indicating that this build is prior to one of the early betas. To be +# unset mid-way through the beta cycle. +EARLY_BETA_OR_EARLIER=1 diff --git a/build/dumbmake-dependencies b/build/dumbmake-dependencies new file mode 100644 index 000000000..be31b1802 --- /dev/null +++ b/build/dumbmake-dependencies @@ -0,0 +1,68 @@ +toolkit/library + ipc + netwerk/build + netwerk + storage/build + storage + xpcom + chrome + extensions + docshell/build + docshell + uriloader + modules + widget + gfx + toolkit/components/build + toolkit/components + security/build + security/manager + security/dbm + security/nss + accessible/build + accessible + dom + content + layout + editor + parser + js/src + js/xpconnect + js/xpconnect/loader + mfbt + view + caps + xpfe/appshell + xpfe/components + js + toolkit + rdf/build + embedding + hal + image/build + image + intl/build + intl + media + profile + services + startupcache +browser/app + browser/base + browser/components + browser/devtools + browser/locales + browser/modules + browser/themes + toolkit + toolkit/components + toolkit/content + toolkit/crashreporter + toolkit/devtools + toolkit/forgetaboutsite + toolkit/identity + toolkit/modules + toolkit/mozapps/extensions + toolkit/profile + toolkit/themes + toolkit/webapps diff --git a/build/genrc.sh b/build/genrc.sh new file mode 100644 index 000000000..c0c619462 --- /dev/null +++ b/build/genrc.sh @@ -0,0 +1,13 @@ +#!/bin/sh +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +DATATYPE="$1" +INFILE="$2" + +echo "${DATATYPE} RCDATA" +sed 's/"/""/g' ${INFILE} | awk 'BEGIN { printf("BEGIN\n") } { printf("\"%s\\r\\n\",\n", $0) } END { printf("\"\\0\"\nEND\n") }' + +exit 0 diff --git a/build/leaktest.py.in b/build/leaktest.py.in new file mode 100644 index 000000000..1e991e6de --- /dev/null +++ b/build/leaktest.py.in @@ -0,0 +1,50 @@ +#literal #!/usr/bin/python +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +import SimpleHTTPServer +import SocketServer +import threading +import os +import sys +import logging +from getopt import getopt +from automation import Automation + +PORT = 8888 +SCRIPT_DIR = os.path.abspath(os.path.realpath(os.path.dirname(sys.argv[0]))) +PROFILE_DIRECTORY = os.path.abspath(os.path.join(SCRIPT_DIR, "./leakprofile")) +os.chdir(SCRIPT_DIR) + +class EasyServer(SocketServer.TCPServer): + allow_reuse_address = True + +if __name__ == '__main__': + automation = Automation() + DIST_BIN = os.path.join(SCRIPT_DIR, automation.DIST_BIN) + opts, extraArgs = getopt(sys.argv[1:], 'l:') + if len(opts) > 0: + try: + automation.log.addHandler(logging.FileHandler(opts[0][1], "w")) + except: + automation.log.info("Unable to open logfile " + opts[0][1] + \ + "ONLY logging to stdout.") + + httpd = EasyServer(("", PORT), SimpleHTTPServer.SimpleHTTPRequestHandler) + t = threading.Thread(target=httpd.serve_forever) + t.setDaemon(True) + t.start() + + automation.setServerInfo("localhost", PORT) + automation.initializeProfile(PROFILE_DIRECTORY) + browserEnv = automation.environment() + + if not "XPCOM_DEBUG_BREAK" in browserEnv: + browserEnv["XPCOM_DEBUG_BREAK"] = "stack" + url = "http://localhost:%d/bloatcycle.html" % PORT + appPath = os.path.join(SCRIPT_DIR, automation.DEFAULT_APP) + status = automation.runApp(url, browserEnv, appPath, PROFILE_DIRECTORY, + extraArgs, timeout=1800) + sys.exit(status) diff --git a/build/link.py b/build/link.py new file mode 100644 index 000000000..19fbfac7b --- /dev/null +++ b/build/link.py @@ -0,0 +1,45 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +from __future__ import with_statement +import os, subprocess, sys, threading, time +from win32 import procmem + +def measure_vsize_threadfunc(proc, output_file): + """ + Measure the virtual memory usage of |proc| at regular intervals + until it exits, then print the maximum value and write it to + |output_file|. + """ + maxvsize = 0 + while proc.returncode is None: + maxvsize, vsize = procmem.get_vmsize(proc._handle) + time.sleep(0.5) + print "TinderboxPrint: linker max vsize: %d" % maxvsize + with open(output_file, "w") as f: + f.write("%d\n" % maxvsize) + +def measure_link_vsize(output_file, args): + """ + Execute |args|, and measure the maximum virtual memory usage of the process, + printing it to stdout when finished. + """ + proc = subprocess.Popen(args) + t = threading.Thread(target=measure_vsize_threadfunc, + args=(proc, output_file)) + t.start() + # Wait for the linker to finish. + exitcode = proc.wait() + # ...and then wait for the background thread to finish. + t.join() + return exitcode + +if __name__ == "__main__": + if sys.platform != "win32": + print >>sys.stderr, "link.py is only for use on Windows!" + sys.exit(1) + if len(sys.argv) < 3: + print >>sys.stderr, "Usage: link.py <output filename> <commandline>" + sys.exit(1) + sys.exit(measure_link_vsize(sys.argv[1], sys.argv[2:])) diff --git a/build/mach_bootstrap.py b/build/mach_bootstrap.py new file mode 100644 index 000000000..4671b18b0 --- /dev/null +++ b/build/mach_bootstrap.py @@ -0,0 +1,163 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +from __future__ import print_function, unicode_literals + +import os +import platform +import sys +import time + + +STATE_DIR_FIRST_RUN = ''' +mach and the build system store shared state in a common directory on the +filesystem. The following directory will be created: + + {userdir} + +If you would like to use a different directory, hit CTRL+c and set the +MOZBUILD_STATE_PATH environment variable to the directory you would like to +use and re-run mach. For this change to take effect forever, you'll likely +want to export this environment variable from your shell's init scripts. +'''.lstrip() + + +# TODO Bug 794506 Integrate with the in-tree virtualenv configuration. +SEARCH_PATHS = [ + 'python/mach', + 'python/mozboot', + 'python/mozbuild', + 'python/blessings', + 'python/psutil', + 'python/which', + 'build/pymake', + 'config', + 'other-licenses/ply', + 'xpcom/idl-parser', + 'testing', + 'testing/xpcshell', + 'testing/marionette/client', + 'testing/marionette/client/marionette', + 'testing/mozbase/mozcrash', + 'testing/mozbase/mozdevice', + 'testing/mozbase/mozfile', + 'testing/mozbase/mozhttpd', + 'testing/mozbase/mozlog', + 'testing/mozbase/moznetwork', + 'testing/mozbase/mozprocess', + 'testing/mozbase/mozprofile', + 'testing/mozbase/mozrunner', + 'testing/mozbase/mozinfo', +] + +# Individual files providing mach commands. +MACH_MODULES = [ + 'addon-sdk/mach_commands.py', + 'layout/tools/reftest/mach_commands.py', + 'python/mach/mach/commands/commandinfo.py', + 'python/mozboot/mozboot/mach_commands.py', + 'python/mozbuild/mozbuild/config.py', + 'python/mozbuild/mozbuild/mach_commands.py', + 'python/mozbuild/mozbuild/frontend/mach_commands.py', + 'testing/marionette/mach_commands.py', + 'testing/mochitest/mach_commands.py', + 'testing/xpcshell/mach_commands.py', + 'tools/mach_commands.py', +] + + +CATEGORIES = { + 'build': { + 'short': 'Build Commands', + 'long': 'Interact with the build system', + 'priority': 80, + }, + 'post-build': { + 'short': 'Post-build Commands', + 'long': 'Common actions performed after completing a build.', + 'priority': 70, + }, + 'testing': { + 'short': 'Testing', + 'long': 'Run tests.', + 'priority': 60, + }, + 'devenv': { + 'short': 'Development Environment', + 'long': 'Set up and configure your development environment.', + 'priority': 50, + }, + 'build-dev': { + 'short': 'Low-level Build System Interaction', + 'long': 'Interact with specific parts of the build system.', + 'priority': 20, + }, + 'misc': { + 'short': 'Potpourri', + 'long': 'Potent potables and assorted snacks.', + 'priority': 10, + } +} + + +def bootstrap(topsrcdir, mozilla_dir=None): + if mozilla_dir is None: + mozilla_dir = topsrcdir + + # Ensure we are running Python 2.7+. We put this check here so we generate a + # user-friendly error message rather than a cryptic stack trace on module + # import. + if sys.version_info[0] != 2 or sys.version_info[1] < 7: + print('Python 2.7 or above (but not Python 3) is required to run mach.') + print('You are running Python', platform.python_version()) + sys.exit(1) + + # Global build system and mach state is stored in a central directory. By + # default, this is ~/.mozbuild. However, it can be defined via an + # environment variable. We detect first run (by lack of this directory + # existing) and notify the user that it will be created. The logic for + # creation is much simpler for the "advanced" environment variable use + # case. For default behavior, we educate users and give them an opportunity + # to react. We always exit after creating the directory because users don't + # like surprises. + state_user_dir = os.path.expanduser('~/.mozbuild') + state_env_dir = os.environ.get('MOZBUILD_STATE_PATH', None) + if state_env_dir: + if not os.path.exists(state_env_dir): + print('Creating global state directory from environment variable: %s' + % state_env_dir) + os.makedirs(state_env_dir, mode=0777) + print('Please re-run mach.') + sys.exit(1) + else: + if not os.path.exists(state_user_dir): + print(STATE_DIR_FIRST_RUN.format(userdir=state_user_dir)) + try: + for i in range(20, -1, -1): + time.sleep(1) + sys.stdout.write('%d ' % i) + sys.stdout.flush() + except KeyboardInterrupt: + sys.exit(1) + + print('\nCreating default state directory: %s' % state_user_dir) + os.mkdir(state_user_dir) + print('Please re-run mach.') + sys.exit(1) + + try: + import mach.main + except ImportError: + sys.path[0:0] = [os.path.join(mozilla_dir, path) for path in SEARCH_PATHS] + import mach.main + + mach = mach.main.Mach(topsrcdir) + for category, meta in CATEGORIES.items(): + mach.define_category(category, meta['short'], meta['long'], + meta['priority']) + + for path in MACH_MODULES: + mach.load_commands_from_file(os.path.join(mozilla_dir, path)) + + return mach diff --git a/build/macosx/mozconfig.common b/build/macosx/mozconfig.common new file mode 100644 index 000000000..e258b0d02 --- /dev/null +++ b/build/macosx/mozconfig.common @@ -0,0 +1,27 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +. "$topsrcdir/build/mozconfig.common" + +if [ -d "$topsrcdir/clang" ]; then + # mozilla-central based build + export CC=$topsrcdir/clang/bin/clang + export CXX=$topsrcdir/clang/bin/clang++ +elif [ -d "$topsrcdir/../clang" ]; then + # comm-central based build + export CC=$topsrcdir/../clang/bin/clang + export CXX=$topsrcdir/../clang/bin/clang++ +fi + +# If not set use the system default clang +if [ -z "$CC" ]; then + export CC=clang +fi + +# If not set use the system default clang++ +if [ -z "$CXX" ]; then + export CXX=clang++ +fi + +ac_add_options --with-ccache diff --git a/build/macosx/permissions/chown_revert.c b/build/macosx/permissions/chown_revert.c new file mode 100644 index 000000000..2cf3e37c3 --- /dev/null +++ b/build/macosx/permissions/chown_revert.c @@ -0,0 +1,20 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include <unistd.h> +#include <stdio.h> + +int main(int argc, char **argv) +{ + if (argc != 2) + return 1; + + uid_t realuser = getuid(); + char uidstring[20]; + snprintf(uidstring, 19, "%i", realuser); + uidstring[19] = '\0'; + + return execl("/usr/sbin/chown", + "/usr/sbin/chown", "-R", "-h", uidstring, argv[1], (char*) 0); +} diff --git a/build/macosx/permissions/chown_root.c b/build/macosx/permissions/chown_root.c new file mode 100644 index 000000000..c9b13a530 --- /dev/null +++ b/build/macosx/permissions/chown_root.c @@ -0,0 +1,14 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include <unistd.h> + +int main(int argc, char **argv) +{ + if (argc != 2) + return 1; + + return execl("/usr/sbin/chown", + "/usr/sbin/chown", "-R", "-h", "root:admin", argv[1], (char*) 0); +} diff --git a/build/macosx/universal/flight.mk b/build/macosx/universal/flight.mk new file mode 100644 index 000000000..d24e4d362 --- /dev/null +++ b/build/macosx/universal/flight.mk @@ -0,0 +1,59 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +# BE CAREFUL! This makefile handles a postflight_all rule for a +# multi-project build, so DON'T rely on anything that might differ between +# the two OBJDIRs. + +ifndef OBJDIR +OBJDIR_ARCH_1 = $(MOZ_OBJDIR)/$(firstword $(MOZ_BUILD_PROJECTS)) +OBJDIR_ARCH_2 = $(MOZ_OBJDIR)/$(word 2,$(MOZ_BUILD_PROJECTS)) +DIST_ARCH_1 = $(OBJDIR_ARCH_1)/dist +DIST_ARCH_2 = $(OBJDIR_ARCH_2)/dist +DIST_UNI = $(DIST_ARCH_1)/universal +OBJDIR = $(OBJDIR_ARCH_1) +endif + +topsrcdir = $(TOPSRCDIR) +DEPTH = $(OBJDIR) +include $(OBJDIR)/config/autoconf.mk + +core_abspath = $(if $(filter /%,$(1)),$(1),$(CURDIR)/$(1)) + +DIST = $(OBJDIR)/dist + +postflight_all: + mkdir -p $(DIST_UNI)/$(MOZ_PKG_APPNAME) + rm -f $(DIST_ARCH_2)/universal + ln -s $(call core_abspath,$(DIST_UNI)) $(DIST_ARCH_2)/universal +# Stage a package for buildsymbols to be happy. Doing so in OBJDIR_ARCH_1 +# actually does a universal staging with both OBJDIR_ARCH_1 and OBJDIR_ARCH_2. + $(MAKE) -C $(OBJDIR_ARCH_1)/$(MOZ_BUILD_APP)/installer \ + PKG_SKIP_STRIP=1 stage-package +ifdef ENABLE_TESTS +# Now, repeat the process for the test package. + $(MAKE) -C $(OBJDIR_ARCH_1) UNIVERSAL_BINARY= CHROME_JAR= package-tests + $(MAKE) -C $(OBJDIR_ARCH_2) UNIVERSAL_BINARY= CHROME_JAR= package-tests + rm -rf $(DIST_UNI)/test-package-stage +# automation.py differs because it hardcodes a path to +# dist/bin. It doesn't matter which one we use. + if test -d $(DIST_ARCH_1)/test-package-stage -a \ + -d $(DIST_ARCH_2)/test-package-stage; then \ + cp $(DIST_ARCH_1)/test-package-stage/mochitest/automation.py \ + $(DIST_ARCH_2)/test-package-stage/mochitest/; \ + cp -RL $(DIST_ARCH_1)/test-package-stage/mochitest/extensions/specialpowers \ + $(DIST_ARCH_2)/test-package-stage/mochitest/extensions/; \ + cp $(DIST_ARCH_1)/test-package-stage/xpcshell/automation.py \ + $(DIST_ARCH_2)/test-package-stage/xpcshell/; \ + cp $(DIST_ARCH_1)/test-package-stage/reftest/automation.py \ + $(DIST_ARCH_2)/test-package-stage/reftest/; \ + cp -RL $(DIST_ARCH_1)/test-package-stage/reftest/specialpowers \ + $(DIST_ARCH_2)/test-package-stage/reftest/; \ + $(TOPSRCDIR)/build/macosx/universal/unify \ + --unify-with-sort "\.manifest$$" \ + --unify-with-sort "all-test-dirs\.list$$" \ + $(DIST_ARCH_1)/test-package-stage \ + $(DIST_ARCH_2)/test-package-stage \ + $(DIST_UNI)/test-package-stage; fi +endif diff --git a/build/macosx/universal/mozconfig b/build/macosx/universal/mozconfig new file mode 100644 index 000000000..fd1b3ee92 --- /dev/null +++ b/build/macosx/universal/mozconfig @@ -0,0 +1,11 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +# i386/x86-64 Universal Build mozconfig + +# As used here, arguments in $MOZ_BUILD_PROJECTS are suitable as arguments +# to gcc's -arch parameter. +mk_add_options MOZ_BUILD_PROJECTS="i386 x86_64" + +. $topsrcdir/build/macosx/universal/mozconfig.common diff --git a/build/macosx/universal/mozconfig.common b/build/macosx/universal/mozconfig.common new file mode 100644 index 000000000..8eb0fef58 --- /dev/null +++ b/build/macosx/universal/mozconfig.common @@ -0,0 +1,58 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +mk_add_options MOZ_UNIFY_BDATE=1 + +mk_add_options MOZ_POSTFLIGHT_ALL+=build/macosx/universal/flight.mk + +DARWIN_VERSION=`uname -r` +ac_add_app_options i386 --target=i386-apple-darwin$DARWIN_VERSION +ac_add_app_options x86_64 --target=x86_64-apple-darwin$DARWIN_VERSION +ac_add_app_options i386 --with-unify-dist=../x86_64/dist +ac_add_app_options x86_64 --with-unify-dist=../i386/dist + +ac_add_options --with-macos-sdk=/Developer/SDKs/MacOSX10.6.sdk + +. $topsrcdir/build/macosx/mozconfig.common + +# $MOZ_BUILD_APP is only defined when sourced by configure. That's not a +# problem, because the variables it affects only need to be set for +# configure. +if test -n "$MOZ_BUILD_APP" ; then +if test "$MOZ_BUILD_APP" = "i386" -o "$MOZ_BUILD_APP" = "x86_64"; then + TARGET_CPU=$MOZ_BUILD_APP + + # $HOST_CXX is presently unused. $HOST_CC will only be used during a cross + # compile. + HOST_CC=$CC + HOST_CXX=$CXX + + NATIVE_CPU=`$topsrcdir/build/autoconf/config.guess | cut -f1 -d-` + + # It's not strictly necessary to specify -arch during native builds, but it + # makes the merged about:buildconfig easier to follow, and it reduces + # conditionalized differences between builds. + CC="$CC -arch $TARGET_CPU" + CXX="$CXX -arch $TARGET_CPU" + + # These must be set for cross builds, and don't hurt straight builds. + RANLIB=ranlib + AR=ar + AS=$CC + LD=ld + STRIP="strip" + + # Let configure know that we mean business. + if test "$NATIVE_CPU" != "$TARGET_CPU" ; then + CROSS_COMPILE=1 + fi + + # Each per-CPU build should be entirely oblivious to the fact that a + # universal binary will be produced. The exception is packager.mk, which + # needs to know to look for universal bits when building the .dmg. + UNIVERSAL_BINARY=1 + + export CC CXX HOST_CC HOST_CXX RANLIB AR AS LD STRIP +fi +fi diff --git a/build/macosx/universal/unify b/build/macosx/universal/unify new file mode 100644 index 000000000..81f270218 --- /dev/null +++ b/build/macosx/universal/unify @@ -0,0 +1,1525 @@ +#!/usr/bin/perl +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +use strict; +use warnings; + +=pod + +=head1 NAME + +B<unify> - Mac OS X universal binary packager + +=head1 SYNOPSIS + +B<unify> +I<ppc-path> +I<x86-path> +I<universal-path> +[B<--dry-run>] +[B<--only-one> I<action>] +[B<--verbosity> I<level>] +[B<--unify-with-sort> I<regex>] + +=head1 DESCRIPTION + +I<unify> merges any two architecture-specific files or directory trees +into a single file or tree suitable for use on either architecture as a +"fat" or "universal binary." + +Architecture-specific Mach-O files will be merged into fat Mach-O files +using L<lipo(1)>. Non-Mach-O files in the architecture-specific trees +are compared to ensure that they are equivalent before copying. Symbolic +links are permitted in the architecture-specific trees and will cause +identical links to be created in the merged tree, provided that the source +links have identical targets. Directories are processed recursively. + +If the architecture-specific source trees contain zip archives (including +jar files) that are not identical according to a byte-for-byte check, they +are still assumed to be equivalent if both archives contain exactly the +same members with identical checksums and sizes. + +Behavior when one architecture-specific tree contains files that the other +does not is controlled by the B<--only-one> option. + +If Mach-O files cannot be merged using L<lipo(1)>, zip archives are not +equivalent, regular files are not identical, or any other error occurs, +B<unify> will fail with an exit status of 1. Diagnostic messages are +typically printed to stderr; this behavior can be controlled with the +B<--verbosity> option. + +=head1 OPTIONS + +=over 5 + +=item I<ppc-path> + +=item I<x86-path> + +The paths to directory trees containing PowerPC and x86 builds, +respectively. I<ppc-path> and I<x86-path> are permitted to contain files +that are already "fat," and only the appropriate architecture's images will +be used. + +I<ppc-path> and I<x86-path> are also permitted to both be files, in which +case B<unify> operates solely on those files, and produces an appropriate +merged file at I<target-path>. + +=item I<target-path> + +The path to the merged file or directory tree. This path will be created, +and it must not exist prior to running B<unify>. + +=item B<--dry-run> + +When specified, the commands that would be executed are printed, without +actually executing them. Note that B<--dry-run> and the equivalent +B<--verbosity> level during "wet" runs may print equivalent commands when +no commands are in fact executed: certain operations are handled internally +within B<unify>, and an approximation of a command that performs a similar +task is printed. + +=item B<--only-one> I<action> + +Controls handling of files that are only present in one of the two source +trees. I<action> may be: + skip - These files are skipped. + copy - These files are copied from the tree in which they exist. + fail - When this condition occurs, it is treated as an error. + +The default I<action> is copy. + +=item B<--verbosity> I<level> + +Adjusts the level of loudness of B<unify>. The possible values for +I<level> are: + 0 - B<unify> never prints anything. + (Other programs that B<unify> calls may still print messages.) + 1 - Fatal error messages are printed to stderr. + 2 - Nonfatal warnings are printed to stderr. + 3 - Commands are printed to stdout as they are executed. + +The default I<level> is 2. + +=item B<--unify-with-sort> I<regex> + +Allows merging files matching I<regex> that differ only by the ordering +of the lines contained within them. The unified file will have its contents +sorted. This option may be given multiple times to specify multiple +regexes for matching files. + +=back + +=head1 EXAMPLES + +=over 5 + +=item Create a universal .app bundle from two architecture-specific .app +bundles: + +unify --only-one copy ppc/dist/firefox/Firefox.app + x86/dist/firefox/Firefox.app universal/Firefox.app + --verbosity 3 + +=item Merge two identical architecture-specific trees: + +unify --only-one fail /usr/local /nfs/x86/usr/local + /tmp/usrlocal.fat + +=back + +=head1 REQUIREMENTS + +The only esoteric requirement of B<unify> is that the L<lipo(1)> command +be available. It is present on Mac OS X systems at least as early as +10.3.9, and probably earlier. Mac OS X 10.4 ("Tiger") or later are +recommended. + +=head1 LICENSE + +MPL 1.1/GPL 2.0/LGPL 2.1. Your choice + +=head1 AUTHOR + +The software was initially written by Mark Mentovai; copyright 2006 +Google Inc. + +=head1 SEE ALSO + +L<cmp(1)>, L<ditto(1)>, L<lipo(1)> + +=cut + +use Archive::Zip(':ERROR_CODES'); +use Errno; +use Fcntl; +use File::Compare; +use File::Copy; +use Getopt::Long; + +my (%gConfig, $gDryRun, $gOnlyOne, $gVerbosity, @gSortMatches); + +sub argumentEscape(@); +sub command(@); +sub compareZipArchives($$); +sub complain($$@); +sub copyIfIdentical($$$); +sub slurp($); +sub get_sorted($); +sub compare_sorted($$); +sub copyIfIdenticalWhenSorted($$$); +sub createUniqueFile($$); +sub makeUniversal($$$); +sub makeUniversalDirectory($$$); +sub makeUniversalInternal($$$$); +sub makeUniversalFile($$$); +sub usage(); +sub readZipCRCs($); + +{ + package FileAttrCache; + + sub new($$); + + sub isFat($); + sub isMachO($); + sub isZip($); + sub lIsDir($); + sub lIsExecutable($); + sub lIsRegularFile($); + sub lIsSymLink($); + sub lstat($); + sub lstatMode($); + sub lstatType($); + sub magic($); + sub magic2($); + sub path($); + sub stat($); + sub statSize($); +} + +%gConfig = ( + 'cmd_lipo' => 'lipo', + 'cmd_rm' => 'rm', +); + +$gDryRun = 0; +$gOnlyOne = 'copy'; +$gVerbosity = 2; +@gSortMatches = (); + +Getopt::Long::Configure('pass_through'); +GetOptions('dry-run' => \$gDryRun, + 'only-one=s' => \$gOnlyOne, + 'verbosity=i' => \$gVerbosity, + 'unify-with-sort=s' => \@gSortMatches, + 'config=s' => \%gConfig); # "hidden" option not in usage() + +if (scalar(@ARGV) != 3 || $gVerbosity < 0 || $gVerbosity > 3 || + ($gOnlyOne ne 'skip' && $gOnlyOne ne 'copy' && $gOnlyOne ne 'fail')) { + usage(); + exit(1); +} + +if (!makeUniversal($ARGV[0],$ARGV[1],$ARGV[2])) { + # makeUniversal or something it called will have printed an error. + exit(1); +} + +exit(0); + +# argumentEscape(@arguments) +# +# Takes a list of @arguments and makes them shell-safe. +sub argumentEscape(@) { + my (@arguments); + @arguments = @_; + + my ($argument, @argumentsOut); + foreach $argument (@arguments) { + $argument =~ s%([^A-Za-z0-9_\-/.=+,])%\\$1%g; + push(@argumentsOut, $argument); + } + + return @argumentsOut; +} + +# command(@arguments) +# +# Runs the specified command by calling system(@arguments). If $gDryRun +# is true, the command is printed but not executed, and 0 is returned. +# if $gVerbosity is greater than 1, the command is printed before being +# executed. When the command is executed, the system() return value will +# be returned. stdout and stderr are left connected for command output. +sub command(@) { + my (@arguments); + @arguments = @_; + if ($gVerbosity >= 3 || $gDryRun) { + print(join(' ', argumentEscape(@arguments))."\n"); + } + if ($gDryRun) { + return 0; + } + return system(@arguments); +} + +# compareZipArchives($zip1, $zip2) +# +# Given two pathnames to zip archives, determines whether or not they are +# functionally identical. Returns true if they are, false if they differ in +# some substantial way, and undef if an error occurs. If the zip files +# differ, diagnostic messages are printed indicating how they differ. +# +# Zip files will differ if any of the members are different as defined by +# readZipCRCs, which consider CRCs, sizes, and file types as stored in the +# file header. Timestamps are not considered. Zip files also differ if one +# file contains members that the other one does not. $gOnlyOne has no +# effect on this behavior. +sub compareZipArchives($$) { + my ($zip1, $zip2); + ($zip1, $zip2) = @_; + + my ($CRCHash1, $CRCHash2); + if (!defined($CRCHash1 = readZipCRCs($zip1))) { + # readZipCRCs printed an error. + return undef; + } + if (!defined($CRCHash2 = readZipCRCs($zip2))) { + # readZipCRCs printed an error. + return undef; + } + + my (@diffCRCs, @onlyInZip1); + @diffCRCs = (); + @onlyInZip1 = (); + + my ($memberName); + foreach $memberName (keys(%$CRCHash1)) { + if (!exists($$CRCHash2{$memberName})) { + # The member is present in $zip1 but not $zip2. + push(@onlyInZip1, $memberName); + } + elsif ($$CRCHash1{$memberName} ne $$CRCHash2{$memberName}) { + # The member is present in both archives but its CRC or some other + # other critical attribute isn't identical. + push(@diffCRCs, $memberName); + } + delete($$CRCHash2{$memberName}); + } + + # If any members remain in %CRCHash2, it's because they're not present + # in $zip1. + my (@onlyInZip2); + @onlyInZip2 = keys(%$CRCHash2); + + if (scalar(@onlyInZip1) + scalar(@onlyInZip2) + scalar(@diffCRCs)) { + complain(1, 'compareZipArchives: zip archives differ:', + $zip1, + $zip2); + if (scalar(@onlyInZip1)) { + complain(1, 'compareZipArchives: members only in former:', + @onlyInZip1); + } + if (scalar(@onlyInZip2)) { + complain(1, 'compareZipArchives: members only in latter:', + @onlyInZip2); + } + if (scalar(@diffCRCs)) { + complain(1, 'compareZipArchives: members differ:', + @diffCRCs); + } + return 0; + } + + return 1; +} + +# complain($severity, $message, @list) +# +# Prints $message to stderr if $gVerbosity allows it for severity level +# $severity. @list is a list of words that will be shell-escaped and printed +# after $message, one per line, intended to be used, for example, to list +# arguments to a call that failed. +# +# Expected severity levels are 1 for hard errors and 2 for non-fatal warnings. +# +# Always returns false as a convenience, so callers can return complain's +# return value when it is used to signal errors. +sub complain($$@) { + my ($severity, $message, @list); + ($severity, $message, @list) = @_; + + if ($gVerbosity >= $severity) { + print STDERR ($0.': '.$message."\n"); + + my ($item); + while ($item = shift(@list)) { + print STDERR (' '.(argumentEscape($item))[0]. + (scalar(@list)?',':'')."\n"); + } + } + + return 0; +} + +# copyIfIdentical($source1, $source2, $target) +# +# $source1 and $source2 are FileAttrCache objects that are compared, and if +# identical, copied to path string $target. The comparison is initially +# done as a byte-for-byte comparison, but if the files differ and appear to +# be zip archives, compareZipArchives is called to determine whether +# files that are not byte-for-byte identical are equivalent archives. +# +# Returns true on success, false for files that are not identical or +# equivalent archives, and undef if an error occurs. +# +# One of $source1 and $source2 is permitted to be undef. In this event, +# whichever source is defined is copied directly to $target without performing +# any comparisons. This enables the $gOnlyOne = 'copy' mode, which is +# driven by makeUniversalDirectory and makeUniversalInternal. +sub copyIfIdentical($$$) { + my ($source1, $source2, $target); + ($source1, $source2, $target) = @_; + + if (!defined($source1)) { + # If there's only one source file, make it the first file. Order + # isn't important here, and this makes it possible to use + # defined($source2) as the switch, and to always copy from $source1. + $source1 = $source2; + $source2 = undef; + } + + if (defined($source2)) { + # Only do the comparisons if there are two source files. If there's + # only one source file, skip the comparisons and go straight to the + # copy operation. + if ($gVerbosity >= 3 || $gDryRun) { + print('cmp -s '. + join(' ',argumentEscape($source1->path(), $source2->path()))."\n"); + } + my ($comparison); + if (!defined($comparison = compare($source1->path(), $source2->path())) || + $comparison == -1) { + return complain(1, 'copyIfIdentical: compare: '.$!.' while comparing:', + $source1->path(), + $source2->path()); + } + elsif ($comparison != 0) { + my ($zip1, $zip2); + if (defined($zip1 = $source1->isZip()) && + defined($zip2 = $source2->isZip()) && + $zip1 && $zip2) { + my ($zipComparison); + if (!defined($zipComparison = compareZipArchives($source1->path(), + $source2->path)) || + !$zipComparison) { + # An error occurred or the zip files aren't sufficiently identical. + # compareZipArchives will have printed an error message. + return 0; + } + # The zip files were compared successfully, and they both contain + # all of the same members, and all of their members' CRCs are + # identical. For the purposes of this script, the zip files can be + # treated as identical, so reset $comparison. + $comparison = 0; + } + } + if ($comparison != 0) { + return complain(1, 'copyIfIdentical: files differ:', + $source1->path(), + $source2->path()); + } + } + + if ($gVerbosity >= 3 || $gDryRun) { + print('cp '. + join(' ',argumentEscape($source1->path(), $target))."\n"); + } + + if (!$gDryRun) { + my ($isExecutable); + + # Set the execute bits (as allowed by the umask) on the new file if any + # execute bit is set on either old file. + $isExecutable = $source1->lIsExecutable() || + (defined($source2) && $source2->lIsExecutable()); + + if (!createUniqueFile($target, $isExecutable ? 0777 : 0666)) { + # createUniqueFile printed an error. + return 0; + } + + if (!copy($source1->path(), $target)) { + complain(1, 'copyIfIdentical: copy: '.$!.' while copying', + $source1->path(), + $target); + unlink($target); + return 0; + } + } + + return 1; +} + +# slurp($file) +# +# Read the contents of $file into an array and return it. +# Returns undef on error. +sub slurp($) { + my $file = $_[0]; + open FILE, $file or return undef; + my @lines = <FILE>; + close FILE; + return @lines; +} + +# get_sorted($file) +# Get the sorted lines of a file as a list, normalizing a newline on the last line if necessary. +sub get_sorted($) { + my ($file) = @_; + my @lines = slurp($file); + my $lastline = $lines[-1]; + if (!($lastline =~ /\n/)) { + $lines[-1] = $lastline . "\n"; + } + return sort(@lines); +} + +# compare_sorted($file1, $file2) +# +# Read the contents of both files into arrays, sort the arrays, +# and then compare the two arrays for equality. +# +# Returns 0 if the sorted array contents are equal, or 1 if not. +# Returns undef on error. +sub compare_sorted($$) { + my ($file1, $file2) = @_; + my @lines1 = get_sorted($file1); + my @lines2 = get_sorted($file2); + + return undef if !@lines1 || !@lines2; + return 1 unless scalar @lines1 == scalar @lines2; + + for (my $i = 0; $i < scalar @lines1; $i++) { + return 1 if $lines1[$i] ne $lines2[$i]; + } + return 0; +} + +# copyIfIdenticalWhenSorted($source1, $source2, $target) +# +# $source1 and $source2 are FileAttrCache objects that are compared, and if +# identical, copied to path string $target. The comparison is done by +# sorting the individual lines within the two files and comparing the results. +# +# Returns true on success, false for files that are not equivalent, +# and undef if an error occurs. +sub copyIfIdenticalWhenSorted($$$) { + my ($source1, $source2, $target); + ($source1, $source2, $target) = @_; + + if ($gVerbosity >= 3 || $gDryRun) { + print('cmp -s '. + join(' ',argumentEscape($source1->path(), $source2->path()))."\n"); + } + my ($comparison); + if (!defined($comparison = compare_sorted($source1->path(), + $source2->path())) || + $comparison == -1) { + return complain(1, 'copyIfIdenticalWhenSorted: compare: '.$! + .' while comparing:', + $source1->path(), + $source2->path()); + } + if ($comparison != 0) { + return complain(1, 'copyIfIdenticalWhenSorted: files differ:', + $source1->path(), + $source2->path()); + } + + if ($gVerbosity >= 3 || $gDryRun) { + print('cp '. + join(' ',argumentEscape($source1->path(), $target))."\n"); + } + + if (!$gDryRun) { + my ($isExecutable); + + # Set the execute bits (as allowed by the umask) on the new file if any + # execute bit is set on either old file. + $isExecutable = $source1->lIsExecutable() || + (defined($source2) && $source2->lIsExecutable()); + + if (!createUniqueFile($target, $isExecutable ? 0777 : 0666)) { + # createUniqueFile printed an error. + return 0; + } + + if (!copy($source1->path(), $target)) { + complain(1, 'copyIfIdenticalWhenSorted: copy: '.$! + .' while copying', + $source1->path(), + $target); + unlink($target); + return 0; + } + } + + return 1; +} + +# createUniqueFile($path, $mode) +# +# Creates a new plain empty file at pathname $path, provided it does not +# yet exist. $mode is used as the file mode. The actual file's mode will +# be modified by the effective umask. Returns false if the file could +# not be created, setting $! to the error. An error message is printed +# in the event of failure. +sub createUniqueFile($$) { + my ($path, $mode); + ($path, $mode) = @_; + + my ($fh); + if (!sysopen($fh, $path, O_WRONLY | O_CREAT | O_EXCL, $mode)) { + return complain(1, 'createUniqueFile: open: '.$!.' for:', + $path); + } + close($fh); + + return 1; +} + +# makeUniversal($pathPPC, $pathX86, $pathTarget) +# +# The top-level call. $pathPPC, $pathX86, and $pathTarget are strings +# identifying the ppc and x86 files or directories to merge and the location +# to merge them to. Returns false on failure and true on success. +sub makeUniversal($$$) { + my ($pathTarget, $pathPPC, $pathX86); + ($pathPPC, $pathX86, $pathTarget) = @_; + + my ($filePPC, $fileX86); + $filePPC = FileAttrCache->new($pathPPC); + $fileX86 = FileAttrCache->new($pathX86); + + return makeUniversalInternal(1, $filePPC, $fileX86, $pathTarget); +} + +# makeUniversalDirectory($dirPPC, $dirX86, $dirTarget) +# +# This is part of the heart of recursion. $dirPPC and $dirX86 are +# FileAttrCache objects designating the source ppc and x86 directories to +# merge into a universal directory at $dirTarget, a string. For each file +# in $dirPPC and $dirX86, makeUniversalInternal is called. +# makeUniversalInternal will call back into makeUniversalDirectory for +# directories, thus completing the recursion. If a failure is encountered +# in ths function or in makeUniversalInternal or anything that it calls, +# false is returned, otherwise, true is returned. +# +# If there are files present in one source directory but not both, the +# value of $gOnlyOne controls the behavior. If $gOnlyOne is 'copy', the +# single source file is copied into $pathTarget. If it is 'skip', it is +# skipped. If it is 'fail', such files will trigger makeUniversalDirectory +# to fail. +# +# If either source directory is undef, it is treated as having no files. +# This facilitates deep recursion when entire directories are only present +# in one source when $gOnlyOne = 'copy'. +sub makeUniversalDirectory($$$) { + my ($dirPPC, $dirX86, $dirTarget); + ($dirPPC, $dirX86, $dirTarget) = @_; + + my ($dh, @filesPPC, @filesX86); + + @filesPPC = (); + if (defined($dirPPC)) { + if (!opendir($dh, $dirPPC->path())) { + return complain(1, 'makeUniversalDirectory: opendir ppc: '.$!.' for:', + $dirPPC->path()); + } + @filesPPC = readdir($dh); + closedir($dh); + } + + @filesX86 = (); + if (defined($dirX86)) { + if (!opendir($dh, $dirX86->path())) { + return complain(1, 'makeUniversalDirectory: opendir x86: '.$!.' for:', + $dirX86->path()); + } + @filesX86 = readdir($dh); + closedir($dh); + } + + my (%common, $file, %onlyPPC, %onlyX86); + + %onlyPPC = (); + foreach $file (@filesPPC) { + if ($file eq '.' || $file eq '..') { + next; + } + $onlyPPC{$file}=1; + } + + %common = (); + %onlyX86 = (); + foreach $file (@filesX86) { + if ($file eq '.' || $file eq '..') { + next; + } + if ($onlyPPC{$file}) { + delete $onlyPPC{$file}; + $common{$file}=1; + } + else { + $onlyX86{$file}=1; + } + } + + # First, handle files common to both. + foreach $file (sort(keys(%common))) { + if (!makeUniversalInternal(0, + FileAttrCache->new($dirPPC->path().'/'.$file), + FileAttrCache->new($dirX86->path().'/'.$file), + $dirTarget.'/'.$file)) { + # makeUniversalInternal will have printed an error. + return 0; + } + } + + # Handle files found only in a single directory here. There are three + # options, dictated by $gOnlyOne: fail if files are only present in + # one directory, skip any files only present in one directory, or copy + # these files straight over to the target directory. In any event, + # a message will be printed indicating that the file trees don't match + # exactly. + if (keys(%onlyPPC)) { + complain(($gOnlyOne eq 'fail' ? 1 : 2), + ($gOnlyOne ne 'fail' ? 'warning: ' : ''). + 'makeUniversalDirectory: only in ppc '. + (argumentEscape($dirPPC->path()))[0].':', + argumentEscape(keys(%onlyPPC))); + } + + if (keys(%onlyX86)) { + complain(($gOnlyOne eq 'fail' ? 1 : 2), + ($gOnlyOne ne 'fail' ? 'warning: ' : ''). + 'makeUniversalDirectory: only in x86 '. + (argumentEscape($dirX86->path()))[0].':', + argumentEscape(keys(%onlyX86))); + } + + if ($gOnlyOne eq 'fail' && (keys(%onlyPPC) || keys(%onlyX86))) { + # Error message(s) printed above. + return 0; + } + + if ($gOnlyOne eq 'copy') { + foreach $file (sort(keys(%onlyPPC))) { + if (!makeUniversalInternal(0, + FileAttrCache->new($dirPPC->path().'/'.$file), + undef, + $dirTarget.'/'.$file)) { + # makeUniversalInternal will have printed an error. + return 0; + } + } + + foreach $file (sort(keys(%onlyX86))) { + if (!makeUniversalInternal(0, + undef, + FileAttrCache->new($dirX86->path().'/'.$file), + $dirTarget.'/'.$file)) { + # makeUniversalInternal will have printed an error. + return 0; + } + } + } + + return 1; +} + +# makeUniversalFile($sourcePPC, $sourceX86, $targetPath) +# +# Creates a universal file at pathname $targetPath based on a ppc image at +# $sourcePPC and an x86 image at $sourceX86. $sourcePPC and $sourceX86 are +# both FileAttrCache objects. Returns true on success and false on failure. +# On failure, diagnostics will be printed to stderr. +# +# The source files may be either thin Mach-O images of the appropriate +# architecture, or fat Mach-O files that contain images of the appropriate +# architecture. +# +# This function wraps the lipo utility, see lipo(1). +sub makeUniversalFile($$$) { + my ($sourcePPC, $sourceX86, $targetPath, @tempThinFiles, $thinPPC, $thinX86); + ($sourcePPC, $sourceX86, $targetPath) = @_; + $thinPPC = $sourcePPC; + $thinX86 = $sourceX86; + + @tempThinFiles = (); + + # The source files might already be fat. They should be thinned out to only + # contain a single architecture. + + my ($isFatPPC, $isFatX86); + + if(!defined($isFatPPC = $sourcePPC->isFat())) { + # isFat printed its own error + return 0; + } + elsif($isFatPPC) { + $thinPPC = FileAttrCache->new($targetPath.'.ppc'); + push(@tempThinFiles, $thinPPC->path()); + if (command($gConfig{'cmd_lipo'}, '-thin', 'ppc', + $sourcePPC->path(), '-output', $thinPPC->path()) != 0) { + unlink(@tempThinFiles); + return complain(1, 'lipo thin ppc failed for:', + $sourcePPC->path(), + $thinPPC->path()); + } + } + + if(!defined($isFatX86 = $sourceX86->isFat())) { + # isFat printed its own error + unlink(@tempThinFiles); + return 0; + } + elsif($isFatX86) { + $thinX86 = FileAttrCache->new($targetPath.'.x86'); + push(@tempThinFiles, $thinX86->path()); + if (command($gConfig{'cmd_lipo'}, '-thin', 'i386', + $sourceX86->path(), '-output', $thinX86->path()) != 0) { + unlink(@tempThinFiles); + return complain(1, 'lipo thin x86 failed for:', + $sourceX86->path(), + $thinX86->path()); + } + } + + # The image for each architecture in the fat file will be aligned on + # a specific boundary, default 4096 bytes, see lipo(1) -segalign. + # Since there's no tail-padding, the fat file will consume the least + # space on disk if the image that comes last exceeds the segment size + # by the smallest amount. + # + # This saves an average of 1kB per fat file over the naive approach of + # always putting one architecture first: average savings is 2kB per + # file, but the naive approach would have gotten it right half of the + # time. + + my ($sizePPC, $sizeX86, $thinPPCForStat, $thinX86ForStat); + + if (!$gDryRun) { + $thinPPCForStat = $thinPPC; + $thinX86ForStat = $thinX86; + } + else { + # Normally, fat source files will have been converted into temporary + # thin files. During a dry run, that doesn't happen, so fake it up + # a little bit by always using the source file, fat or thin, for the + # stat. + $thinPPCForStat = $sourcePPC; + $thinX86ForStat = $sourceX86; + } + + if (!defined($sizePPC = $thinPPCForStat->statSize())) { + unlink(@tempThinFiles); + return complain(1, 'stat ppc: '.$!.' for:', + $thinPPCForStat->path()); + } + if (!defined($sizeX86 = $thinX86ForStat->statSize())) { + unlink(@tempThinFiles); + return complain(1, 'stat x86: '.$!.' for:', + $thinX86ForStat->path()); + } + + $sizePPC = $sizePPC % 4096; + $sizeX86 = $sizeX86 % 4096; + + my (@thinFiles); + + if ($sizePPC == 0) { + # PPC image ends on an alignment boundary, there will be no padding before + # starting the x86 image. + @thinFiles = ($thinPPC->path(), $thinX86->path()); + } + elsif ($sizeX86 == 0 || $sizeX86 > $sizePPC) { + # x86 image ends on an alignment boundary, there will be no padding before + # starting the PPC image, or the x86 image exceeds its alignment boundary + # by more than the PPC image, so there will be less padding if the x86 + # comes first. + @thinFiles = ($thinX86->path(), $thinPPC->path()); + } + else { + # PPC image exceeds its alignment boundary by more than the x86 image, so + # there will be less padding if the PPC comes first. + @thinFiles = ($thinPPC->path(), $thinX86->path()); + } + + my ($isExecutable); + $isExecutable = $sourcePPC->lIsExecutable() || + $sourceX86->lIsExecutable(); + + if (!$gDryRun) { + # Ensure that the file does not yet exist. + + # Set the execute bits (as allowed by the umask) on the new file if any + # execute bit is set on either old file. Yes, it is possible to have + # proper Mach-O files without x-bits: think object files (.o) and static + # archives (.a). + if (!createUniqueFile($targetPath, $isExecutable ? 0777 : 0666)) { + # createUniqueFile printed an error. + unlink(@tempThinFiles); + return 0; + } + } + + # Create the fat file. + if (command($gConfig{'cmd_lipo'}, '-create', @thinFiles, + '-output', $targetPath) != 0) { + unlink(@tempThinFiles, $targetPath); + return complain(1, 'lipo create fat failed for:', + @thinFiles, + $targetPath); + } + + unlink(@tempThinFiles); + + if (!$gDryRun) { + # lipo seems to think that it's free to set its own file modes that + # ignore the umask, which is bogus when the rest of this script + # respects the umask. + if (!chmod(($isExecutable ? 0777 : 0666) & ~umask(), $targetPath)) { + complain(1, 'makeUniversalFile: chmod: '.$!.' for', + $targetPath); + unlink($targetPath); + return 0; + } + } + + return 1; +} + +# makeUniversalInternal($isToplevel, $filePPC, $fileX86, $fileTargetPath) +# +# Given FileAttrCache objects $filePPC and $fileX86, compares filetypes +# and performs the appropriate action to produce a universal file at +# path string $fileTargetPath. $isToplevel should be true if this is +# the recursive base and false otherwise; this controls cleanup behavior +# (cleanup is only performed at the base, because cleanup itself is +# recursive). +# +# This handles regular files by determining whether they are Mach-O files +# and calling makeUniversalFile if so and copyIfIdentical otherwise. Symbolic +# links are handled directly in this function by ensuring that the source link +# targets are identical and creating a new link with the same target +# at $fileTargetPath. Directories are handled by calling +# makeUniversalDirectory. +# +# One of $filePPC and $fileX86 is permitted to be undef. In that case, +# the defined source file is copied directly to the target if a regular +# file, and symlinked appropriately if a symbolic link. This facilitates +# use of $gOnlyOne = 'copy', although no $gOnlyOne checks are made in this +# function, they are all handled in makeUniversalDirectory. +# +# Returns true on success. Returns false on failure, including failures +# in other functions called. +sub makeUniversalInternal($$$$) { + my ($filePPC, $fileTargetPath, $fileX86, $isToplevel); + ($isToplevel, $filePPC, $fileX86, $fileTargetPath) = @_; + + my ($typePPC, $typeX86); + if (defined($filePPC) && !defined($typePPC = $filePPC->lstatType())) { + return complain(1, 'makeUniversal: lstat ppc: '.$!.' for:', + $filePPC->path()); + } + if (defined($fileX86) && !defined($typeX86 = $fileX86->lstatType())) { + return complain(1, 'makeUniversal: lstat x86: '.$!.' for:', + $fileX86->path()); + } + + if (defined($filePPC) && defined($fileX86) && $typePPC != $typeX86) { + return complain(1, 'makeUniversal: incompatible types:', + $filePPC->path(), + $fileX86->path()); + } + + # $aSourceFile will contain a FileAttrCache object that will return + # the correct type data. It's used because it's possible for one of + # the two source files to be undefined (indicating a straight copy). + my ($aSourceFile); + if (defined($filePPC)) { + $aSourceFile = $filePPC; + } + else { + $aSourceFile = $fileX86; + } + + if ($aSourceFile->lIsDir()) { + if ($gVerbosity >= 3 || $gDryRun) { + print('mkdir '.(argumentEscape($fileTargetPath))[0]."\n"); + } + if (!$gDryRun && !mkdir($fileTargetPath)) { + return complain(1, 'makeUniversal: mkdir: '.$!.' for:', + $fileTargetPath); + } + + my ($rv); + + if (!($rv = makeUniversalDirectory($filePPC, $fileX86, $fileTargetPath))) { + # makeUniversalDirectory printed an error. + if ($isToplevel) { + command($gConfig{'cmd_rm'},'-rf','--',$fileTargetPath); + } + } + else { + # Touch the directory when leaving it. If unify is being run on an + # .app bundle, the .app might show up without an icon because the + # system might have found the .app before it was completely built. + # Touching it dirties it in LaunchServices' mind. + if ($gVerbosity >= 3) { + print('touch '.(argumentEscape($fileTargetPath))[0]."\n"); + } + utime(undef, undef, $fileTargetPath); + } + + return $rv; + } + elsif ($aSourceFile->lIsSymLink()) { + my ($linkPPC, $linkX86); + if (defined($filePPC) && !defined($linkPPC=readlink($filePPC->path()))) { + return complain(1, 'makeUniversal: readlink ppc: '.$!.' for:', + $filePPC->path()); + } + if (defined($fileX86) && !defined($linkX86=readlink($fileX86->path()))) { + return complain(1, 'makeUniversal: readlink x86: '.$!.' for:', + $fileX86->path()); + } + if (defined($filePPC) && defined($fileX86) && $linkPPC ne $linkX86) { + return complain(1, 'makeUniversal: symbolic links differ:', + $filePPC->path(), + $fileX86->path()); + } + + # $aLink here serves the same purpose as $aSourceFile in the enclosing + # block: it refers to the target of the symbolic link, whether there + # is one valid source or two. + my ($aLink); + if (defined($linkPPC)) { + $aLink = $linkPPC; + } + else { + $aLink = $linkX86; + } + + if ($gVerbosity >= 3 || $gDryRun) { + print('ln -s '. + join(' ',argumentEscape($aLink, $fileTargetPath))."\n"); + } + if (!$gDryRun && !symlink($aLink, $fileTargetPath)) { + return complain(1, 'makeUniversal: symlink: '.$!.' for:', + $aLink, + $fileTargetPath); + } + + return 1; + } + elsif($aSourceFile->lIsRegularFile()) { + my ($machPPC, $machX86, $fileName); + if (!defined($filePPC) || !defined($fileX86)) { + # One of the source files isn't present. The right thing to do is + # to just copy what does exist straight over, so skip Mach-O checks. + $machPPC = 0; + $machX86 = 0; + if (defined($filePPC)) { + $fileName = $filePPC; + } elsif (defined($fileX86)) { + $fileName = $fileX86; + } else { + complain(1, "The file must exist in at least one directory"); + exit(1); + } + } + else { + # both files exist, pick the name of one. + $fileName = $fileX86; + if (!defined($machPPC=$filePPC->isMachO())) { + return complain(1, 'makeUniversal: isFileMachO ppc failed for:', + $filePPC->path()); + } + if (!defined($machX86=$fileX86->isMachO())) { + return complain(1, 'makeUniversal: isFileMachO x86 failed for:', + $fileX86->path()); + } + } + + if ($machPPC != $machX86) { + return complain(1, 'makeUniversal: variant Mach-O attributes:', + $filePPC->path(), + $fileX86->path()); + } + + if ($machPPC) { + # makeUniversalFile will print an error if it fails. + return makeUniversalFile($filePPC, $fileX86, $fileTargetPath); + } + + if (grep { $fileName->path() =~ m/$_/; } @gSortMatches) { + # Regular files, but should be compared with sorting first. + # copyIfIdenticalWhenSorted will print an error if it fails. + return copyIfIdenticalWhenSorted($filePPC, $fileX86, $fileTargetPath); + } + + # Regular file. copyIfIdentical will print an error if it fails. + return copyIfIdentical($filePPC, $fileX86, $fileTargetPath); + } + + # Special file, don't know how to handle. + return complain(1, 'makeUniversal: cannot handle special file:', + $filePPC->path(), + $fileX86->path()); +} + +# usage() +# +# Give the user a hand. +sub usage() { + print STDERR ( +"usage: unify <ppc-path> <x86-path> <universal-path>\n". +" [--dry-run] (print what would be done)\n". +" [--only-one <action>] (skip, copy, fail; default=copy)\n". +" [--verbosity <level>] (0, 1, 2, 3; default=2)\n"); + return; +} + +# readZipCRCs($zipFile) +# +# $zipFile is the pathname to a zip file whose directory will be read. +# A reference to a hash is returned, with the member pathnames from the +# zip file as keys, and reasonably unique identifiers as values. The +# format of the values is not specified exactly, but does include the +# member CRCs and sizes and differentiates between files and directories. +# It specifically does not distinguish between modification times. On +# failure, prints a message and returns undef. +sub readZipCRCs($) { + my ($zipFile); + ($zipFile) = @_; + + my ($ze, $zip); + $zip = Archive::Zip->new(); + + if (($ze = $zip->read($zipFile)) != AZ_OK) { + complain(1, 'readZipCRCs: read error '.$ze.' for:', + $zipFile); + return undef; + } + + my ($member, %memberCRCs, @memberList); + %memberCRCs = (); + @memberList = $zip->members(); + + foreach $member (@memberList) { + # Take a few of the attributes that identify the file and stuff them into + # the members hash. Directories will show up with size 0 and crc32 0, + # so isDirectory() is used to distinguish them from empty files. + $memberCRCs{$member->fileName()} = join(',', $member->isDirectory() ? 1 : 0, + $member->uncompressedSize(), + $member->crc32String()); + } + + return {%memberCRCs}; +} + +{ + # FileAttrCache allows various attributes about a file to be cached + # so that if they are needed again after first use, no system calls + # will be made and the program won't need to hit the disk. + + package FileAttrCache; + + # from /usr/include/mach-o/loader.h + use constant MH_MAGIC => 0xfeedface; + use constant MH_CIGAM => 0xcefaedfe; + use constant MH_MAGIC_64 => 0xfeedfacf; + use constant MH_CIGAM_64 => 0xcffaedfe; + + use Fcntl(':DEFAULT', ':mode'); + + # FileAttrCache->new($path) + # + # Creates a new FileAttrCache object for the file at path $path and + # returns it. The cache is not primed at creation time, values are + # fetched lazily as they are needed. + sub new($$) { + my ($class, $path, $proto, $this); + ($proto, $path) = @_; + if (!($class = ref($proto))) { + $class = $proto; + } + $this = { + 'path' => $path, + 'lstat' => undef, + 'lstatErrno' => 0, + 'lstatInit' => 0, + 'magic' => undef, + 'magic2' => undef, + 'magicErrno' => 0, + 'magicErrMsg' => undef, + 'magicInit' => 0, + 'stat' => undef, + 'statErrno' => 0, + 'statInit' => 0, + }; + bless($this, $class); + return($this); + } + + # $FileAttrCache->isFat() + # + # Returns true if the file is a fat Mach-O file, false if it's not, and + # undef if an error occurs. See /usr/include/mach-o/fat.h. + sub isFat($) { + my ($magic, $magic2, $this); + ($this) = @_; + + # magic() caches, there's no separate cache because isFat() doesn't hit + # the disk other than by calling magic(). + + if (!defined($magic = $this->magic())) { + return undef; + } + $magic2 = $this->magic2(); + + # We have to sanity check the second four bytes, because Java class + # files use the same magic number as Mach-O fat binaries. + # This logic is adapted from file(1), which says that Mach-O uses + # these bytes to count the number of architectures within, while + # Java uses it for a version number. Conveniently, there are only + # 18 labelled Mach-O architectures, and Java's first released + # class format used the version 43.0. + if ($magic == 0xcafebabe && $magic2 < 20) { + return 1; + } + + return 0; + } + + # $FileAttrCache->isMachO() + # + # Returns true if the file is a Mach-O image (including a fat file), false + # if it's not, and undef if an error occurs. See + # /usr/include/mach-o/loader.h and /usr/include/mach-o/fat.h. + sub isMachO($) { + my ($magic, $this); + ($this) = @_; + + # magic() caches, there's no separate cache because isMachO() doesn't hit + # the disk other than by calling magic(). + + if (!defined($magic = $this->magic())) { + return undef; + } + + # Accept Mach-O fat files or Mach-O thin files of either endianness. + if ($magic == MH_MAGIC || + $magic == MH_CIGAM || + $magic == MH_MAGIC_64 || + $magic == MH_CIGAM_64 || + $this->isFat()) { + return 1; + } + + return 0; + } + + # $FileAttrCache->isZip() + # + # Returns true if the file is a zip file, false if it's not, and undef if + # an error occurs. See http://www.pkware.com/business_and_developers/developer/popups/appnote.txt . + sub isZip($) { + my ($magic, $this); + ($this) = @_; + + # magic() caches, there's no separate cache because isFat() doesn't hit + # the disk other than by calling magic(). + + if (!defined($magic = $this->magic())) { + return undef; + } + + if ($magic == 0x504b0304) { + return 1; + } + + return 0; + } + + # $FileAttrCache->lIsExecutable() + # + # Wraps $FileAttrCache->lstat(), returning true if the file is has any, + # execute bit set, false if none are set, or undef if an error occurs. + # On error, $! is set to lstat's errno. + sub lIsExecutable($) { + my ($mode, $this); + ($this) = @_; + + if (!defined($mode = $this->lstatMode())) { + return undef; + } + + return $mode & (S_IXUSR | S_IXGRP | S_IXOTH); + } + + # $FileAttrCache->lIsDir() + # + # Wraps $FileAttrCache->lstat(), returning true if the file is a directory, + # false if it isn't, or undef if an error occurs. Because lstat is used, + # this will return false even if the file is a symlink pointing to a + # directory. On error, $! is set to lstat's errno. + sub lIsDir($) { + my ($type, $this); + ($this) = @_; + + if (!defined($type = $this->lstatType())) { + return undef; + } + + return S_ISDIR($type); + } + + # $FileAttrCache->lIsRegularFile() + # + # Wraps $FileAttrCache->lstat(), returning true if the file is a regular, + # file, false if it isn't, or undef if an error occurs. Because lstat is + # used, this will return false even if the file is a symlink pointing to a + # regular file. On error, $! is set to lstat's errno. + sub lIsRegularFile($) { + my ($type, $this); + ($this) = @_; + + if (!defined($type = $this->lstatType())) { + return undef; + } + + return S_ISREG($type); + } + + # $FileAttrCache->lIsSymLink() + # + # Wraps $FileAttrCache->lstat(), returning true if the file is a symbolic, + # link, false if it isn't, or undef if an error occurs. On error, $! is + # set to lstat's errno. + sub lIsSymLink($) { + my ($type, $this); + ($this) = @_; + + if (!defined($type = $this->lstatType())) { + return undef; + } + + return S_ISLNK($type); + } + + # $FileAttrCache->lstat() + # + # Wraps the lstat system call, providing a cache to speed up multiple + # lstat calls for the same file. See lstat(2) and lstat in perlfunc(1). + sub lstat($) { + my (@stat, $this); + ($this) = @_; + + # Use the cached lstat result. + if ($$this{'lstatInit'}) { + if (defined($$this{'lstatErrno'})) { + $! = $$this{'lstatErrno'}; + } + return @{$$this{'lstat'}}; + } + $$this{'lstatInit'} = 1; + + if (!(@stat = CORE::lstat($$this{'path'}))) { + $$this{'lstatErrno'} = $!; + } + + $$this{'lstat'} = [@stat]; + return @stat; + } + + # $FileAttrCache->lstatMode() + # + # Wraps $FileAttrCache->lstat(), returning the mode bits from the st_mode + # field, or undef if an error occurs. On error, $! is set to lstat's + # errno. + sub lstatMode($) { + my (@stat, $this); + ($this) = @_; + + if (!(@stat = $this->lstat())) { + return undef; + } + + return S_IMODE($stat[2]); + } + + # $FileAttrCache->lstatType() + # + # Wraps $FileAttrCache->lstat(), returning the type bits from the st_mode + # field, or undef if an error occurs. On error, $! is set to lstat's + # errno. + sub lstatType($) { + my (@stat, $this); + ($this) = @_; + + if (!(@stat = $this->lstat())) { + return undef; + } + + return S_IFMT($stat[2]); + } + + # $FileAttrCache->magic() + # + # Returns the "magic number" for the file by reading its first four bytes + # as a big-endian unsigned 32-bit integer and returning the result. If an + # error occurs, returns undef and prints diagnostic messages to stderr. If + # the file is shorter than 32 bits, returns -1. A cache is provided to + # speed multiple magic calls for the same file. + sub magic($) { + my ($this); + ($this) = @_; + + # Use the cached magic result. + if ($$this{'magicInit'}) { + if (defined($$this{'magicErrno'})) { + if (defined($$this{'magicErrMsg'})) { + complain(1, 'FileAttrCache::magic: '.$$this{'magicErrMsg'}.' for:', + $$this{'path'}); + } + $! = $$this{'magicErrno'}; + } + return $$this{'magic'}; + } + + $$this{'magicInit'} = 1; + + my ($fh); + if (!sysopen($fh, $$this{'path'}, O_RDONLY)) { + $$this{'magicErrno'} = $!; + $$this{'magicErrMsg'} = 'open "'.$$this{'path'}.'": '.$!; + complain(1, 'FileAttrCache::magic: '.$$this{'magicErrMsg'}.' for:', + $$this{'path'}); + return undef; + } + + $! = 0; + my ($bytes, $magic, $bytes2, $magic2); + if (!defined($bytes = sysread($fh, $magic, 4))) { + $$this{'magicErrno'} = $!; + $$this{'magicErrMsg'} = 'read "'.$$this{'path'}.'": '.$!; + complain(1, 'FileAttrCache::magic: '.$$this{'magicErrMsg'}.' for:', + $$this{'path'}); + close($fh); + return undef; + } + else { + $bytes2 = sysread($fh, $magic2, 4); + } + + close($fh); + + if ($bytes != 4) { + # The file is too short, didn't read a magic number. This isn't really + # an error. Return an unlikely value. + $$this{'magic'} = -1; + $$this{'magic2'} = -1; + return -1; + } + if ($bytes2 != 4) { + # File is too short to read a second 4 bytes. + $magic2 = -1; + } + + $$this{'magic'} = unpack('N', $magic); + $$this{'magic2'} = unpack('N', $magic2); + return $$this{'magic'}; + } + + # $FileAttrCache->magic2() + # + # Returns the second four bytes of the file as a 32-bit little endian number. + # See magic(), above for more info. + sub magic2($) { + my ($this); + ($this) = @_; + + # we do the actual work (and cache it) in magic(). + if (!$$this{'magicInit'}) { + my $magic = $$this->magic(); + } + + return $$this{'magic2'}; + } + + # $FileAttrCache->path() + # + # Returns the file's pathname. + sub path($) { + my ($this); + ($this) = @_; + return $$this{'path'}; + } + + # $FileAttrCache->stat() + # + # Wraps the stat system call, providing a cache to speed up multiple + # stat calls for the same file. If lstat() has already been called and + # the file is not a symbolic link, the cached lstat() result will be used. + # See stat(2) and lstat in perlfunc(1). + sub stat($) { + my (@stat, $this); + ($this) = @_; + + # Use the cached stat result. + if ($$this{'statInit'}) { + if (defined($$this{'statErrno'})) { + $! = $$this{'statErrno'}; + } + return @{$$this{'stat'}}; + } + + $$this{'statInit'} = 1; + + # If lstat has already been called, and the file isn't a symbolic link, + # use the cached lstat result. + if ($$this{'lstatInit'} && !$$this{'lstatErrno'} && + !S_ISLNK(${$$this{'lstat'}}[2])) { + $$this{'stat'} = $$this{'lstat'}; + return @{$$this{'stat'}}; + } + + if (!(@stat = CORE::stat($$this{'path'}))) { + $$this{'statErrno'} = $!; + } + + $$this{'stat'} = [@stat]; + return @stat; + } + + # $FileAttrCache->statSize() + # + # Wraps $FileAttrCache->stat(), returning the st_size field, or undef + # undef if an error occurs. On error, $! is set to stat's errno. + sub statSize($) { + my (@stat, $this); + ($this) = @_; + + if (!(@stat = $this->lstat())) { + return undef; + } + + return $stat[7]; + } +} diff --git a/build/mobile/b2gautomation.py b/build/mobile/b2gautomation.py new file mode 100644 index 000000000..047a19128 --- /dev/null +++ b/build/mobile/b2gautomation.py @@ -0,0 +1,373 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at http://mozilla.org/MPL/2.0/. + +import mozcrash +import threading +import os +import Queue +import re +import shutil +import tempfile +import time +import traceback + +from automation import Automation +from devicemanager import NetworkTools +from mozprocess import ProcessHandlerMixin + + +class StdOutProc(ProcessHandlerMixin): + """Process handler for b2g which puts all output in a Queue. + """ + + def __init__(self, cmd, queue, **kwargs): + self.queue = queue + kwargs.setdefault('processOutputLine', []).append(self.handle_output) + ProcessHandlerMixin.__init__(self, cmd, **kwargs) + + def handle_output(self, line): + self.queue.put_nowait(line) + + +class B2GRemoteAutomation(Automation): + _devicemanager = None + + def __init__(self, deviceManager, appName='', remoteLog=None, + marionette=None, context_chrome=True): + self._devicemanager = deviceManager + self._appName = appName + self._remoteProfile = None + self._remoteLog = remoteLog + self.marionette = marionette + self.context_chrome = context_chrome + self._is_emulator = False + self.test_script = None + self.test_script_args = None + + # Default our product to b2g + self._product = "b2g" + self.lastTestSeen = "b2gautomation.py" + # Default log finish to mochitest standard + self.logFinish = 'INFO SimpleTest FINISHED' + Automation.__init__(self) + + def setEmulator(self, is_emulator): + self._is_emulator = is_emulator + + def setDeviceManager(self, deviceManager): + self._devicemanager = deviceManager + + def setAppName(self, appName): + self._appName = appName + + def setRemoteProfile(self, remoteProfile): + self._remoteProfile = remoteProfile + + def setProduct(self, product): + self._product = product + + def setRemoteLog(self, logfile): + self._remoteLog = logfile + + def installExtension(self, extensionSource, profileDir, extensionID=None): + # Bug 827504 - installing special-powers extension separately causes problems in B2G + if extensionID != "special-powers@mozilla.org": + Automation.installExtension(self, extensionSource, profileDir, extensionID) + + # Set up what we need for the remote environment + def environment(self, env=None, xrePath=None, crashreporter=True): + # Because we are running remote, we don't want to mimic the local env + # so no copying of os.environ + if env is None: + env = {} + + if crashreporter: + env['MOZ_CRASHREPORTER'] = '1' + env['MOZ_CRASHREPORTER_NO_REPORT'] = '1' + + # We always hide the results table in B2G; it's much slower if we don't. + env['MOZ_HIDE_RESULTS_TABLE'] = '1' + return env + + def waitForNet(self): + active = False + time_out = 0 + while not active and time_out < 40: + data = self._devicemanager._runCmd(['shell', '/system/bin/netcfg']).stdout.readlines() + data.pop(0) + for line in data: + if (re.search(r'UP\s+(?:[0-9]{1,3}\.){3}[0-9]{1,3}', line)): + active = True + break + time_out += 1 + time.sleep(1) + return active + + def checkForCrashes(self, directory, symbolsPath): + crashed = False + remote_dump_dir = self._remoteProfile + '/minidumps' + print "checking for crashes in '%s'" % remote_dump_dir + if self._devicemanager.dirExists(remote_dump_dir): + local_dump_dir = tempfile.mkdtemp() + self._devicemanager.getDirectory(remote_dump_dir, local_dump_dir) + try: + crashed = mozcrash.check_for_crashes(local_dump_dir, symbolsPath, test_name=self.lastTestSeen) + except: + traceback.print_exc() + finally: + shutil.rmtree(local_dump_dir) + self._devicemanager.removeDir(remote_dump_dir) + return crashed + + def buildCommandLine(self, app, debuggerInfo, profileDir, testURL, extraArgs): + # if remote profile is specified, use that instead + if (self._remoteProfile): + profileDir = self._remoteProfile + + cmd, args = Automation.buildCommandLine(self, app, debuggerInfo, profileDir, testURL, extraArgs) + + return app, args + + def getLanIp(self): + nettools = NetworkTools() + return nettools.getLanIp() + + def waitForFinish(self, proc, utilityPath, timeout, maxTime, startTime, + debuggerInfo, symbolsPath): + """ Wait for tests to finish (as evidenced by a signature string + in logcat), or for a given amount of time to elapse with no + output. + """ + timeout = timeout or 120 + responseDueBy = time.time() + timeout + while True: + currentlog = proc.stdout + if currentlog: + responseDueBy = time.time() + timeout + print currentlog + # Match the test filepath from the last TEST-START line found in the new + # log content. These lines are in the form: + # ... INFO TEST-START | /filepath/we/wish/to/capture.html\n + testStartFilenames = re.findall(r"TEST-START \| ([^\s]*)", currentlog) + if testStartFilenames: + self.lastTestSeen = testStartFilenames[-1] + if hasattr(self, 'logFinish') and self.logFinish in currentlog: + return 0 + else: + if time.time() > responseDueBy: + self.log.info("TEST-UNEXPECTED-FAIL | %s | application timed " + "out after %d seconds with no output", + self.lastTestSeen, int(timeout)) + return 1 + + def getDeviceStatus(self, serial=None): + # Get the current status of the device. If we know the device + # serial number, we look for that, otherwise we use the (presumably + # only) device shown in 'adb devices'. + serial = serial or self._devicemanager._deviceSerial + status = 'unknown' + + for line in self._devicemanager._runCmd(['devices']).stdout.readlines(): + result = re.match('(.*?)\t(.*)', line) + if result: + thisSerial = result.group(1) + if not serial or thisSerial == serial: + serial = thisSerial + status = result.group(2) + + return (serial, status) + + def restartB2G(self): + # TODO hangs in subprocess.Popen without this delay + time.sleep(5) + self._devicemanager._checkCmd(['shell', 'stop', 'b2g']) + # Wait for a bit to make sure B2G has completely shut down. + time.sleep(10) + self._devicemanager._checkCmd(['shell', 'start', 'b2g']) + if self._is_emulator: + self.marionette.emulator.wait_for_port() + + def rebootDevice(self): + # find device's current status and serial number + serial, status = self.getDeviceStatus() + + # reboot! + self._devicemanager._runCmd(['shell', '/system/bin/reboot']) + + # The above command can return while adb still thinks the device is + # connected, so wait a little bit for it to disconnect from adb. + time.sleep(10) + + # wait for device to come back to previous status + print 'waiting for device to come back online after reboot' + start = time.time() + rserial, rstatus = self.getDeviceStatus(serial) + while rstatus != 'device': + if time.time() - start > 120: + # device hasn't come back online in 2 minutes, something's wrong + raise Exception("Device %s (status: %s) not back online after reboot" % (serial, rstatus)) + time.sleep(5) + rserial, rstatus = self.getDeviceStatus(serial) + print 'device:', serial, 'status:', rstatus + + def Process(self, cmd, stdout=None, stderr=None, env=None, cwd=None): + # On a desktop or fennec run, the Process method invokes a gecko + # process in which to the tests. For B2G, we simply + # reboot the device (which was configured with a test profile + # already), wait for B2G to start up, and then navigate to the + # test url using Marionette. There doesn't seem to be any way + # to pass env variables into the B2G process, but this doesn't + # seem to matter. + + # reboot device so it starts up with the mochitest profile + # XXX: We could potentially use 'stop b2g' + 'start b2g' to achieve + # a similar effect; will see which is more stable while attempting + # to bring up the continuous integration. + if not self._is_emulator: + self.rebootDevice() + time.sleep(5) + #wait for wlan to come up + if not self.waitForNet(): + raise Exception("network did not come up, please configure the network" + + " prior to running before running the automation framework") + + # stop b2g + self._devicemanager._runCmd(['shell', 'stop', 'b2g']) + time.sleep(5) + + # relaunch b2g inside b2g instance + instance = self.B2GInstance(self._devicemanager, env=env) + + time.sleep(5) + + # Set up port forwarding again for Marionette, since any that + # existed previously got wiped out by the reboot. + if not self._is_emulator: + self._devicemanager._checkCmd(['forward', + 'tcp:%s' % self.marionette.port, + 'tcp:%s' % self.marionette.port]) + + if self._is_emulator: + self.marionette.emulator.wait_for_port() + else: + time.sleep(5) + + # start a marionette session + session = self.marionette.start_session() + if 'b2g' not in session: + raise Exception("bad session value %s returned by start_session" % session) + + if self._is_emulator: + # Disable offline status management (bug 777145), otherwise the network + # will be 'offline' when the mochitests start. Presumably, the network + # won't be offline on a real device, so we only do this for emulators. + self.marionette.set_context(self.marionette.CONTEXT_CHROME) + self.marionette.execute_script(""" + Components.utils.import("resource://gre/modules/Services.jsm"); + Services.io.manageOfflineStatus = false; + Services.io.offline = false; + """) + + if self.context_chrome: + self.marionette.set_context(self.marionette.CONTEXT_CHROME) + else: + self.marionette.set_context(self.marionette.CONTEXT_CONTENT) + + # run the script that starts the tests + if self.test_script: + if os.path.isfile(self.test_script): + script = open(self.test_script, 'r') + self.marionette.execute_script(script.read(), script_args=self.test_script_args) + script.close() + elif isinstance(self.test_script, basestring): + self.marionette.execute_script(self.test_script, script_args=self.test_script_args) + else: + # assumes the tests are started on startup automatically + pass + + return instance + + # be careful here as this inner class doesn't have access to outer class members + class B2GInstance(object): + """Represents a B2G instance running on a device, and exposes + some process-like methods/properties that are expected by the + automation. + """ + + def __init__(self, dm, env=None): + self.dm = dm + self.env = env or {} + self.stdout_proc = None + self.queue = Queue.Queue() + + # Launch b2g in a separate thread, and dump all output lines + # into a queue. The lines in this queue are + # retrieved and returned by accessing the stdout property of + # this class. + cmd = [self.dm._adbPath] + if self.dm._deviceSerial: + cmd.extend(['-s', self.dm._deviceSerial]) + cmd.append('shell') + for k, v in self.env.iteritems(): + cmd.append("%s=%s" % (k, v)) + cmd.append('/system/bin/b2g.sh') + proc = threading.Thread(target=self._save_stdout_proc, args=(cmd, self.queue)) + proc.daemon = True + proc.start() + + def _save_stdout_proc(self, cmd, queue): + self.stdout_proc = StdOutProc(cmd, queue) + self.stdout_proc.run() + if hasattr(self.stdout_proc, 'processOutput'): + self.stdout_proc.processOutput() + self.stdout_proc.wait() + self.stdout_proc = None + + @property + def pid(self): + # a dummy value to make the automation happy + return 0 + + @property + def stdout(self): + # Return any lines in the queue used by the + # b2g process handler. + lines = [] + while True: + try: + lines.append(self.queue.get_nowait()) + except Queue.Empty: + break + return '\n'.join(lines) + + def wait(self, timeout=None): + # this should never happen + raise Exception("'wait' called on B2GInstance") + + def kill(self): + # this should never happen + raise Exception("'kill' called on B2GInstance") + + +class B2GDesktopAutomation(Automation): + + def buildCommandLine(self, app, debuggerInfo, profileDir, testURL, extraArgs): + """ build the application command line """ + + cmd = os.path.abspath(app) + args = [] + + if debuggerInfo: + args.extend(debuggerInfo["args"]) + args.append(cmd) + cmd = os.path.abspath(debuggerInfo["path"]) + + if self.IS_MAC: + args.append("-foreground") + + profileDirectory = profileDir + "/" + + args.extend(("-profile", profileDirectory)) + args.extend(extraArgs) + return cmd, args diff --git a/build/mobile/remoteautomation.py b/build/mobile/remoteautomation.py new file mode 100644 index 000000000..973d6a365 --- /dev/null +++ b/build/mobile/remoteautomation.py @@ -0,0 +1,314 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +import time +import re +import os +import tempfile +import shutil +import subprocess + +from automation import Automation +from devicemanager import NetworkTools, DMError + +# signatures for logcat messages that we don't care about much +fennecLogcatFilters = [ "The character encoding of the HTML document was not declared", + "Use of Mutation Events is deprecated. Use MutationObserver instead." ] + +class RemoteAutomation(Automation): + _devicemanager = None + + def __init__(self, deviceManager, appName = '', remoteLog = None): + self._devicemanager = deviceManager + self._appName = appName + self._remoteProfile = None + self._remoteLog = remoteLog + + # Default our product to fennec + self._product = "fennec" + self.lastTestSeen = "remoteautomation.py" + Automation.__init__(self) + + def setDeviceManager(self, deviceManager): + self._devicemanager = deviceManager + + def setAppName(self, appName): + self._appName = appName + + def setRemoteProfile(self, remoteProfile): + self._remoteProfile = remoteProfile + + def setProduct(self, product): + self._product = product + + def setRemoteLog(self, logfile): + self._remoteLog = logfile + + # Set up what we need for the remote environment + def environment(self, env = None, xrePath = None, crashreporter = True): + # Because we are running remote, we don't want to mimic the local env + # so no copying of os.environ + if env is None: + env = {} + + # Except for the mochitest results table hiding option, which isn't + # passed to runtestsremote.py as an actual option, but through the + # MOZ_CRASHREPORTER_DISABLE environment variable. + if 'MOZ_HIDE_RESULTS_TABLE' in os.environ: + env['MOZ_HIDE_RESULTS_TABLE'] = os.environ['MOZ_HIDE_RESULTS_TABLE'] + + if crashreporter: + env['MOZ_CRASHREPORTER_NO_REPORT'] = '1' + env['MOZ_CRASHREPORTER'] = '1' + else: + env['MOZ_CRASHREPORTER_DISABLE'] = '1' + + return env + + def waitForFinish(self, proc, utilityPath, timeout, maxTime, startTime, debuggerInfo, symbolsPath): + """ Wait for tests to finish (as evidenced by the process exiting), + or for maxTime elapse, in which case kill the process regardless. + """ + # maxTime is used to override the default timeout, we should honor that + status = proc.wait(timeout = maxTime) + self.lastTestSeen = proc.getLastTestSeen + + if (status == 1 and self._devicemanager.getTopActivity() == proc.procName): + # Then we timed out, make sure Fennec is dead + if maxTime: + print "TEST-UNEXPECTED-FAIL | %s | application ran for longer than " \ + "allowed maximum time of %s seconds" % (self.lastTestSeen, maxTime) + else: + print "TEST-UNEXPECTED-FAIL | %s | application ran for longer than " \ + "allowed maximum time" % (self.lastTestSeen) + proc.kill() + + return status + + def checkForJavaException(self, logcat): + found_exception = False + for i, line in enumerate(logcat): + if "REPORTING UNCAUGHT EXCEPTION" in line or "FATAL EXCEPTION" in line: + # Strip away the date, time, logcat tag and pid from the next two lines and + # concatenate the remainder to form a concise summary of the exception. + # + # For example: + # + # 01-30 20:15:41.937 E/GeckoAppShell( 1703): >>> REPORTING UNCAUGHT EXCEPTION FROM THREAD 9 ("GeckoBackgroundThread") + # 01-30 20:15:41.937 E/GeckoAppShell( 1703): java.lang.NullPointerException + # 01-30 20:15:41.937 E/GeckoAppShell( 1703): at org.mozilla.gecko.GeckoApp$21.run(GeckoApp.java:1833) + # 01-30 20:15:41.937 E/GeckoAppShell( 1703): at android.os.Handler.handleCallback(Handler.java:587) + # 01-30 20:15:41.937 E/GeckoAppShell( 1703): at android.os.Handler.dispatchMessage(Handler.java:92) + # 01-30 20:15:41.937 E/GeckoAppShell( 1703): at android.os.Looper.loop(Looper.java:123) + # 01-30 20:15:41.937 E/GeckoAppShell( 1703): at org.mozilla.gecko.util.GeckoBackgroundThread.run(GeckoBackgroundThread.java:31) + # + # -> java.lang.NullPointerException at org.mozilla.gecko.GeckoApp$21.run(GeckoApp.java:1833) + found_exception = True + logre = re.compile(r".*\): \t?(.*)") + m = logre.search(logcat[i+1]) + if m and m.group(1): + top_frame = m.group(1) + m = logre.search(logcat[i+2]) + if m and m.group(1): + top_frame = top_frame + m.group(1) + print "PROCESS-CRASH | java-exception | %s" % top_frame + break + return found_exception + + def deleteANRs(self): + # delete ANR traces.txt file; usually need root permissions + traces = "/data/anr/traces.txt" + try: + self._devicemanager.shellCheckOutput(['rm', traces], root=True) + except DMError: + print "Error deleting %s" % traces + pass + + def checkForANRs(self): + traces = "/data/anr/traces.txt" + if self._devicemanager.fileExists(traces): + try: + t = self._devicemanager.pullFile(traces) + print "Contents of %s:" % traces + print t + # Once reported, delete traces + self.deleteANRs() + except DMError: + print "Error pulling %s" % traces + pass + else: + print "%s not found" % traces + + def checkForCrashes(self, directory, symbolsPath): + self.checkForANRs() + + logcat = self._devicemanager.getLogcat(filterOutRegexps=fennecLogcatFilters) + javaException = self.checkForJavaException(logcat) + if javaException: + return True + + # If crash reporting is disabled (MOZ_CRASHREPORTER!=1), we can't say + # anything. + if not self.CRASHREPORTER: + return False + + try: + dumpDir = tempfile.mkdtemp() + remoteCrashDir = self._remoteProfile + '/minidumps/' + if not self._devicemanager.dirExists(remoteCrashDir): + # If crash reporting is enabled (MOZ_CRASHREPORTER=1), the + # minidumps directory is automatically created when Fennec + # (first) starts, so its lack of presence is a hint that + # something went wrong. + print "Automation Error: No crash directory (%s) found on remote device" % remoteCrashDir + # Whilst no crash was found, the run should still display as a failure + return True + self._devicemanager.getDirectory(remoteCrashDir, dumpDir) + crashed = Automation.checkForCrashes(self, dumpDir, symbolsPath) + + finally: + try: + shutil.rmtree(dumpDir) + except: + print "WARNING: unable to remove directory: %s" % dumpDir + return crashed + + def buildCommandLine(self, app, debuggerInfo, profileDir, testURL, extraArgs): + # If remote profile is specified, use that instead + if (self._remoteProfile): + profileDir = self._remoteProfile + + # Hack for robocop, if app & testURL == None and extraArgs contains the rest of the stuff, lets + # assume extraArgs is all we need + if app == "am" and extraArgs[0] == "instrument": + return app, extraArgs + + cmd, args = Automation.buildCommandLine(self, app, debuggerInfo, profileDir, testURL, extraArgs) + # Remove -foreground if it exists, if it doesn't this just returns + try: + args.remove('-foreground') + except: + pass +#TODO: figure out which platform require NO_EM_RESTART +# return app, ['--environ:NO_EM_RESTART=1'] + args + return app, args + + def getLanIp(self): + nettools = NetworkTools() + return nettools.getLanIp() + + def Process(self, cmd, stdout = None, stderr = None, env = None, cwd = None): + if stdout == None or stdout == -1 or stdout == subprocess.PIPE: + stdout = self._remoteLog + + return self.RProcess(self._devicemanager, cmd, stdout, stderr, env, cwd) + + # be careful here as this inner class doesn't have access to outer class members + class RProcess(object): + # device manager process + dm = None + def __init__(self, dm, cmd, stdout = None, stderr = None, env = None, cwd = None): + self.dm = dm + self.stdoutlen = 0 + self.lastTestSeen = "remoteautomation.py" + self.proc = dm.launchProcess(cmd, stdout, cwd, env, True) + if (self.proc is None): + if cmd[0] == 'am': + self.proc = stdout + else: + raise Exception("unable to launch process") + exepath = cmd[0] + name = exepath.split('/')[-1] + self.procName = name + # Hack for Robocop: Derive the actual process name from the command line. + # We expect something like: + # ['am', 'instrument', '-w', '-e', 'class', 'org.mozilla.fennec.tests.testBookmark', 'org.mozilla.roboexample.test/android.test.InstrumentationTestRunner'] + # and want to derive 'org.mozilla.fennec'. + if cmd[0] == 'am' and cmd[1] == "instrument": + try: + i = cmd.index("class") + except ValueError: + # no "class" argument -- maybe this isn't robocop? + i = -1 + if (i > 0): + classname = cmd[i+1] + parts = classname.split('.') + try: + i = parts.index("tests") + except ValueError: + # no "tests" component -- maybe this isn't robocop? + i = -1 + if (i > 0): + self.procName = '.'.join(parts[0:i]) + print "Robocop derived process name: "+self.procName + + # Setting timeout at 1 hour since on a remote device this takes much longer + self.timeout = 3600 + # The benefit of the following sleep is unclear; it was formerly 15 seconds + time.sleep(1) + + @property + def pid(self): + pid = self.dm.processExist(self.procName) + # HACK: we should probably be more sophisticated about monitoring + # running processes for the remote case, but for now we'll assume + # that this method can be called when nothing exists and it is not + # an error + if pid is None: + return 0 + return pid + + @property + def stdout(self): + """ Fetch the full remote log file using devicemanager and return just + the new log entries since the last call (as a multi-line string). + """ + if self.dm.fileExists(self.proc): + try: + t = self.dm.pullFile(self.proc) + except DMError: + # we currently don't retry properly in the pullFile + # function in dmSUT, so an error here is not necessarily + # the end of the world + return '' + newLogContent = t[self.stdoutlen:] + self.stdoutlen = len(t) + # Match the test filepath from the last TEST-START line found in the new + # log content. These lines are in the form: + # 1234 INFO TEST-START | /filepath/we/wish/to/capture.html\n + testStartFilenames = re.findall(r"TEST-START \| ([^\s]*)", newLogContent) + if testStartFilenames: + self.lastTestSeen = testStartFilenames[-1] + return newLogContent.strip('\n').strip() + else: + return '' + + @property + def getLastTestSeen(self): + return self.lastTestSeen + + def wait(self, timeout = None): + timer = 0 + interval = 5 + + if timeout == None: + timeout = self.timeout + + while (self.dm.getTopActivity() == self.procName): + t = self.stdout + if t != '': print t + time.sleep(interval) + timer += interval + if (timer > timeout): + break + + # Flush anything added to stdout during the sleep + print self.stdout + + if (timer >= timeout): + return 1 + return 0 + + def kill(self): + self.dm.killProcess(self.procName) diff --git a/build/mobile/robocop/Actions.java.in b/build/mobile/robocop/Actions.java.in new file mode 100644 index 000000000..6ac6bf81b --- /dev/null +++ b/build/mobile/robocop/Actions.java.in @@ -0,0 +1,89 @@ +#filter substitution +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package @ANDROID_PACKAGE_NAME@; +import java.util.List; +import java.util.ArrayList; + +import android.database.Cursor; + +public interface Actions { + + /** Special keys supported by sendSpecialKey() */ + public enum SpecialKey { + DOWN, UP, LEFT, RIGHT, ENTER, MENU, BACK + } + + public interface EventExpecter { + /** Blocks until the event has been received. Subsequent calls will return immediately. */ + public void blockForEvent(); + + /** Blocks until the event has been received and returns data associated with the event. */ + public String blockForEventData(); + + /** + * Blocks until the event has been received, or until the timeout has been exceeded. + * Returns the data associated with the event, if applicable. + */ + public String blockForEventDataWithTimeout(long millis); + + /** Polls to see if the event has been received. Once this returns true, subsequent calls will also return true. */ + public boolean eventReceived(); + + /** Stop listening for events. */ + public void unregisterListener(); + } + + public interface RepeatedEventExpecter extends EventExpecter { + /** Blocks until at least one event has been received, and no events have been received in the last <code>millis</code> milliseconds. */ + public void blockUntilClear(long millis); + } + + /** + * Sends an event to Gecko. + * + * @param geckoEvent The geckoEvent JSONObject's type + */ + void sendGeckoEvent(String geckoEvent, String data); + + /** + * Listens for a gecko event to be sent from the Gecko instance. + * The returned object can be used to test if the event has been + * received. Note that only one event is listened for. + * + * @param geckoEvent The geckoEvent JSONObject's type + */ + RepeatedEventExpecter expectGeckoEvent(String geckoEvent); + + /** + * Listens for a paint event. Note that calling expectPaint() will + * invalidate the event expecters returned from any previous calls + * to expectPaint(); calling any methods on those invalidated objects + * will result in undefined behaviour. + */ + RepeatedEventExpecter expectPaint(); + + /** + * Send a string to the application + * + * @param keysToSend The string to send + */ + void sendKeys(String keysToSend); + + /** + * Send a special keycode to the element + * + * @param key The special key to send + */ + void sendSpecialKey(SpecialKey key); + void sendKeyCode(int keyCode); + + void drag(int startingX, int endingX, int startingY, int endingY); + + /** + * Run a sql query on the specified database + */ + public Cursor querySql(String dbPath, String sql); +} diff --git a/build/mobile/robocop/AndroidManifest.xml.in b/build/mobile/robocop/AndroidManifest.xml.in new file mode 100644 index 000000000..2d0a79e7e --- /dev/null +++ b/build/mobile/robocop/AndroidManifest.xml.in @@ -0,0 +1,19 @@ +#filter substitution +<?xml version="1.0" encoding="utf-8"?> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="org.mozilla.roboexample.test" + android:versionCode="1" + android:versionName="1.0" > + + <uses-sdk android:minSdkVersion="8" /> + + <instrumentation + android:name="@ANDROID_PACKAGE_NAME@.FennecInstrumentationTestRunner" + android:targetPackage="@ANDROID_PACKAGE_NAME@" /> + + <application + android:label="@string/app_name" > + <uses-library android:name="android.test.runner" /> + </application> + +</manifest> diff --git a/build/mobile/robocop/Assert.java.in b/build/mobile/robocop/Assert.java.in new file mode 100644 index 000000000..878ff06b8 --- /dev/null +++ b/build/mobile/robocop/Assert.java.in @@ -0,0 +1,26 @@ +#filter substitution +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package @ANDROID_PACKAGE_NAME@; + +public interface Assert { + void dumpLog(String message); + void dumpLog(String message, Throwable t); + void setLogFile(String filename); + void setTestName(String testName); + void endTest(); + + void ok(boolean condition, String name, String diag); + void is(Object a, Object b, String name); + void isnot(Object a, Object b, String name); + void todo(boolean condition, String name, String diag); + void todo_is(Object a, Object b, String name); + void todo_isnot(Object a, Object b, String name); + void info(String name, String message); + + // robocop-specific asserts + void ispixel(int actual, int r, int g, int b, String name); + void isnotpixel(int actual, int r, int g, int b, String name); +} diff --git a/build/mobile/robocop/Driver.java.in b/build/mobile/robocop/Driver.java.in new file mode 100644 index 000000000..5d606d945 --- /dev/null +++ b/build/mobile/robocop/Driver.java.in @@ -0,0 +1,46 @@ +#filter substitution +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package @ANDROID_PACKAGE_NAME@; + +import java.util.List; +import android.app.Activity; + +public interface Driver { + /** + * Find the first Element using the given method. + * + * @param activity The activity the element belongs to + * @param name The name of the element + * @return The first matching element on the current context, or null if not found. + */ + Element findElement(Activity activity, String name); + + /** + * Sets up scroll handling so that data is received from the extension. + */ + void setupScrollHandling(); + + int getPageHeight(); + int getScrollHeight(); + int getHeight(); + int getGeckoTop(); + int getGeckoLeft(); + int getGeckoWidth(); + int getGeckoHeight(); + + void startFrameRecording(); + int stopFrameRecording(); + + void startCheckerboardRecording(); + float stopCheckerboardRecording(); + + /** + * Get a copy of the painted content region. + * @return A 2-D array of pixels (indexed by y, then x). The pixels + * are in ARGB-8888 format. + */ + PaintedSurface getPaintedSurface(); +} diff --git a/build/mobile/robocop/Element.java.in b/build/mobile/robocop/Element.java.in new file mode 100644 index 000000000..97bb2f873 --- /dev/null +++ b/build/mobile/robocop/Element.java.in @@ -0,0 +1,28 @@ +#filter substitution +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package @ANDROID_PACKAGE_NAME@; + +/** + * Element provides access to a specific UI view (android.view.View). + * See also Driver.findElement(). + */ +public interface Element { + + /** Click on the element's view. Returns true on success. */ + boolean click(); + + /** Returns true if the element is currently displayed */ + boolean isDisplayed(); + + /** + * Returns the text currently displayed on the element, or null + * if the text cannot be retrieved. + */ + String getText(); + + /** Returns the view ID */ + Integer getId(); +} diff --git a/build/mobile/robocop/FennecInstrumentationTestRunner.java.in b/build/mobile/robocop/FennecInstrumentationTestRunner.java.in new file mode 100644 index 000000000..6b86fe457 --- /dev/null +++ b/build/mobile/robocop/FennecInstrumentationTestRunner.java.in @@ -0,0 +1,32 @@ +#filter substitution +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package @ANDROID_PACKAGE_NAME@; + +import android.os.Bundle; +import android.test.InstrumentationTestRunner; +import android.util.Log; + +public class FennecInstrumentationTestRunner extends InstrumentationTestRunner { + private static Bundle sArguments; + + @Override + public void onCreate(Bundle arguments) { + sArguments = arguments; + if (sArguments == null) { + Log.e("Robocop", "FennecInstrumentationTestRunner.onCreate got null bundle"); + } + super.onCreate(arguments); + } + + // unfortunately we have to make this static because test classes that don't extend + // from ActivityInstrumentationTestCase2 can't get a reference to this class. + public static Bundle getArguments() { + if (sArguments == null) { + Log.e("Robocop", "FennecInstrumentationTestCase.getArguments returns null bundle"); + } + return sArguments; + } +} diff --git a/build/mobile/robocop/FennecMochitestAssert.java.in b/build/mobile/robocop/FennecMochitestAssert.java.in new file mode 100644 index 000000000..d13bff21b --- /dev/null +++ b/build/mobile/robocop/FennecMochitestAssert.java.in @@ -0,0 +1,248 @@ +#filter substitution +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package @ANDROID_PACKAGE_NAME@; + +import java.util.LinkedList; +import android.os.SystemClock; + +public class FennecMochitestAssert implements Assert { + private LinkedList<testInfo> mTestList = new LinkedList<testInfo>(); + + // Internal state variables to make logging match up with existing mochitests + private int mLineNumber = 0; + private int mPassed = 0; + private int mFailed = 0; + private int mTodo = 0; + + // Used to write the first line of the test file + private boolean mLogStarted = false; + + // Used to write the test-start/test-end log lines + private String mLogTestName = ""; + + // Measure the time it takes to run test case + private long mStartTime = 0; + + public FennecMochitestAssert() { + } + + /** Write information to a logfile and logcat */ + public void dumpLog(String message) { + FennecNativeDriver.log(FennecNativeDriver.LogLevel.INFO, message); + } + + /** Write information to a logfile and logcat */ + public void dumpLog(String message, Throwable t) { + FennecNativeDriver.log(FennecNativeDriver.LogLevel.INFO, message, t); + } + + /** Set the filename used for dumpLog. */ + public void setLogFile(String filename) { + FennecNativeDriver.setLogFile(filename); + + String message; + if (!mLogStarted) { + dumpLog(Integer.toString(mLineNumber++) + " INFO SimpleTest START"); + mLogStarted = true; + } + + if (mLogTestName != "") { + long diff = SystemClock.uptimeMillis() - mStartTime; + message = Integer.toString(mLineNumber++) + " INFO TEST-END | " + mLogTestName; + message += " | finished in " + diff + "ms"; + dumpLog(message); + mLogTestName = ""; + } + } + + public void setTestName(String testName) { + String[] nameParts = testName.split("\\."); + mLogTestName = nameParts[nameParts.length - 1]; + mStartTime = SystemClock.uptimeMillis(); + + dumpLog(Integer.toString(mLineNumber++) + " INFO TEST-START | " + mLogTestName); + } + + class testInfo { + public boolean mResult; + public String mName; + public String mDiag; + public boolean mTodo; + public testInfo(boolean r, String n, String d, boolean t) { + mResult = r; + mName = n; + mDiag = d; + mTodo = t; + } + + } + + private void _logMochitestResult(testInfo test, String passString, String failString) { + boolean isError = true; + String resultString = failString; + if (test.mResult || test.mTodo) { + isError = false; + } + if (test.mResult) + { + resultString = passString; + } + String diag = test.mName; + if (test.mDiag != null) diag += " - " + test.mDiag; + + String message = Integer.toString(mLineNumber++) + " INFO " + resultString + " | " + mLogTestName + " | " + diag; + dumpLog(message); + + if (test.mTodo) { + mTodo++; + } else if (isError) { + mFailed++; + } else { + mPassed++; + } + if (isError) { + junit.framework.Assert.fail(message); + } + } + + public void endTest() { + String message; + + if (mLogTestName != "") { + long diff = SystemClock.uptimeMillis() - mStartTime; + message = Integer.toString(mLineNumber++) + " INFO TEST-END | " + mLogTestName; + message += " | finished in " + diff + "ms"; + dumpLog(message); + mLogTestName = ""; + } + + message = Integer.toString(mLineNumber++) + " INFO TEST-START | Shutdown"; + dumpLog(message); + message = Integer.toString(mLineNumber++) + " INFO Passed: " + Integer.toString(mPassed); + dumpLog(message); + message = Integer.toString(mLineNumber++) + " INFO Failed: " + Integer.toString(mFailed); + dumpLog(message); + message = Integer.toString(mLineNumber++) + " INFO Todo: " + Integer.toString(mTodo); + dumpLog(message); + message = Integer.toString(mLineNumber++) + " INFO SimpleTest FINISHED"; + dumpLog(message); + } + + public void ok(boolean condition, String name, String diag) { + testInfo test = new testInfo(condition, name, diag, false); + _logMochitestResult(test, "TEST-PASS", "TEST-UNEXPECTED-FAIL"); + mTestList.add(test); + } + + public void is(Object a, Object b, String name) { + boolean pass = checkObjectsEqual(a,b); + ok(pass, name, getEqualString(a,b, pass)); + } + + public void isnot(Object a, Object b, String name) { + boolean pass = checkObjectsNotEqual(a,b); + ok(pass, name, getNotEqualString(a,b,pass)); + } + + public void ispixel(int actual, int r, int g, int b, String name) { + int aAlpha = ((actual >> 24) & 0xFF); + int aR = ((actual >> 16) & 0xFF); + int aG = ((actual >> 8) & 0xFF); + int aB = (actual & 0xFF); + boolean pass = checkPixel(actual, r, g, b); + ok(pass, name, "Color rgba(" + aR + "," + aG + "," + aB + "," + aAlpha + ")" + (pass ? " " : " not") + " close enough to expected rgb(" + r + "," + g + "," + b + ")"); + } + + public void isnotpixel(int actual, int r, int g, int b, String name) { + int aAlpha = ((actual >> 24) & 0xFF); + int aR = ((actual >> 16) & 0xFF); + int aG = ((actual >> 8) & 0xFF); + int aB = (actual & 0xFF); + boolean pass = checkPixel(actual, r, g, b); + ok(!pass, name, "Color rgba(" + aR + "," + aG + "," + aB + "," + aAlpha + ")" + (!pass ? " is" : " is not") + " different enough from rgb(" + r + "," + g + "," + b + ")"); + } + + private boolean checkPixel(int actual, int r, int g, int b) { + // When we read GL pixels the GPU has already processed them and they + // are usually off by a little bit. For example a CSS-color pixel of color #64FFF5 + // was turned into #63FFF7 when it came out of glReadPixels. So in order to compare + // against the expected value, we use a little fuzz factor. For the alpha we just + // make sure it is always 0xFF. There is also bug 691354 which crops up every so + // often just to make our lives difficult. However the individual color components + // should never be off by more than 8. + int aAlpha = ((actual >> 24) & 0xFF); + int aR = ((actual >> 16) & 0xFF); + int aG = ((actual >> 8) & 0xFF); + int aB = (actual & 0xFF); + boolean pass = (aAlpha == 0xFF) /* alpha */ + && (Math.abs(aR - r) <= 8) /* red */ + && (Math.abs(aG - g) <= 8) /* green */ + && (Math.abs(aB - b) <= 8); /* blue */ + if (pass) { + return true; + } else { + return false; + } + } + + public void todo(boolean condition, String name, String diag) { + testInfo test = new testInfo(condition, name, diag, true); + _logMochitestResult(test, "TEST-UNEXPECTED-PASS", "TEST-KNOWN-FAIL"); + mTestList.add(test); + } + + public void todo_is(Object a, Object b, String name) { + boolean pass = checkObjectsEqual(a,b); + todo(pass, name, getEqualString(a,b,pass)); + } + + public void todo_isnot(Object a, Object b, String name) { + boolean pass = checkObjectsNotEqual(a,b); + todo(pass, name, getNotEqualString(a,b,pass)); + } + + private boolean checkObjectsEqual(Object a, Object b) { + if (a == null || b == null) { + if (a == null && b == null) { + return true; + } + return false; + } else { + return a.equals(b); + } + } + + private String getEqualString(Object a, Object b, boolean pass) { + if (pass) { + return a + " should equal " + b; + } + return "got " + a + ", expected " + b; + } + + private boolean checkObjectsNotEqual(Object a, Object b) { + if (a == null || b == null) { + if ((a == null && b != null) || (a != null && b == null)) { + return true; + } else { + return false; + } + } else { + return !a.equals(b); + } + } + + private String getNotEqualString(Object a, Object b, boolean pass) { + if(pass) { + return a + " should not equal " + b; + } + return "didn't expect " + a + ", but got it"; + } + + public void info(String name, String message) { + testInfo test = new testInfo(true, name, message, false); + _logMochitestResult(test, "TEST-INFO", "INFO FAILED?"); + } +} diff --git a/build/mobile/robocop/FennecNativeActions.java.in b/build/mobile/robocop/FennecNativeActions.java.in new file mode 100644 index 000000000..c4c400e5c --- /dev/null +++ b/build/mobile/robocop/FennecNativeActions.java.in @@ -0,0 +1,483 @@ +#filter substitution +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package @ANDROID_PACKAGE_NAME@; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; +import java.lang.reflect.InvocationHandler; +import java.util.concurrent.SynchronousQueue; +import java.util.ArrayList; + +import android.app.Activity; +import android.content.Context; +import android.app.Instrumentation; +import android.database.Cursor; +import android.os.SystemClock; +import android.text.TextUtils; +import android.view.View; +import android.view.KeyEvent; +import android.util.Log; + +import org.json.*; + +import com.jayway.android.robotium.solo.Solo; + +import static @ANDROID_PACKAGE_NAME@.FennecNativeDriver.LogLevel; + +public class FennecNativeActions implements Actions { + private Solo mSolo; + private Instrumentation mInstr; + private Activity mGeckoApp; + private Assert mAsserter; + + // Objects for reflexive access of fennec classes. + private ClassLoader mClassLoader; + private Class mApiClass; + private Class mEventListenerClass; + private Class mDrawListenerClass; + private Method mRegisterEventListener; + private Method mUnregisterEventListener; + private Method mBroadcastEvent; + private Method mSetDrawListener; + private Method mQuerySql; + private Object mRobocopApi; + + private static final String LOGTAG = "FennecNativeActions"; + + public FennecNativeActions(Activity activity, Solo robocop, Instrumentation instrumentation, Assert asserter) { + mSolo = robocop; + mInstr = instrumentation; + mGeckoApp = activity; + mAsserter = asserter; + // Set up reflexive access of java classes and methods. + try { + mClassLoader = activity.getClassLoader(); + + mApiClass = mClassLoader.loadClass("org.mozilla.gecko.RobocopAPI"); + mEventListenerClass = mClassLoader.loadClass("org.mozilla.gecko.util.GeckoEventListener"); + mDrawListenerClass = mClassLoader.loadClass("org.mozilla.gecko.gfx.GeckoLayerClient$DrawListener"); + + mRegisterEventListener = mApiClass.getMethod("registerEventListener", String.class, mEventListenerClass); + mUnregisterEventListener = mApiClass.getMethod("unregisterEventListener", String.class, mEventListenerClass); + mBroadcastEvent = mApiClass.getMethod("broadcastEvent", String.class, String.class); + mSetDrawListener = mApiClass.getMethod("setDrawListener", mDrawListenerClass); + mQuerySql = mApiClass.getMethod("querySql", String.class, String.class); + + mRobocopApi = mApiClass.getConstructor(Activity.class).newInstance(activity); + } catch (Exception e) { + FennecNativeDriver.log(LogLevel.ERROR, e); + } + } + + class wakeInvocationHandler implements InvocationHandler { + private final GeckoEventExpecter mEventExpecter; + + public wakeInvocationHandler(GeckoEventExpecter expecter) { + mEventExpecter = expecter; + } + + public Object invoke(Object proxy, Method method, Object[] args) { + String methodName = method.getName(); + //Depending on the method, return a completely different type. + if(methodName.equals("toString")) { + return this.toString(); + } + if(methodName.equals("equals")) { + return + args[0] == null ? false : + this.toString().equals(args[0].toString()); + } + if(methodName.equals("clone")) { + return this; + } + if(methodName.equals("hashCode")) { + return 314; + } + FennecNativeDriver.log(FennecNativeDriver.LogLevel.DEBUG, + "Waking up on "+methodName); + mEventExpecter.notifyOfEvent(args); + return null; + } + } + + class GeckoEventExpecter implements RepeatedEventExpecter { + private final String mGeckoEvent; + private Object[] mRegistrationParams; + private boolean mEventReceived; + private boolean mEventEverReceived; + private String mEventData; + private static final int MAX_WAIT_MS = 90000; + + GeckoEventExpecter(String geckoEvent, Object[] registrationParams) { + if (TextUtils.isEmpty(geckoEvent)) { + throw new IllegalArgumentException("geckoEvent must not be empty"); + } + if (registrationParams == null || registrationParams.length == 0) { + throw new IllegalArgumentException("registrationParams must not be empty"); + } + + mGeckoEvent = geckoEvent; + mRegistrationParams = registrationParams; + } + + public synchronized void blockForEvent() { + blockForEvent(MAX_WAIT_MS, true); + } + + private synchronized void blockForEvent(long millis, boolean failOnTimeout) { + if (mRegistrationParams == null) { + throw new IllegalStateException("listener not registered"); + } + long startTime = SystemClock.uptimeMillis(); + long endTime = 0; + while (! mEventReceived) { + try { + this.wait(millis); + } catch (InterruptedException ie) { + FennecNativeDriver.log(LogLevel.ERROR, ie); + break; + } + endTime = SystemClock.uptimeMillis(); + if (!mEventReceived && (endTime - startTime >= millis)) { + if (failOnTimeout) { + FennecNativeDriver.logAllStackTraces(FennecNativeDriver.LogLevel.ERROR); + mAsserter.ok(false, "GeckoEventExpecter", + "blockForEvent timeout: "+mGeckoEvent); + } + mEventData = null; + return; + } + } + FennecNativeDriver.log(FennecNativeDriver.LogLevel.DEBUG, + "unblocked on expecter for " + mGeckoEvent); + mEventReceived = false; + } + + public synchronized void blockUntilClear(long millis) { + if (mRegistrationParams == null) { + throw new IllegalStateException("listener not registered"); + } + if (millis <= 0) { + throw new IllegalArgumentException("millis must be > 0"); + } + // wait for at least one event + long startTime = SystemClock.uptimeMillis(); + long endTime = 0; + while (!mEventReceived) { + try { + this.wait(MAX_WAIT_MS); + } catch (InterruptedException ie) { + FennecNativeDriver.log(LogLevel.ERROR, ie); + break; + } + endTime = SystemClock.uptimeMillis(); + if (!mEventReceived && (endTime - startTime >= MAX_WAIT_MS)) { + FennecNativeDriver.logAllStackTraces(FennecNativeDriver.LogLevel.ERROR); + mAsserter.ok(false, "GeckoEventExpecter", "blockUtilClear timeout"); + return; + } + } + // now wait for a period of millis where we don't get an event + startTime = SystemClock.uptimeMillis(); + while (true) { + try { + this.wait(millis); + } catch (InterruptedException ie) { + FennecNativeDriver.log(LogLevel.ERROR, ie); + break; + } + endTime = SystemClock.uptimeMillis(); + if (endTime - startTime >= millis) { + // success + break; + } + // we got a notify() before we could wait long enough, so we need to start over + startTime = endTime; + } + FennecNativeDriver.log(FennecNativeDriver.LogLevel.DEBUG, + "unblocked on expecter for " + mGeckoEvent); + mEventReceived = false; + } + + public synchronized String blockForEventData() { + blockForEvent(); + return mEventData; + } + + public synchronized String blockForEventDataWithTimeout(long millis) { + blockForEvent(millis, false); + return mEventData; + } + + public synchronized void unregisterListener() { + if (mRegistrationParams == null) { + throw new IllegalStateException("listener not registered"); + } + try { + FennecNativeDriver.log(LogLevel.INFO, "EventExpecter: no longer listening for "+mGeckoEvent); + mUnregisterEventListener.invoke(mRobocopApi, mRegistrationParams); + mRegistrationParams = null; + } catch (IllegalAccessException e) { + FennecNativeDriver.log(LogLevel.ERROR, e); + } catch (InvocationTargetException e) { + FennecNativeDriver.log(LogLevel.ERROR, e); + } + } + + public synchronized boolean eventReceived() { + return mEventEverReceived; + } + + void notifyOfEvent(Object[] args) { + FennecNativeDriver.log(FennecNativeDriver.LogLevel.DEBUG, + "received event " + mGeckoEvent); + synchronized (this) { + mEventReceived = true; + mEventEverReceived = true; + mEventData = args[1].toString(); + this.notifyAll(); + } + } + } + + public RepeatedEventExpecter expectGeckoEvent(String geckoEvent) { + FennecNativeDriver.log(FennecNativeDriver.LogLevel.DEBUG, + "waiting for "+geckoEvent); + try { + Object[] finalParams = new Object[2]; + finalParams[0] = geckoEvent; + GeckoEventExpecter expecter = new GeckoEventExpecter(geckoEvent, finalParams); + wakeInvocationHandler wIH = new wakeInvocationHandler(expecter); + Object proxy = Proxy.newProxyInstance(mClassLoader, new Class[] { mEventListenerClass }, wIH); + finalParams[1] = proxy; + + mRegisterEventListener.invoke(mRobocopApi, finalParams); + return expecter; + } catch (IllegalAccessException e) { + FennecNativeDriver.log(LogLevel.ERROR, e); + } catch (InvocationTargetException e) { + FennecNativeDriver.log(LogLevel.ERROR, e); + } + return null; + } + + public void sendGeckoEvent(String geckoEvent, String data) { + try { + mBroadcastEvent.invoke(mRobocopApi, geckoEvent, data); + } catch (IllegalAccessException e) { + FennecNativeDriver.log(LogLevel.ERROR, e); + } catch (InvocationTargetException e) { + FennecNativeDriver.log(LogLevel.ERROR, e); + } + } + + class DrawListenerProxy implements InvocationHandler { + private final PaintExpecter mPaintExpecter; + + DrawListenerProxy(PaintExpecter paintExpecter) { + mPaintExpecter = paintExpecter; + } + + public Object invoke(Object proxy, Method method, Object[] args) { + String methodName = method.getName(); + if ("drawFinished".equals(methodName)) { + FennecNativeDriver.log(FennecNativeDriver.LogLevel.DEBUG, + "Received drawFinished notification"); + mPaintExpecter.notifyOfEvent(args); + } else if ("toString".equals(methodName)) { + return "DrawListenerProxy"; + } else if ("equals".equals(methodName)) { + return false; + } else if ("hashCode".equals(methodName)) { + return 0; + } + return null; + } + } + + class PaintExpecter implements RepeatedEventExpecter { + private boolean mPaintDone; + private boolean mListening; + private static final int MAX_WAIT_MS = 90000; + + PaintExpecter() throws IllegalAccessException, InvocationTargetException { + Object proxy = Proxy.newProxyInstance(mClassLoader, new Class[] { mDrawListenerClass }, new DrawListenerProxy(this)); + mSetDrawListener.invoke(mRobocopApi, proxy); + mListening = true; + } + + void notifyOfEvent(Object[] args) { + synchronized (this) { + mPaintDone = true; + this.notifyAll(); + } + } + + private synchronized void blockForEvent(long millis, boolean failOnTimeout) { + if (!mListening) { + throw new IllegalStateException("draw listener not registered"); + } + long startTime = SystemClock.uptimeMillis(); + long endTime = 0; + while (!mPaintDone) { + try { + this.wait(millis); + } catch (InterruptedException ie) { + FennecNativeDriver.log(LogLevel.ERROR, ie); + break; + } + endTime = SystemClock.uptimeMillis(); + if (!mPaintDone && (endTime - startTime >= millis)) { + if (failOnTimeout) { + FennecNativeDriver.logAllStackTraces(FennecNativeDriver.LogLevel.ERROR); + mAsserter.ok(false, "PaintExpecter", "blockForEvent timeout"); + } + return; + } + } + } + + public synchronized void blockForEvent() { + blockForEvent(MAX_WAIT_MS, true); + } + + public synchronized String blockForEventData() { + blockForEvent(); + return null; + } + + public synchronized String blockForEventDataWithTimeout(long millis) { + blockForEvent(millis, false); + return null; + } + + public synchronized boolean eventReceived() { + return mPaintDone; + } + + public synchronized void blockUntilClear(long millis) { + if (!mListening) { + throw new IllegalStateException("draw listener not registered"); + } + if (millis <= 0) { + throw new IllegalArgumentException("millis must be > 0"); + } + // wait for at least one event + long startTime = SystemClock.uptimeMillis(); + long endTime = 0; + while (!mPaintDone) { + try { + this.wait(MAX_WAIT_MS); + } catch (InterruptedException ie) { + FennecNativeDriver.log(LogLevel.ERROR, ie); + break; + } + endTime = SystemClock.uptimeMillis(); + if (!mPaintDone && (endTime - startTime >= MAX_WAIT_MS)) { + FennecNativeDriver.logAllStackTraces(FennecNativeDriver.LogLevel.ERROR); + mAsserter.ok(false, "PaintExpecter", "blockUtilClear timeout"); + return; + } + } + // now wait for a period of millis where we don't get an event + startTime = SystemClock.uptimeMillis(); + while (true) { + try { + this.wait(millis); + } catch (InterruptedException ie) { + FennecNativeDriver.log(LogLevel.ERROR, ie); + break; + } + endTime = SystemClock.uptimeMillis(); + if (endTime - startTime >= millis) { + // success + break; + } + // we got a notify() before we could wait long enough, so we need to start over + startTime = endTime; + } + } + + public synchronized void unregisterListener() { + if (!mListening) { + throw new IllegalStateException("listener not registered"); + } + try { + FennecNativeDriver.log(LogLevel.INFO, "PaintExpecter: no longer listening for events"); + mListening = false; + mSetDrawListener.invoke(mRobocopApi, (Object)null); + } catch (Exception e) { + FennecNativeDriver.log(LogLevel.ERROR, e); + } + } + } + + public RepeatedEventExpecter expectPaint() { + try { + return new PaintExpecter(); + } catch (Exception e) { + FennecNativeDriver.log(LogLevel.ERROR, e); + return null; + } + } + + public void sendSpecialKey(SpecialKey button) { + switch(button) { + case DOWN: + sendKeyCode(KeyEvent.KEYCODE_DPAD_DOWN); + break; + case UP: + sendKeyCode(KeyEvent.KEYCODE_DPAD_UP); + break; + case LEFT: + sendKeyCode(KeyEvent.KEYCODE_DPAD_LEFT); + break; + case RIGHT: + sendKeyCode(KeyEvent.KEYCODE_DPAD_RIGHT); + break; + case ENTER: + sendKeyCode(KeyEvent.KEYCODE_ENTER); + break; + case MENU: + sendKeyCode(KeyEvent.KEYCODE_MENU); + break; + case BACK: + sendKeyCode(KeyEvent.KEYCODE_BACK); + break; + default: + mAsserter.ok(false, "sendSpecialKey", "Unknown SpecialKey " + button); + break; + } + } + + public void sendKeyCode(int keyCode) { + if (keyCode <= 0 || keyCode > KeyEvent.getMaxKeyCode()) { + mAsserter.ok(false, "sendKeyCode", "Unknown keyCode " + keyCode); + } + mInstr.sendCharacterSync(keyCode); + } + + @Override + public void sendKeys(String input) { + mInstr.sendStringSync(input); + } + + public void drag(int startingX, int endingX, int startingY, int endingY) { + mSolo.drag(startingX, endingX, startingY, endingY, 10); + } + + public Cursor querySql(String dbPath, String sql) { + try { + return (Cursor)mQuerySql.invoke(mRobocopApi, dbPath, sql); + } catch(InvocationTargetException ex) { + Log.e(LOGTAG, "Error invoking method", ex); + } catch(IllegalAccessException ex) { + Log.e(LOGTAG, "Error using field", ex); + } + return null; + } +} diff --git a/build/mobile/robocop/FennecNativeDriver.java.in b/build/mobile/robocop/FennecNativeDriver.java.in new file mode 100644 index 000000000..e05fa6f83 --- /dev/null +++ b/build/mobile/robocop/FennecNativeDriver.java.in @@ -0,0 +1,483 @@ +#filter substitution +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package @ANDROID_PACKAGE_NAME@; + +import java.io.BufferedOutputStream; +import java.io.BufferedReader; +import java.io.DataOutputStream; +import java.io.File; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.io.PrintWriter; +import java.nio.IntBuffer; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; +import java.lang.reflect.InvocationHandler; + +import android.app.Activity; +import android.opengl.GLSurfaceView; +import android.view.View; +import android.util.Log; + +import org.json.*; + +import com.jayway.android.robotium.solo.Solo; + +public class FennecNativeDriver implements Driver { + private static final int FRAME_TIME_THRESHOLD = 25; // allow 25ms per frame (40fps) + + // Map of IDs to element names. + private HashMap mLocators = null; + private Activity mActivity; + private Solo mSolo; + private String mRootPath; + + private static String mLogFile = null; + private static LogLevel mLogLevel = LogLevel.INFO; + + // Objects for reflexive access of fennec classes. + private ClassLoader mClassLoader; + private Class mApiClass; + private Class mEventListenerClass; + private Class mPanningPerfClass; + private Method mRegisterEventListener; + private Method mGetPixels; + private Method mStartFrameRecording; + private Method mStopFrameRecording; + private Method mStartCheckerboardRecording; + private Method mStopCheckerboardRecording; + private Object mRobocopApi; + + public enum LogLevel { + DEBUG(1), + INFO(2), + WARN(3), + ERROR(4); + + private int mValue; + LogLevel(int value) { + mValue = value; + } + public boolean isEnabled(LogLevel configuredLevel) { + return mValue >= configuredLevel.getValue(); + } + private int getValue() { + return mValue; + } + } + + public FennecNativeDriver(Activity activity, Solo robocop, String rootPath) { + mActivity = activity; + mSolo = robocop; + mRootPath = rootPath; + + // Set up table of fennec_ids. + mLocators = convertTextToTable(getFile(mRootPath + "/fennec_ids.txt")); + + // Set up reflexive access of java classes and methods. + try { + mClassLoader = activity.getClassLoader(); + + mApiClass = mClassLoader.loadClass("org.mozilla.gecko.RobocopAPI"); + mEventListenerClass = mClassLoader.loadClass("org.mozilla.gecko.util.GeckoEventListener"); + mPanningPerfClass = mClassLoader.loadClass("org.mozilla.gecko.gfx.PanningPerfAPI"); + + mRegisterEventListener = mApiClass.getMethod("registerEventListener", String.class, mEventListenerClass); + mGetPixels = mApiClass.getMethod("getViewPixels", View.class); + mStartFrameRecording = mPanningPerfClass.getDeclaredMethod("startFrameTimeRecording"); + mStopFrameRecording = mPanningPerfClass.getDeclaredMethod("stopFrameTimeRecording"); + mStartCheckerboardRecording = mPanningPerfClass.getDeclaredMethod("startCheckerboardRecording"); + mStopCheckerboardRecording = mPanningPerfClass.getDeclaredMethod("stopCheckerboardRecording"); + + mRobocopApi = mApiClass.getConstructor(Activity.class).newInstance(activity); + } catch (Exception e) { + log(LogLevel.ERROR, e); + } + } + + //Information on the location of the Gecko Frame. + private boolean mGeckoInfo = false; + private int mGeckoTop = 100; + private int mGeckoLeft = 0; + private int mGeckoHeight= 700; + private int mGeckoWidth = 1024; + + private void getGeckoInfo() { + View geckoLayout = mActivity.findViewById(Integer.decode((String)mLocators.get("gecko_layout"))); + if (geckoLayout != null) { + int[] pos = new int[2]; + geckoLayout.getLocationOnScreen(pos); + mGeckoTop = pos[1]; + mGeckoLeft = pos[0]; + mGeckoWidth = geckoLayout.getWidth(); + mGeckoHeight = geckoLayout.getHeight(); + mGeckoInfo = true; + } else { + throw new RoboCopException("Unable to find view gecko_layout"); + } + } + + public int getGeckoTop() { + if (!mGeckoInfo) { + getGeckoInfo(); + } + return mGeckoTop; + } + + public int getGeckoLeft() { + if (!mGeckoInfo) { + getGeckoInfo(); + } + return mGeckoLeft; + } + + public int getGeckoHeight() { + if (!mGeckoInfo) { + getGeckoInfo(); + } + return mGeckoHeight; + } + + public int getGeckoWidth() { + if (!mGeckoInfo) { + getGeckoInfo(); + } + return mGeckoWidth; + } + + /** Find the named element in the list of known Fennec views. + * @return An Element representing the view, or null if the view is not found. + */ + public Element findElement(Activity activity, String name) { + if (name == null) { + FennecNativeDriver.log(FennecNativeDriver.LogLevel.ERROR, + "Can not findElements when passed a null"); + return null; + } + if (mLocators.containsKey(name)) { + return new FennecNativeElement(Integer.decode((String)mLocators.get(name)), activity, mSolo); + } + FennecNativeDriver.log(FennecNativeDriver.LogLevel.ERROR, + "findElement: Element '"+name+"' does not exist in the list"); + return null; + } + + public void startFrameRecording() { + try { + mStartFrameRecording.invoke(null); + } catch (IllegalAccessException e) { + log(LogLevel.ERROR, e); + } catch (InvocationTargetException e) { + log(LogLevel.ERROR, e); + } + } + + public int stopFrameRecording() { + try { + List<Long> frames = (List<Long>)mStopFrameRecording.invoke(null); + int badness = 0; + for (int i = 1; i < frames.size(); i++) { + long frameTime = frames.get(i) - frames.get(i - 1); + int delay = (int)(frameTime - FRAME_TIME_THRESHOLD); + // for each frame we miss, add the square of the delay. This + // makes large delays much worse than small delays. + if (delay > 0) { + badness += delay * delay; + } + } + // Don't do any averaging of the numbers because really we want to + // know how bad the jank was at its worst + return badness; + } catch (IllegalAccessException e) { + log(LogLevel.ERROR, e); + } catch (InvocationTargetException e) { + log(LogLevel.ERROR, e); + } + + // higher values are worse, and the test failing is the worst! + return Integer.MAX_VALUE; + } + + public void startCheckerboardRecording() { + try { + mStartCheckerboardRecording.invoke(null); + } catch (IllegalAccessException e) { + log(LogLevel.ERROR, e); + } catch (InvocationTargetException e) { + log(LogLevel.ERROR, e); + } + } + + public float stopCheckerboardRecording() { + try { + List<Float> checkerboard = (List<Float>)mStopCheckerboardRecording.invoke(null); + float total = 0; + for (float val : checkerboard) { + total += val; + } + return total * 100.0f; + } catch (IllegalAccessException e) { + log(LogLevel.ERROR, e); + } catch (InvocationTargetException e) { + log(LogLevel.ERROR, e); + } + + return 0.0f; + } + + private View getSurfaceView() { + ArrayList<View> views = mSolo.getCurrentViews(); + try { + Class c = Class.forName("org.mozilla.gecko.gfx.LayerView"); + for (View v : views) { + if (c.isInstance(v)) { + return v; + } + } + } catch (ClassNotFoundException e) { + log(LogLevel.ERROR, e); + } + log(LogLevel.WARN, "getSurfaceView could not find LayerView"); + for (View v : views) { + log(LogLevel.WARN, v.toString()); + } + return null; + } + + public PaintedSurface getPaintedSurface() { + View view = getSurfaceView(); + if (view == null) { + return null; + } + IntBuffer pixelBuffer; + try { + pixelBuffer = (IntBuffer)mGetPixels.invoke(mRobocopApi, view); + } catch (Exception e) { + log(LogLevel.ERROR, e); + return null; + } + + // now we need to (1) flip the image, because GL likes to do things up-side-down, + // and (2) rearrange the bits from AGBR-8888 to ARGB-8888. + int w = view.getWidth(); + int h = view.getHeight(); + pixelBuffer.position(0); + String mapFile = mRootPath + "/pixels.map"; + + FileOutputStream fos = null; + BufferedOutputStream bos = null; + DataOutputStream dos = null; + try { + fos = new FileOutputStream(mapFile); + bos = new BufferedOutputStream(fos); + dos = new DataOutputStream(bos); + + for (int y = h - 1; y >= 0; y--) { + for (int x = 0; x < w; x++) { + int agbr = pixelBuffer.get(); + dos.writeInt((agbr & 0xFF00FF00) | ((agbr >> 16) & 0x000000FF) | ((agbr << 16) & 0x00FF0000)); + } + } + } catch (IOException e) { + throw new RoboCopException("exception with pixel writer on file: " + mapFile); + } finally { + try { + if (dos != null) { + dos.flush(); + dos.close(); + } + // closing dos automatically closes bos + if (fos != null) { + fos.flush(); + fos.close(); + } + } catch (IOException e) { + log(LogLevel.ERROR, e); + throw new RoboCopException("exception closing pixel writer on file: " + mapFile); + } + } + return new PaintedSurface(mapFile, w, h); + } + + public int mHeight=0; + public int mScrollHeight=0; + public int mPageHeight=10; + + class scrollHandler implements InvocationHandler { + public scrollHandler(){}; + public Object invoke(Object proxy, Method method, Object[] args) { + try { + // Disect the JSON object into the appropriate variables + JSONObject jo = ((JSONObject)args[1]); + mScrollHeight = jo.getInt("y"); + mHeight = jo.getInt("cheight"); + // We don't want a height of 0. That means it's a bad response. + if (mHeight > 0) { + mPageHeight = jo.getInt("height"); + } + + } catch( Throwable e) { + FennecNativeDriver.log(FennecNativeDriver.LogLevel.WARN, + "WARNING: ScrollReceived, but read wrong!"); + } + return null; + } + } + + public int getScrollHeight() { + return mScrollHeight; + } + public int getPageHeight() { + return mPageHeight; + } + public int getHeight() { + return mHeight; + } + + public void setupScrollHandling() { + //Setup scrollHandler to catch "robocop:scroll" events. + try { + Class [] interfaces = new Class[1]; + interfaces[0] = mEventListenerClass; + Object[] finalParams = new Object[2]; + finalParams[0] = "robocop:scroll"; + finalParams[1] = Proxy.newProxyInstance(mClassLoader, interfaces, new scrollHandler()); + mRegisterEventListener.invoke(mRobocopApi, finalParams); + } catch (IllegalAccessException e) { + log(LogLevel.ERROR, e); + } catch (InvocationTargetException e) { + log(LogLevel.ERROR, e); + } + + } + + /** + * Takes a filename, loads the file, and returns a string version of the entire file. + */ + public static String getFile(String filename) + { + StringBuilder text = new StringBuilder(); + + BufferedReader br = null; + try { + br = new BufferedReader(new FileReader(filename)); + String line; + + while ((line = br.readLine()) != null) { + text.append(line); + text.append('\n'); + } + } catch (IOException e) { + log(LogLevel.ERROR, e); + } finally { + try { + br.close(); + } catch (IOException e) { + } + } + return text.toString(); + } + + /** + * Takes a string of "key=value" pairs split by \n and creates a hash table. + */ + public static HashMap convertTextToTable(String data) + { + HashMap retVal = new HashMap(); + + String[] lines = data.split("\n"); + for (int i = 0; i < lines.length; i++) { + String[] parts = lines[i].split("=", 2); + retVal.put(parts[0].trim(), parts[1].trim()); + } + return retVal; + } + + public static void logAllStackTraces(LogLevel level) { + StringBuffer sb = new StringBuffer(); + sb.append("Dumping ALL the threads!\n"); + Map<Thread, StackTraceElement[]> allStacks = Thread.getAllStackTraces(); + for (Thread t : allStacks.keySet()) { + sb.append(t.toString()).append('\n'); + for (StackTraceElement ste : allStacks.get(t)) { + sb.append(ste.toString()).append('\n'); + } + sb.append('\n'); + } + log(level, sb.toString()); + } + + /** + * Set the filename used for logging. If the file already exists, delete it + * as a safe-guard against accidentally appending to an old log file. + */ + public static void setLogFile(String filename) { + mLogFile = filename; + File file = new File(mLogFile); + if (file.exists()) { + file.delete(); + } + } + + public static void setLogLevel(LogLevel level) { + mLogLevel = level; + } + + public static void log(LogLevel level, String message) { + log(level, message, null); + } + + public static void log(LogLevel level, Throwable t) { + log(level, null, t); + } + + public static void log(LogLevel level, String message, Throwable t) { + if (mLogFile == null) { + assert(false); + } + + if (level.isEnabled(mLogLevel)) { + PrintWriter pw = null; + try { + pw = new PrintWriter(new FileWriter(mLogFile, true)); + if (message != null) { + pw.println(message); + } + if (t != null) { + t.printStackTrace(pw); + } + } catch (IOException ioe) { + Log.e("Robocop", "exception with file writer on: " + mLogFile); + } finally { + pw.close(); + } + // PrintWriter doesn't throw IOE but sets an error flag instead, + // so check for that + if (pw.checkError()) { + Log.e("Robocop", "exception with file writer on: " + mLogFile); + } + } + + if (level == LogLevel.INFO) { + Log.i("Robocop", message, t); + } else if (level == LogLevel.DEBUG) { + Log.d("Robocop", message, t); + } else if (level == LogLevel.WARN) { + Log.w("Robocop", message, t); + } else if (level == LogLevel.ERROR) { + Log.e("Robocop", message, t); + } + } +} diff --git a/build/mobile/robocop/FennecNativeElement.java.in b/build/mobile/robocop/FennecNativeElement.java.in new file mode 100644 index 000000000..a12b0f7db --- /dev/null +++ b/build/mobile/robocop/FennecNativeElement.java.in @@ -0,0 +1,121 @@ +#filter substitution +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package @ANDROID_PACKAGE_NAME@; + +import java.util.List; + +import android.app.Activity; +import android.view.View; +import android.view.ViewGroup; +import android.widget.Button; +import android.widget.EditText; +import android.widget.ListView; +import android.widget.TextView; +import android.widget.TextSwitcher; +import android.app.Instrumentation; +import com.jayway.android.robotium.solo.Solo; +import java.util.concurrent.SynchronousQueue; +import java.util.concurrent.TimeUnit; + +public class FennecNativeElement implements Element { + private final Activity mActivity; + private Integer mId; + private Solo mSolo; + // max time to wait for thread synchronization + private static final int MAX_WAIT_MS = 60000; + + public FennecNativeElement(Integer id, Activity activity, Solo solo) { + mId = id; + mActivity = activity; + mSolo = solo; + } + + public Integer getId() { + return mId; + } + + private boolean mClickSuccess; + + public boolean click() { + mClickSuccess = false; + RobocopUtils.runOnUiThreadSync(mActivity, + new Runnable() { + public void run() { + View view = (View)mActivity.findViewById(mId); + if (view != null) { + if (view.performClick()) { + mClickSuccess = true; + } else { + FennecNativeDriver.log(FennecNativeDriver.LogLevel.WARN, + "Robocop called click on an element with no listener"); + } + } else { + FennecNativeDriver.log(FennecNativeDriver.LogLevel.ERROR, + "click: unable to find view "+mId); + } + } + }); + return mClickSuccess; + } + + private Object mText; + + public String getText() { + mText = null; + RobocopUtils.runOnUiThreadSync(mActivity, + new Runnable() { + public void run() { + View v = mActivity.findViewById(mId); + if (v instanceof EditText) { + EditText et = (EditText)v; + mText = et.getEditableText(); + } else if (v instanceof TextSwitcher) { + TextSwitcher ts = (TextSwitcher)v; + ts.getNextView(); + mText = ((TextView)ts.getCurrentView()).getText(); + } else if (v instanceof ViewGroup) { + ViewGroup vg = (ViewGroup)v; + for (int i = 0; i < vg.getChildCount(); i++) { + if (vg.getChildAt(i) instanceof TextView) { + mText = ((TextView)vg.getChildAt(i)).getText(); + } + } + } else if (v instanceof TextView) { + mText = ((TextView)v).getText(); + } else if (v == null) { + FennecNativeDriver.log(FennecNativeDriver.LogLevel.ERROR, + "getText: unable to find view "+mId); + } else { + FennecNativeDriver.log(FennecNativeDriver.LogLevel.ERROR, + "getText: unhandled type for view "+mId); + } + } // end of run() method definition + } // end of anonymous Runnable object instantiation + ); + if (mText == null) { + FennecNativeDriver.log(FennecNativeDriver.LogLevel.WARN, + "getText: Text is null for view "+mId); + return null; + } + return mText.toString(); + } + + private boolean mDisplayed; + + public boolean isDisplayed() { + mDisplayed = false; + RobocopUtils.runOnUiThreadSync(mActivity, + new Runnable() { + public void run() { + View view = (View)mActivity.findViewById(mId); + if (view != null) { + mDisplayed = true; + } + } + }); + return mDisplayed; + } +} diff --git a/build/mobile/robocop/FennecTalosAssert.java.in b/build/mobile/robocop/FennecTalosAssert.java.in new file mode 100644 index 000000000..0034da20c --- /dev/null +++ b/build/mobile/robocop/FennecTalosAssert.java.in @@ -0,0 +1,75 @@ +#filter substitution +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package @ANDROID_PACKAGE_NAME@; + + +public class FennecTalosAssert implements Assert { + + public FennecTalosAssert() { } + + /** + * Write information to a logfile and logcat + */ + public void dumpLog(String message) { + FennecNativeDriver.log(FennecNativeDriver.LogLevel.INFO, message); + } + + /** Write information to a logfile and logcat */ + public void dumpLog(String message, Throwable t) { + FennecNativeDriver.log(FennecNativeDriver.LogLevel.INFO, message, t); + } + + /** + * Set the filename used for dumpLog. + */ + public void setLogFile(String filename) { + FennecNativeDriver.setLogFile(filename); + } + + public void setTestName(String testName) { } + + public void endTest() { } + + public void ok(boolean condition, String name, String diag) { + if (!condition) { + dumpLog("__FAIL" + name + ": " + diag + "__FAIL"); + } + } + + public void is(Object a, Object b, String name) { + boolean pass = (a == null ? b == null : a.equals(b)); + ok(pass, name, "got " + a + ", expected " + b); + } + + public void isnot(Object a, Object b, String name) { + boolean fail = (a == null ? b == null : a.equals(b)); + ok(!fail, name, "got " + a + ", expected not " + b); + } + + public void ispixel(int actual, int r, int g, int b, String name) { + throw new UnsupportedOperationException(); + } + + public void isnotpixel(int actual, int r, int g, int b, String name) { + throw new UnsupportedOperationException(); + } + + public void todo(boolean condition, String name, String diag) { + throw new UnsupportedOperationException(); + } + + public void todo_is(Object a, Object b, String name) { + throw new UnsupportedOperationException(); + } + + public void todo_isnot(Object a, Object b, String name) { + throw new UnsupportedOperationException(); + } + + public void info(String name, String message) { + dumpLog(name + ": " + message); + } +} diff --git a/build/mobile/robocop/Makefile.in b/build/mobile/robocop/Makefile.in new file mode 100644 index 000000000..866701526 --- /dev/null +++ b/build/mobile/robocop/Makefile.in @@ -0,0 +1,132 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +DEPTH = @DEPTH@ +topsrcdir = @top_srcdir@ +srcdir = @srcdir@ +VPATH = @srcdir@ + +mobile-tests := mobile/android/base/tests +TESTPATH := $(topsrcdir)/$(mobile-tests) +dir-tests := $(DEPTH)/$(mobile-tests) + +include $(DEPTH)/config/autoconf.mk + +ANDROID_APK_NAME := robocop-debug + +ROBOTIUM_PATH = $(srcdir)/robotium-solo-3.6.jar + +JAVAFILES = \ + R.java \ + $(NULL) + +RES_FILES = \ + res/values/strings.xml \ + $(NULL) + +_JAVA_HARNESS = \ + Actions.java \ + Assert.java \ + Driver.java \ + Element.java \ + FennecInstrumentationTestRunner.java \ + FennecNativeActions.java \ + FennecMochitestAssert.java \ + FennecTalosAssert.java \ + FennecNativeDriver.java \ + FennecNativeElement.java \ + RoboCopException.java \ + RobocopUtils.java \ + PaintedSurface.java \ + $(NULL) + +# pre-process harness sources +PP_TARGETS += java-harness +java-harness := $(addprefix $(srcdir)/,$(addsuffix .in,$(_JAVA_HARNESS))) +java-harness-dep := $(addprefix $(CURDIR)/,$(_JAVA_HARNESS)) +java-harness_PATH := $(CURDIR) + +# pre-process test sources +PP_TARGETS += java-tests +java-tests-src := $(wildcard $(TESTPATH)/*.java.in) +java-tests-dep := $(patsubst $(TESTPATH)/%.java.in,$(dir-tests)/%.java,$(java-tests-src)) +java-tests := $(java-tests-src) +java-tests_PATH := $(dir-tests) + +PP_TARGETS += manifest +manifest := $(srcdir)/AndroidManifest.xml.in +manifest_TARGET := AndroidManifest.xml + + +# Install robocop configs and helper +INSTALL_TARGETS += robocop +robocop_TARGET := libs +robocop_DEST := $(CURDIR) +robocop_FILES := \ + $(TESTPATH)/robocop.ini \ + $(TESTPATH)/robocop_autophone.ini \ + $(NULL) +robocop-deps := $(notdir $(robocop_FILES)) + +MOCHITEST_ROBOCOP_FILES := \ + $(wildcard $(TESTPATH)/*.html) \ + $(wildcard $(TESTPATH)/*.jpg) \ + $(wildcard $(TESTPATH)/*.sjs) \ + $(wildcard $(TESTPATH)/test*.js) \ + $(wildcard $(TESTPATH)/robocop*.js) \ + $(wildcard $(TESTPATH)/*.xml) \ + $(NULL) + +GARBAGE += \ + AndroidManifest.xml \ + $(java-tests-dep) \ + $(_JAVA_HARNESS) \ + classes.dex \ + $(ANDROID_APK_NAME).ap_ \ + $(ANDROID_APK_NAME)-unsigned-unaligned.apk \ + $(ANDROID_APK_NAME)-unaligned.apk \ + $(ANDROID_APK_NAME).apk \ + $(robocop-deps) \ + $(NULL) + +DEFINES += \ + -DANDROID_PACKAGE_NAME=$(ANDROID_PACKAGE_NAME) \ + $(NULL) + +JAVA_CLASSPATH = $(ANDROID_SDK)/android.jar:$(ROBOTIUM_PATH) + +include $(topsrcdir)/config/rules.mk + +# Override rules.mk java flags with the android specific ones +include $(topsrcdir)/config/android-common.mk + +GENERATED_DIRS_tools = classes $(dir-tests) + +tools:: $(ANDROID_APK_NAME).apk + +classes.dex: $(ANDROID_APK_NAME).ap_ +classes.dex: $(robocop-deps) +classes.dex: $(java-harness-dep) +classes.dex: $(java-tests-dep) + $(JAVAC) $(JAVAC_FLAGS) -d classes $(JAVAFILES) $(_JAVA_HARNESS) $(java-tests-dep) + $(DX) --dex --output=$@ classes $(ROBOTIUM_PATH) $(ANDROID_COMPT_LIB) + +$(ANDROID_APK_NAME).ap_: AndroidManifest.xml $(TESTPATH)/assets/* + $(AAPT) package -f -M $< -I $(ANDROID_SDK)/android.jar -I . -S res -A $(TESTPATH)/assets -F $@ -J ./ + +$(ANDROID_APK_NAME)-unsigned-unaligned.apk: $(ANDROID_APK_NAME).ap_ classes.dex + cp $< $@ + $(ZIP) -0 $@ classes.dex + +$(ANDROID_APK_NAME)-unaligned.apk: $(ANDROID_APK_NAME)-unsigned-unaligned.apk + cp $< $@ + $(DEBUG_JARSIGNER) $@ + +$(ANDROID_APK_NAME).apk: $(ANDROID_APK_NAME)-unaligned.apk + $(ZIPALIGN) -f -v 4 $< $@ + +# PP_java-tests not fully usable here +# Intermediate step toward a library rule. +$(dir-tests)/%.java: $(TESTPATH)/%.java.in $(call mkdir_deps,$(dir-tests)) + $(PYTHON) $(topsrcdir)/config/Preprocessor.py $(DEFINES) $< > $@ diff --git a/build/mobile/robocop/PaintedSurface.java.in b/build/mobile/robocop/PaintedSurface.java.in new file mode 100644 index 000000000..deca9f12b --- /dev/null +++ b/build/mobile/robocop/PaintedSurface.java.in @@ -0,0 +1,78 @@ +#filter substitution +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package @ANDROID_PACKAGE_NAME@; + +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileInputStream; +import java.nio.MappedByteBuffer; +import java.nio.channels.FileChannel; + +public class PaintedSurface { + private String mFileName; + private int mWidth; + private int mHeight; + private FileInputStream mPixelFile; + private MappedByteBuffer mPixelBuffer; + + public PaintedSurface(String filename, int width, int height) { + mFileName = filename; + mWidth = width; + mHeight = height; + + try { + File f = new File(filename); + int pixelSize = (int)f.length(); + + mPixelFile = new FileInputStream(filename); + mPixelBuffer = mPixelFile.getChannel().map(FileChannel.MapMode.READ_ONLY, 0, pixelSize); + } catch (java.io.FileNotFoundException e) { + FennecNativeDriver.log(FennecNativeDriver.LogLevel.ERROR, e); + } catch (java.io.IOException e) { + FennecNativeDriver.log(FennecNativeDriver.LogLevel.ERROR, e); + } + } + + public final int getWidth() { + return mWidth; + } + + public final int getHeight() { + return mHeight; + } + + public final int getPixelAt(int x, int y) { + if (mPixelBuffer == null) { + throw new RoboCopException("Trying to access PaintedSurface with no active PixelBuffer"); + } + + if (x >= mWidth || x < 0) { + throw new RoboCopException("Trying to access PaintedSurface with invalid x value"); + } + + if (y >= mHeight || y < 0) { + throw new RoboCopException("Trying to access PaintedSurface with invalid y value"); + } + + // The rows are reversed so row 0 is at the end and we start with the last row. + // This is why we do mHeight-y; + int index = (x + ((mHeight - y - 1) * mWidth)) * 4; + int b1 = mPixelBuffer.get(index) & 0xFF; + int b2 = mPixelBuffer.get(index + 1) & 0xFF; + int b3 = mPixelBuffer.get(index + 2) & 0xFF; + int b4 = mPixelBuffer.get(index + 3) & 0xFF; + int value = (b1 << 24) + (b2 << 16) + (b3 << 8) + (b4 << 0); + return value; + } + + public void close() { + try { + mPixelFile.close(); + } catch (Exception e) { + FennecNativeDriver.log(FennecNativeDriver.LogLevel.DEBUG, e); + } + } +} diff --git a/build/mobile/robocop/README b/build/mobile/robocop/README new file mode 100644 index 000000000..3de479685 --- /dev/null +++ b/build/mobile/robocop/README @@ -0,0 +1,12 @@ +Robocop is a Mozilla project which uses Robotium to test Firefox on Android devices. + +Robotium is an open source tool licensed under the Apache 2.0 license and the original +source can be found here: +http://code.google.com/p/robotium/ + +We are including robotium-solo-3.6.jar as a binary and are not modifying it in any way +from the original download found at: +http://code.google.com/p/robotium/ + +Firefox for Android developers should read the documentation in +mobile/android/base/tests/README.rst. diff --git a/build/mobile/robocop/RoboCopException.java.in b/build/mobile/robocop/RoboCopException.java.in new file mode 100644 index 000000000..5bff42494 --- /dev/null +++ b/build/mobile/robocop/RoboCopException.java.in @@ -0,0 +1,25 @@ +#filter substitution +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package @ANDROID_PACKAGE_NAME@; + +public class RoboCopException extends RuntimeException { + + public RoboCopException() { + super(); + } + + public RoboCopException(String message) { + super(message); + } + + public RoboCopException(Throwable cause) { + super(cause); + } + + public RoboCopException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/build/mobile/robocop/RobocopUtils.java.in b/build/mobile/robocop/RobocopUtils.java.in new file mode 100644 index 000000000..f2ab238f3 --- /dev/null +++ b/build/mobile/robocop/RobocopUtils.java.in @@ -0,0 +1,43 @@ +#filter substitution + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package @ANDROID_PACKAGE_NAME@; + +import android.app.Activity; + +import java.util.concurrent.SynchronousQueue; +import java.util.concurrent.TimeUnit; + +public final class RobocopUtils { + private static final int MAX_WAIT_MS = 20000; + + private RobocopUtils() {} + + public static void runOnUiThreadSync(Activity activity, final Runnable runnable) { + final SynchronousQueue syncQueue = new SynchronousQueue(); + activity.runOnUiThread( + new Runnable() { + public void run() { + runnable.run(); + try { + syncQueue.put(new Object()); + } catch (InterruptedException e) { + FennecNativeDriver.log(FennecNativeDriver.LogLevel.ERROR, e); + } + } + }); + try { + // Wait for the UiThread code to finish running + if (syncQueue.poll(MAX_WAIT_MS, TimeUnit.MILLISECONDS) == null) { + FennecNativeDriver.log(FennecNativeDriver.LogLevel.ERROR, + "time-out waiting for UI thread"); + FennecNativeDriver.logAllStackTraces(FennecNativeDriver.LogLevel.ERROR); + } + } catch (InterruptedException e) { + FennecNativeDriver.log(FennecNativeDriver.LogLevel.ERROR, e); + } + } +} diff --git a/build/mobile/robocop/moz.build b/build/mobile/robocop/moz.build new file mode 100644 index 000000000..e6e0c96fe --- /dev/null +++ b/build/mobile/robocop/moz.build @@ -0,0 +1,8 @@ +# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +MODULE = 'robocop' + diff --git a/build/mobile/robocop/res/values/strings.xml b/build/mobile/robocop/res/values/strings.xml new file mode 100644 index 000000000..c1727416b --- /dev/null +++ b/build/mobile/robocop/res/values/strings.xml @@ -0,0 +1,9 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- This Source Code Form is subject to the terms of the Mozilla Public + - License, v. 2.0. If a copy of the MPL was not distributed with this + - file, You can obtain one at http://mozilla.org/MPL/2.0/. --> + +<resources> + <string name="app_name">Roboexample</string> + +</resources> diff --git a/build/mobile/robocop/robotium-solo-3.6.jar b/build/mobile/robocop/robotium-solo-3.6.jar Binary files differnew file mode 100644 index 000000000..4280906d1 --- /dev/null +++ b/build/mobile/robocop/robotium-solo-3.6.jar diff --git a/build/mobile/sutagent/android/ASMozStub.java b/build/mobile/sutagent/android/ASMozStub.java new file mode 100644 index 000000000..7ecf2f2e0 --- /dev/null +++ b/build/mobile/sutagent/android/ASMozStub.java @@ -0,0 +1,327 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package com.mozilla.SUTAgentAndroid.service; + +import java.io.File; +import java.io.IOException; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.net.InetAddress; +import java.net.ServerSocket; +import java.util.Timer; + +import com.mozilla.SUTAgentAndroid.SUTAgentAndroid; +import com.mozilla.SUTAgentAndroid.R; + +import android.app.Notification; +import android.app.NotificationManager; +import android.content.Context; +import android.content.Intent; +import android.net.wifi.WifiManager; +import android.os.Handler; +import android.os.IBinder; +import android.util.Log; +import android.view.Gravity; +import android.widget.Toast; + +import javax.jmdns.JmDNS; +import javax.jmdns.ServiceInfo; + +public class ASMozStub extends android.app.Service { + private final static int COMMAND_PORT = 20701; + private final static int DATA_PORT = 20700; + + private ServerSocket cmdChnl = null; + private ServerSocket dataChnl = null; + private Handler handler = new Handler(); + RunCmdThread runCmdThrd = null; + RunDataThread runDataThrd = null; + Thread monitor = null; + Timer timer = null; + boolean doZeroConfig = false; + + @SuppressWarnings("unchecked") + private static final Class<?>[] mSetForegroundSignature = new Class[] { + boolean.class}; + @SuppressWarnings("unchecked") + private static final Class<?>[] mStartForegroundSignature = new Class[] { + int.class, Notification.class}; + @SuppressWarnings("unchecked") + private static final Class<?>[] mStopForegroundSignature = new Class[] { + boolean.class}; + + private NotificationManager mNM; + private Method mSetForeground; + private Method mStartForeground; + private Method mStopForeground; + private Object[] mSetForegroundArgs = new Object[1]; + private Object[] mStartForegroundArgs = new Object[2]; + private Object[] mStopForegroundArgs = new Object[1]; + + @Override + public IBinder onBind(Intent intent) + { + return null; + } + + @Override + public void onCreate() { + super.onCreate(); + + mNM = (NotificationManager)getSystemService(NOTIFICATION_SERVICE); + try { + mStartForeground = getClass().getMethod("startForeground", mStartForegroundSignature); + mStopForeground = getClass().getMethod("stopForeground", mStopForegroundSignature); + } + catch (NoSuchMethodException e) { + // Might be running on an older platform. + mStartForeground = mStopForeground = null; + Log.w("SUTAgent", "unable to find start/stopForeground method(s) -- older platform?"); + } + + try { + mSetForeground = getClass().getMethod("setForeground", mSetForegroundSignature); + } + catch (NoSuchMethodException e) { + mSetForeground = null; + Log.e("SUTAgent", "unable to find setForeground method!"); + } + + doToast("Listener Service created..."); + } + + WifiManager.MulticastLock multicastLock; + JmDNS jmdns; + + void startZeroConf() { + if (multicastLock == null) { + WifiManager wifi = (WifiManager) getSystemService(Context.WIFI_SERVICE); + multicastLock = wifi.createMulticastLock("SUTAgent"); + multicastLock.setReferenceCounted(true); + } + + multicastLock.acquire(); + + try { + InetAddress inetAddress = SUTAgentAndroid.getLocalInetAddress(); + + if (jmdns == null) { + jmdns = JmDNS.create(inetAddress, null); + } + + if (jmdns != null) { + String name = "SUTAgent"; + + String hwid = SUTAgentAndroid.getHWID(this); + if (hwid != null) { + name += " [hwid:" + hwid + "]"; + } + + // multicast reception is broken for some reason, so + // this service can't be resolved; it can only be + // broadcast. So, we cheat -- we put the IP address + // in the broadcast that we can pull out later. + // However, periods aren't legal, so replace them. + // The IP address will show up as [ip:127_0_0_1] + name += " [ip:" + inetAddress.getHostAddress().toString().replace('.', '_') + "]"; + + final ServiceInfo serviceInfo = ServiceInfo.create("_sutagent._tcp.local.", + name, + COMMAND_PORT, + "Android SUTAgent"); + final JmDNS dns = jmdns; + // we want to call registerService on a new thread, because it can block + // for a little while. + Thread registerThread = new Thread() { + public void run() { + try { + dns.registerService(serviceInfo); + } catch (IOException e) { + Log.e("SUTAgent", "Failed to register JmDNS service!", e); + } + } + }; + registerThread.setDaemon(true); + registerThread.start(); + } + } catch (IOException e) { + Log.e("SUTAgent", "Failed to register JmDNS service!", e); + } + } + + void stopZeroConf() { + if (jmdns != null) { + try { + jmdns.unregisterAllServices(); + jmdns.close(); + } catch (IOException e) { + Log.e("SUTAgent", "Failed to close JmDNS service!", e); + } + jmdns = null; + } + + if (multicastLock != null) { + multicastLock.release(); + multicastLock = null; + } + } + + public void onStart(Intent intent, int startId) { + super.onStart(intent, startId); + + try { + cmdChnl = new ServerSocket(COMMAND_PORT); + runCmdThrd = new RunCmdThread(cmdChnl, this, handler); + runCmdThrd.start(); + doToast(String.format("Command channel port %d ...", COMMAND_PORT)); + + dataChnl = new ServerSocket(DATA_PORT); + runDataThrd = new RunDataThread(dataChnl, this); + runDataThrd.start(); + doToast(String.format("Data channel port %d ...", DATA_PORT)); + + DoCommand tmpdc = new DoCommand(getApplication()); + File dir = getFilesDir(); + File iniFile = new File(dir, "SUTAgent.ini"); + String sIniFile = iniFile.getAbsolutePath(); + String zeroconf = tmpdc.GetIniData("General", "ZeroConfig", sIniFile); + if (zeroconf != "" && Integer.parseInt(zeroconf) == 1) { + this.doZeroConfig = true; + } + + if (this.doZeroConfig) { + startZeroConf(); + } + + Notification notification = new Notification(); + startForegroundCompat(R.string.foreground_service_started, notification); + } + catch (Exception e) { + doToast(e.toString()); + } + + return; + } + + public void onDestroy() + { + super.onDestroy(); + + if (this.doZeroConfig) { + stopZeroConf(); + } + + if (runCmdThrd.isAlive()) + { + runCmdThrd.StopListening(); + } + + if (runDataThrd.isAlive()) + { + runDataThrd.StopListening(); + } + + NotificationManager notificationManager = (NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE); + notificationManager.cancel(1959); + + stopForegroundCompat(R.string.foreground_service_started); + + doToast("Listener Service destroyed..."); + + System.exit(0); + } + + public void SendToDataChannel(String strToSend) + { + if (runDataThrd.isAlive()) + runDataThrd.SendToDataChannel(strToSend); + } + + public void doToast(String sMsg) { + Toast toast = Toast.makeText(this, sMsg, Toast.LENGTH_LONG); + toast.setGravity(Gravity.TOP|Gravity.CENTER_HORIZONTAL, 0, 100); + toast.show(); + } + + /** + * This is a wrapper around the new startForeground method, using the older + * APIs if it is not available. + */ + void startForegroundCompat(int id, Notification notification) { + // If we have the new startForeground API, then use it. + if (mStartForeground != null) { + mStartForegroundArgs[0] = Integer.valueOf(id); + mStartForegroundArgs[1] = notification; + try { + mStartForeground.invoke(this, mStartForegroundArgs); + } catch (InvocationTargetException e) { + // Should not happen. + Log.e("SUTAgent", "Unable to invoke startForeground", e); + } catch (IllegalAccessException e) { + // Should not happen. + Log.e("SUTAgent", "Unable to invoke startForeground", e); + } + return; + } + + // Fall back on the old API. + if (mSetForeground != null) { + try { + mSetForegroundArgs[0] = Boolean.TRUE; + mSetForeground.invoke(this, mSetForegroundArgs); + } catch (IllegalArgumentException e) { + Log.e("SUTAgent", "Unable to invoke setForeground", e); + e.printStackTrace(); + } catch (IllegalAccessException e) { + Log.e("SUTAgent", "Unable to invoke setForeground", e); + e.printStackTrace(); + } catch (InvocationTargetException e) { + Log.e("SUTAgent", "Unable to invoke setForeground", e); + e.printStackTrace(); + } + } + mNM.notify(id, notification); + } + + /** + * This is a wrapper around the new stopForeground method, using the older + * APIs if it is not available. + */ + void stopForegroundCompat(int id) { + // If we have the new stopForeground API, then use it. + if (mStopForeground != null) { + mStopForegroundArgs[0] = Boolean.TRUE; + try { + mStopForeground.invoke(this, mStopForegroundArgs); + } catch (InvocationTargetException e) { + // Should not happen. + Log.e("SUTAgent", "Unable to invoke stopForeground", e); + } catch (IllegalAccessException e) { + // Should not happen. + Log.e("SUTAgent", "Unable to invoke stopForeground", e); + } + return; + } + + // Fall back on the old API. Note to cancel BEFORE changing the + // foreground state, since we could be killed at that point. + mNM.cancel(id); + if (mSetForeground != null) { + try { + mSetForegroundArgs[0] = Boolean.FALSE; + mSetForeground.invoke(this, mSetForegroundArgs); + } catch (IllegalArgumentException e) { + Log.e("SUTAgent", "Unable to invoke setForeground", e); + e.printStackTrace(); + } catch (IllegalAccessException e) { + Log.e("SUTAgent", "Unable to invoke setForeground", e); + e.printStackTrace(); + } catch (InvocationTargetException e) { + Log.e("SUTAgent", "Unable to invoke setForeground", e); + e.printStackTrace(); + } + } + } +} diff --git a/build/mobile/sutagent/android/AlertLooperThread.java b/build/mobile/sutagent/android/AlertLooperThread.java new file mode 100644 index 000000000..01d0b43b8 --- /dev/null +++ b/build/mobile/sutagent/android/AlertLooperThread.java @@ -0,0 +1,63 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package com.mozilla.SUTAgentAndroid.service; + +import java.util.Timer; + +import android.content.ContextWrapper; +import android.os.Handler; +import android.os.Looper; +import android.os.Message; + +class AlertLooperThread extends Thread + { + public Handler mHandler; + private Looper looper = null; + private DoAlert da = null; + private Timer alertTimer = null; + private ContextWrapper contextWrapper = null; + + AlertLooperThread(ContextWrapper ctxW) + { + this.contextWrapper = ctxW; + } + + public Timer getAlertTimer() + { + return alertTimer; + } + + public void term() + { + if (da != null) + da.term(); + } + + public void quit() + { + if (looper != null) + looper.quit(); + } + + public void run() + { + Looper.prepare(); + + looper = Looper.myLooper(); + + mHandler = new Handler() + { + public void handleMessage(Message msg) + { + // process incoming messages here + } + }; + + alertTimer = new Timer(); + da = new DoAlert(contextWrapper); + alertTimer.scheduleAtFixedRate(da, 0, 5000); + Looper.loop(); + } + } diff --git a/build/mobile/sutagent/android/AndroidManifest.xml b/build/mobile/sutagent/android/AndroidManifest.xml new file mode 100644 index 000000000..baf140db9 --- /dev/null +++ b/build/mobile/sutagent/android/AndroidManifest.xml @@ -0,0 +1,57 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- This Source Code Form is subject to the terms of the Mozilla Public + - License, v. 2.0. If a copy of the MPL was not distributed with this + - file, You can obtain one at http://mozilla.org/MPL/2.0/. --> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.mozilla.SUTAgentAndroid" + android:versionCode="1" android:versionName="1.01"> + <application android:icon="@drawable/icon" android:label="@string/app_name" android:debuggable="true"> + <activity android:name=".SUTAgentAndroid" + android:screenOrientation="nosensor" + android:label="@string/app_name"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> + </activity> + <service android:name=".service.ASMozStub"> + <intent-filter> + <action android:name="com.mozilla.SUTAgentAndroid.service.LISTENER_SERVICE" /> + </intent-filter> + </service> + </application> + + <uses-sdk android:minSdkVersion="5" android:targetSdkVersion="8"/> + +<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"></uses-permission> +<uses-permission android:name="android.permission.INTERNET"></uses-permission> +<uses-permission android:name="android.permission.REBOOT"></uses-permission> +<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"></uses-permission> +<uses-permission android:name="android.permission.CHANGE_WIFI_MULTICAST_STATE"></uses-permission> +<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"></uses-permission> +<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"></uses-permission> +<uses-permission android:name="android.permission.RESTART_PACKAGES"></uses-permission> +<uses-permission android:name="android.permission.CHANGE_WIFI_STATE"></uses-permission> +<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE"></uses-permission> +<uses-permission android:name="android.permission.CHANGE_CONFIGURATION"></uses-permission> +<uses-permission android:name="android.permission.READ_PHONE_STATE"></uses-permission> +<uses-permission android:name="android.permission.BATTERY_STATS"></uses-permission> +<uses-permission android:name="android.permission.DEVICE_POWER"></uses-permission> +<uses-permission android:name="android.permission.DISABLE_KEYGUARD"></uses-permission> +<uses-permission android:name="android.permission.WAKE_LOCK"></uses-permission> +<uses-permission android:name="android.permission.WRITE_SETTINGS"></uses-permission> +<uses-permission android:name="android.permission.READ_SYNC_SETTINGS"></uses-permission> +<uses-permission android:name="android.permission.BLUETOOTH"></uses-permission> +<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"></uses-permission> +<uses-permission android:name="android.permission.INSTALL_PACKAGES"></uses-permission> +<uses-permission android:name="android.permission.STATUS_BAR"></uses-permission> +<uses-permission android:name="android.permission.VIBRATE"></uses-permission> +<uses-permission android:name="android.permission.SET_TIME"></uses-permission> +<uses-permission android:name="android.permission.SET_TIME_ZONE"></uses-permission> + +<uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS"></uses-permission> +<uses-permission android:name="android.permission.WRITE_SYNC_SETTINGS"></uses-permission> +<uses-permission android:name="android.permission.GET_TASKS"></uses-permission> + +</manifest> diff --git a/build/mobile/sutagent/android/CmdWorkerThread.java b/build/mobile/sutagent/android/CmdWorkerThread.java new file mode 100644 index 000000000..12ce6bfdb --- /dev/null +++ b/build/mobile/sutagent/android/CmdWorkerThread.java @@ -0,0 +1,177 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package com.mozilla.SUTAgentAndroid.service; + +import java.io.BufferedInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.PrintWriter; +import java.net.Socket; +import java.net.SocketTimeoutException; + +import com.mozilla.SUTAgentAndroid.SUTAgentAndroid; + +import android.util.Log; + +// import com.mozilla.SUTAgentAndroid.DoCommand; + +public class CmdWorkerThread extends Thread +{ + private RunCmdThread theParent = null; + private Socket socket = null; + private String prompt = null; + boolean bListening = true; + + public CmdWorkerThread(RunCmdThread theParent, Socket workerSocket) + { + super("CmdWorkerThread"); + this.theParent = theParent; + this.socket = workerSocket; + byte pr [] = new byte [3]; + pr[0] = '$'; + pr[1] = '>'; + pr[2] = 0; + prompt = new String(pr,0,3); + } + + public void StopListening() + { + bListening = false; + } + + private String readLine(BufferedInputStream in) + { + String sRet = ""; + int nByte = 0; + char cChar = 0; + + try + { + nByte = in.read(); + while (nByte != -1) + { + cChar = ((char)(nByte & 0xFF)); + if ((cChar != '\r') && (cChar != '\n')) + sRet += cChar; + else + break; + nByte = in.read(); + } + + if ((in.available() > 0) && (cChar != '\n')) + { + in.mark(1024); + nByte = in.read(); + + if (nByte != -1) + { + cChar = ((char)(nByte & 0xFF)); + if (cChar != '\n') + { + in.reset(); + } + } + } + } + catch (IOException e) + { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + if (sRet.length() == 0) + sRet = null; + + return(sRet); + } + + public void run() + { + try { + OutputStream cmdOut = socket.getOutputStream(); + InputStream cmdIn = socket.getInputStream(); + PrintWriter out = new PrintWriter(cmdOut, true); + BufferedInputStream in = new BufferedInputStream(cmdIn); + String inputLine, outputLine; + DoCommand dc = new DoCommand(theParent.svc); + + SUTAgentAndroid.log(dc, "CmdWorkerThread starts: "+getId()); + + int nAvail = cmdIn.available(); + cmdIn.skip(nAvail); + + out.print(prompt); + out.flush(); + + while (bListening) + { + if (!(in.available() > 0)) + { + socket.setSoTimeout(500); + try { + int nRead = cmdIn.read(); + if (nRead == -1) + { + bListening = false; + continue; + } + else + { + inputLine = ((char)nRead) + ""; + socket.setSoTimeout(120000); + } + } + catch(SocketTimeoutException toe) + { + continue; + } + } + else + inputLine = ""; + + if ((inputLine += readLine(in)) != null) + { + String message = String.format("%s : %s", + socket.getInetAddress().getHostAddress(), inputLine); + SUTAgentAndroid.log(dc, message); + + outputLine = dc.processCommand(inputLine, out, in, cmdOut); + if (outputLine.length() > 0) + { + out.print(outputLine + "\n" + prompt); + } + else + out.print(prompt); + out.flush(); + if (outputLine.equals("exit")) + { + theParent.StopListening(); + bListening = false; + } + if (outputLine.equals("quit")) + { + bListening = false; + } + outputLine = null; + System.gc(); + } + else + break; + } + out.close(); + out = null; + in.close(); + in = null; + socket.close(); + SUTAgentAndroid.log(dc, "CmdWorkerThread ends: "+getId()); + } + catch (IOException e) + { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } +} diff --git a/build/mobile/sutagent/android/DataWorkerThread.java b/build/mobile/sutagent/android/DataWorkerThread.java new file mode 100644 index 000000000..e572a5c98 --- /dev/null +++ b/build/mobile/sutagent/android/DataWorkerThread.java @@ -0,0 +1,204 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package com.mozilla.SUTAgentAndroid.service; + +import java.io.BufferedInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.PrintWriter; +import java.net.Socket; +import java.net.SocketTimeoutException; +import java.text.SimpleDateFormat; +import java.util.Calendar; + +// import com.mozilla.SUTAgentAndroid.DoCommand; +import com.mozilla.SUTAgentAndroid.SUTAgentAndroid; + +public class DataWorkerThread extends Thread +{ + private RunDataThread theParent = null; + private Socket socket = null; + boolean bListening = true; + PrintWriter out = null; + SimpleDateFormat sdf = null; + + public DataWorkerThread(RunDataThread theParent, Socket workerSocket) + { + super("DataWorkerThread"); + this.theParent = theParent; + this.socket = workerSocket; + this.sdf = new SimpleDateFormat("yyyyMMdd-HH:mm:ss"); + } + + public void StopListening() + { + bListening = false; + } + + public void SendString(String strToSend) + { + if (this.out != null) + { + Calendar cal = Calendar.getInstance(); + String strOut = sdf.format(cal.getTime()); + strOut += " " + strToSend + "\r\n"; + + out.write(strOut); + out.flush(); + } + } + + private String readLine(BufferedInputStream in) + { + String sRet = ""; + int nByte = 0; + char cChar = 0; + + try + { + nByte = in.read(); + while (nByte != -1) + { + cChar = ((char)(nByte & 0xFF)); + if ((cChar != '\r') && (cChar != '\n')) + sRet += cChar; + else + break; + nByte = in.read(); + } + + if (in.available() > 0) + { + in.mark(1024); + nByte = in.read(); + + while (nByte != -1) + { + cChar = ((char)(nByte & 0xFF)); + if ((cChar == '\r') || (cChar == '\n')) + { + if (in.available() > 0) + { + in.mark(1024); + nByte = in.read(); + } + else + nByte = -1; + } + else + { + in.reset(); + break; + } + } + } + } + catch (IOException e) + { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + if (sRet.length() == 0) + sRet = null; + + return(sRet); + } + + public void run() + { + String sRet = ""; + long lEndTime = System.currentTimeMillis() + 60000; + + try { + while(bListening) + { + OutputStream cmdOut = socket.getOutputStream(); + InputStream cmdIn = socket.getInputStream(); + this.out = new PrintWriter(cmdOut, true); + BufferedInputStream in = new BufferedInputStream(cmdIn); + String inputLine, outputLine; + DoCommand dc = new DoCommand(theParent.svc); + + Calendar cal = Calendar.getInstance(); + sRet = sdf.format(cal.getTime()); + sRet += " trace output"; + + out.println(sRet); + out.flush(); + int nAvail = cmdIn.available(); + cmdIn.skip(nAvail); + + while (bListening) + { + if (System.currentTimeMillis() > lEndTime) + { + cal = Calendar.getInstance(); + sRet = sdf.format(cal.getTime()); + sRet += " Thump thump - " + SUTAgentAndroid.sUniqueID + "\r\n"; + + out.write(sRet); + out.flush(); + + lEndTime = System.currentTimeMillis() + 60000; + } + + if (!(in.available() > 0)) + { + socket.setSoTimeout(500); + try { + int nRead = cmdIn.read(); + if (nRead == -1) + { + bListening = false; + continue; + } + else + inputLine = (char)nRead + ""; + } + catch(SocketTimeoutException toe) + { + continue; + } + } + else + inputLine = ""; + + if ((inputLine += readLine(in)) != null) + { + outputLine = dc.processCommand(inputLine, out, in, cmdOut); + out.print(outputLine + "\n"); + out.flush(); + if (outputLine.equals("exit")) + { + theParent.StopListening(); + bListening = false; + } + if (outputLine.equals("quit")) + { + bListening = false; + } + outputLine = null; + System.gc(); + } + else + break; + } + + out.close(); + out = null; + in.close(); + in = null; + socket.close(); + } + } + catch (IOException e) + { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } +} diff --git a/build/mobile/sutagent/android/DoAlert.java b/build/mobile/sutagent/android/DoAlert.java new file mode 100644 index 000000000..ac11f2b15 --- /dev/null +++ b/build/mobile/sutagent/android/DoAlert.java @@ -0,0 +1,48 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package com.mozilla.SUTAgentAndroid.service; + +import java.util.TimerTask; + +import android.content.Context; +import android.content.ContextWrapper; +import android.media.Ringtone; +import android.media.RingtoneManager; +import android.widget.Toast; + +class DoAlert extends TimerTask + { + int lcv = 0; + Toast toast = null; + Ringtone rt = null; + + DoAlert(ContextWrapper contextWrapper) + { + Context ctx = contextWrapper.getApplicationContext(); + this.toast = Toast.makeText(ctx, "Help me!", Toast.LENGTH_LONG); + rt = RingtoneManager.getRingtone(ctx, RingtoneManager.getDefaultUri(RingtoneManager.TYPE_ALARM)); + } + + public void term() + { + if (rt != null) + { + if (rt.isPlaying()) + rt.stop(); + } + + if (toast != null) + toast.cancel(); + } + + public void run () + { + String sText =(((lcv++ % 2) == 0) ? "Help me!" : "I've fallen down!" ); + toast.setText(sText); + toast.show(); + if (rt != null) + rt.play(); + } + } diff --git a/build/mobile/sutagent/android/DoCommand.java b/build/mobile/sutagent/android/DoCommand.java new file mode 100644 index 000000000..8c7d071ea --- /dev/null +++ b/build/mobile/sutagent/android/DoCommand.java @@ -0,0 +1,3973 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package com.mozilla.SUTAgentAndroid.service; + +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.BufferedReader; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.FileReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.PrintWriter; +import java.io.RandomAccessFile; +import java.net.DatagramPacket; +import java.net.DatagramSocket; +import java.net.InetAddress; +import java.net.Socket; +import java.net.SocketException; +import java.net.UnknownHostException; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Date; +import java.util.GregorianCalendar; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.StringTokenizer; +import java.util.TimeZone; +import java.util.zip.Adler32; +import java.util.zip.CheckedInputStream; +import java.util.zip.CheckedOutputStream; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; +import java.util.zip.ZipInputStream; +import java.util.zip.ZipOutputStream; + +import org.apache.commons.net.ftp.FTP; +import org.apache.commons.net.ftp.FTPClient; +import org.apache.commons.net.ftp.FTPFile; +import org.apache.commons.net.ftp.FTPReply; +import org.apache.http.HttpResponse; +import org.apache.http.HttpStatus; +import org.apache.http.client.ClientProtocolException; +import org.apache.http.client.HttpClient; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.impl.client.DefaultHttpClient; + +import com.mozilla.SUTAgentAndroid.R; +import com.mozilla.SUTAgentAndroid.SUTAgentAndroid; + +import android.app.Activity; +import android.app.ActivityManager; +import android.app.AlarmManager; +import android.app.Notification; +import android.app.NotificationManager; +import android.app.PendingIntent; +import android.content.ActivityNotFoundException; +import android.content.ComponentName; +import android.content.ContentResolver; +import android.content.ContentValues; +import android.content.Context; +import android.content.ContextWrapper; +import android.content.Intent; +import android.content.pm.ActivityInfo; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.content.pm.ServiceInfo; +import android.content.pm.PackageManager.NameNotFoundException; +import android.database.Cursor; +import android.net.Uri; +import android.os.Build; +import android.os.Debug; +import android.os.Environment; +import android.os.StatFs; +import android.os.SystemClock; +import android.text.TextUtils; +import android.util.DisplayMetrics; +import android.util.Log; +import android.view.Surface; +import android.view.WindowManager; + +public class DoCommand { + + String lineSep = System.getProperty("line.separator"); + Process pProc; + OutputStream sutIn; + InputStream sutErr; + InputStream sutOut; + AlertLooperThread alrt = null; + ContextWrapper contextWrapper = null; + + String currentDir = "/"; + String sErrorPrefix = "##AGENT-WARNING## "; + boolean bTraceOn = false; + + String ffxProvider = "org.mozilla.ffxcp"; + String fenProvider = "org.mozilla.fencp"; + + private final String prgVersion = "SUTAgentAndroid Version 1.18"; + + public enum Command + { + RUN ("run"), + EXEC ("exec"), + EXECSU ("execsu"), + EXECCWD ("execcwd"), + EXECCWDSU ("execcwdsu"), + ENVRUN ("envrun"), + KILL ("kill"), + PS ("ps"), + DEVINFO ("info"), + OS ("os"), + ID ("id"), + UPTIME ("uptime"), + UPTIMEMILLIS ("uptimemillis"), + SUTUPTIMEMILLIS ("sutuptimemillis"), + SETTIME ("settime"), + SYSTIME ("systime"), + SCREEN ("screen"), + ROTATION ("rotation"), + MEMORY ("memory"), + POWER ("power"), + PROCESS ("process"), + SUTUSERINFO ("sutuserinfo"), + TEMPERATURE ("temperature"), + GETAPPROOT ("getapproot"), + TESTROOT ("testroot"), + ALRT ("alrt"), + DISK ("disk"), + CP ("cp"), + TIME ("time"), + HASH ("hash"), + CD ("cd"), + CAT ("cat"), + CWD ("cwd"), + MV ("mv"), + PUSH ("push"), + PULL ("pull"), + RM ("rm"), + PRUNE ("rmdr"), + MKDR ("mkdr"), + DIRWRITABLE ("dirw"), + ISDIR ("isdir"), + DEAD ("dead"), + MEMS ("mems"), + LS ("ls"), + TMPD ("tmpd"), + PING ("ping"), + REBT ("rebt"), + UNZP ("unzp"), + ZIP ("zip"), + CLOK ("clok"), + STAT ("stat"), + QUIT ("quit"), + EXIT ("exit"), + HELP ("help"), + FTPG ("ftpg"), + FTPP ("ftpp"), + INST ("inst"), + UPDT ("updt"), + UNINST ("uninst"), + UNINSTALL ("uninstall"), + TEST ("test"), + DBG ("dbg"), + TRACE ("trace"), + VER ("ver"), + TZGET ("tzget"), + TZSET ("tzset"), + ADB ("adb"), + CHMOD ("chmod"), + TOPACTIVITY ("activity"), + UNKNOWN ("unknown"); + + private final String theCmd; + + Command(String theCmd) { this.theCmd = theCmd; } + + public String theCmd() {return theCmd;} + + public static Command getCmd(String sCmd) + { + Command retCmd = UNKNOWN; + for (Command cmd : Command.values()) + { + if (cmd.theCmd().equalsIgnoreCase(sCmd)) + { + retCmd = cmd; + break; + } + } + return (retCmd); + } + } + + public DoCommand(ContextWrapper service) + { + this.contextWrapper = service; + } + + public String processCommand(String theCmdLine, PrintWriter out, BufferedInputStream in, OutputStream cmdOut) + { + String strReturn = ""; + Command cCmd = null; + Command cSubCmd = null; + + if (bTraceOn) + ((ASMozStub)this.contextWrapper).SendToDataChannel(theCmdLine); + + String [] Argv = parseCmdLine2(theCmdLine); + + int Argc = Argv.length; + + cCmd = Command.getCmd(Argv[0]); + + switch(cCmd) + { + case TRACE: + if (Argc == 2) + bTraceOn = (Argv[1].equalsIgnoreCase("on") ? true : false); + else + strReturn = sErrorPrefix + "Wrong number of arguments for trace command!"; + break; + + case VER: + strReturn = prgVersion; + break; + + case CLOK: + strReturn = GetClok(); + break; + + case TZGET: + strReturn = GetTimeZone(); + break; + + case TZSET: + if (Argc == 2) + strReturn = SetTimeZone(Argv[1]); + else + strReturn = sErrorPrefix + "Wrong number of arguments for settz command!"; + break; + + case UPDT: + if (Argc >= 2) + strReturn = StrtUpdtOMatic(Argv[1], Argv[2], (Argc > 3 ? Argv[3] : null), (Argc > 4 ? Argv[4] : null)); + else + strReturn = sErrorPrefix + "Wrong number of arguments for updt command!"; + break; + + case SETTIME: + strReturn = SetSystemTime(Argv[1], (Argc > 2 ? Argv[2] : null), cmdOut); + break; + + case CWD: + try { + strReturn = new java.io.File(currentDir).getCanonicalPath(); + } + catch (IOException e) + { + e.printStackTrace(); + } + break; + + case CD: + if (Argc == 2) + strReturn = changeDir(Argv[1]); + else + strReturn = sErrorPrefix + "Wrong number of arguments for cd command!"; + break; + + case LS: + strReturn = PrintDir(((Argc > 1) ? Argv[1] : currentDir)); + break; + + case GETAPPROOT: + if (Argc == 2) + strReturn = GetAppRoot(Argv[1]); + else + strReturn = sErrorPrefix + "Wrong number of arguments for getapproot command!"; + break; + + case ISDIR: + if (Argc == 2) + strReturn = isDirectory(Argv[1]); + else + strReturn = sErrorPrefix + "Wrong number of arguments for isdir command!"; + break; + + case TESTROOT: + strReturn = GetTestRoot(); + break; + + case DEAD: + if (Argc == 2) + strReturn = (IsProcessDead(Argv[1]) ? (Argv[1] + " is hung or unresponsive") : (Argv[1] + " is ok")); + else + strReturn = sErrorPrefix + "Wrong number of arguments for dead command!"; + break; + + case PS: + strReturn = GetProcessInfo(); + break; + + case PULL: + if (Argc >= 2) { + long lOff = 0; + long lLen = -1; + if (Argc > 2) { + try { + lOff = Long.parseLong(Argv[2].trim()); + } catch (NumberFormatException nfe) { + lOff = 0; + System.out.println("NumberFormatException: " + nfe.getMessage()); + } + } + if (Argc == 4) { + try { + lLen = Long.parseLong(Argv[3].trim()); + } catch (NumberFormatException nfe) { + lLen = -1; + System.out.println("NumberFormatException: " + nfe.getMessage()); + } + } + strReturn = Pull(Argv[1], lOff, lLen, cmdOut); + } else { + strReturn = sErrorPrefix + "Wrong number of arguments for pull command!"; + } + break; + + case PUSH: + if (Argc == 3) + { + long lArg = 0; + try + { + lArg = Long.parseLong(Argv[2].trim()); + } + catch (NumberFormatException nfe) + { + System.out.println("NumberFormatException: " + nfe.getMessage()); + } + + strReturn = Push(Argv[1], in, lArg); + } + else + strReturn = sErrorPrefix + "Wrong number of arguments for push command!"; + break; + + case INST: + if (Argc >= 2) + strReturn = InstallApp(Argv[1], cmdOut); + else + strReturn = sErrorPrefix + "Wrong number of arguments for inst command!"; + break; + + case UNINST: + if (Argc >= 2) + strReturn = UnInstallApp(Argv[1], cmdOut, true); + else + strReturn = sErrorPrefix + "Wrong number of arguments for uninst command!"; + break; + + case UNINSTALL: + if (Argc >= 2) + strReturn = UnInstallApp(Argv[1], cmdOut, false); + else + strReturn = sErrorPrefix + "Wrong number of arguments for uninstall command!"; + break; + + case ALRT: + if (Argc > 1) + { + if (Argv[1].contentEquals("on")) + { + String sTitle = "Agent Alert"; + String sMsg = "The Agent Alert System has been activated!"; + if (Argc == 3) { + sTitle = Argv[2]; + sMsg = ""; + } else if (Argc == 4) { + sTitle = Argv[2]; + sMsg = Argv[3]; + } + StartAlert(sTitle, sMsg); + } + else + { + StopAlert(); + } + } + else + { + strReturn = sErrorPrefix + "Wrong number of arguments for alrt command!"; + } + break; + + case REBT: + if (Argc >= 1) + strReturn = RunReboot(cmdOut, (Argc > 1 ? Argv[1] : null), (Argc > 2 ? Argv[2] : null)); + else + strReturn = sErrorPrefix + "Wrong number of arguments for rebt command!"; +// RunReboot(cmdOut); + break; + + case TMPD: + strReturn = GetTmpDir(); + break; + + case DEVINFO: + if (Argc == 1) + { + strReturn += SUTAgentAndroid.sUniqueID; + strReturn += "\n"; + strReturn += GetOSInfo(); + strReturn += "\n"; + strReturn += GetSystemTime(); + strReturn += "\n"; + strReturn += GetUptime(); + strReturn += "\n"; + strReturn += GetUptimeMillis(); + strReturn += "\n"; + strReturn += GetSutUptimeMillis(); + strReturn += "\n"; + strReturn += GetScreenInfo(); + strReturn += "\n"; + strReturn += GetRotationInfo(); + strReturn += "\n"; + strReturn += GetMemoryInfo(); + strReturn += "\n"; + strReturn += GetPowerInfo(); + strReturn += "\n"; + strReturn += GetTemperatureInfo(); + strReturn += "\n"; + strReturn += GetProcessInfo(); + strReturn += "\n"; + strReturn += GetSutUserInfo(); + strReturn += "\n"; + strReturn += GetDiskInfo("/data"); + strReturn += "\n"; + strReturn += GetDiskInfo("/system"); + strReturn += "\n"; + strReturn += GetDiskInfo("/mnt/sdcard"); + } + else + { + cSubCmd = Command.getCmd(Argv[1]); + switch(cSubCmd) + { + case ID: + strReturn = SUTAgentAndroid.sUniqueID; + break; + + case SCREEN: + strReturn = GetScreenInfo(); + break; + + case ROTATION: + strReturn = GetRotationInfo(); + break; + + case PROCESS: + strReturn = GetProcessInfo(); + break; + + case OS: + strReturn = GetOSInfo(); + break; + + case SYSTIME: + strReturn = GetSystemTime(); + break; + + case UPTIME: + strReturn = GetUptime(); + break; + + case UPTIMEMILLIS: + strReturn = GetUptimeMillis(); + break; + + case SUTUPTIMEMILLIS: + strReturn = GetSutUptimeMillis(); + break; + + case MEMORY: + strReturn = GetMemoryInfo(); + break; + + case POWER: + strReturn += GetPowerInfo(); + break; + + case SUTUSERINFO: + strReturn += GetSutUserInfo(); + break; + + case TEMPERATURE: + strReturn += GetTemperatureInfo(); + break; + + case DISK: + strReturn += "\n"; + strReturn += GetDiskInfo("/data"); + strReturn += "\n"; + strReturn += GetDiskInfo("/system"); + strReturn += "\n"; + strReturn += GetDiskInfo("/mnt/sdcard"); + break; + + default: + break; + } + } + break; + + case STAT: + if (Argc == 2) + strReturn = StatProcess(Argv[1]); + else + strReturn = sErrorPrefix + "Wrong number of arguments for ping command!"; + break; + + case PING: + if (Argc == 2) + strReturn = SendPing(Argv[1], cmdOut); + else + strReturn = sErrorPrefix + "Wrong number of arguments for ping command!"; + break; + + case HASH: + if (Argc == 2) + strReturn = HashFile(Argv[1]); + else + strReturn = sErrorPrefix + "Wrong number of arguments for hash command!"; + break; + + case PRUNE: + if (Argc == 2) + strReturn = PruneDir(Argv[1]); + else + strReturn = sErrorPrefix + "Wrong number of arguments for prune command!"; + break; + + case FTPG: + if (Argc == 4) + strReturn = FTPGetFile(Argv[1], Argv[2], Argv[3], cmdOut); + else + strReturn = sErrorPrefix + "Wrong number of arguments for ftpg command!"; + break; + + case CAT: + if (Argc == 2) + strReturn = Cat(Argv[1], cmdOut); + else + strReturn = sErrorPrefix + "Wrong number of arguments for cat command!"; + break; + + case DIRWRITABLE: + if (Argc == 2) + strReturn = IsDirWritable(Argv[1]); + else + strReturn = sErrorPrefix + "Wrong number of arguments for dirwritable command!"; + break; + + case TIME: + if (Argc == 2) + strReturn = PrintFileTimestamp(Argv[1]); + else + strReturn = sErrorPrefix + "Wrong number of arguments for time command!"; + break; + + case MKDR: + if (Argc == 2) + strReturn = MakeDir(Argv[1]); + else + strReturn = sErrorPrefix + "Wrong number of arguments for mkdr command!"; + break; + + case RM: + if (Argc == 2) + strReturn = RemoveFile(Argv[1]); + else + strReturn = sErrorPrefix + "Wrong number of arguments for rm command!"; + break; + + case MV: + if (Argc == 3) + strReturn = Move(Argv[1], Argv[2]); + else + strReturn = sErrorPrefix + "Wrong number of arguments for mv command!"; + break; + + case CP: + if (Argc == 3) + strReturn = CopyFile(Argv[1], Argv[2]); + else + strReturn = sErrorPrefix + "Wrong number of arguments for cp command!"; + break; + + case QUIT: + case EXIT: + strReturn = Argv[0]; + break; + + case DBG: + Debug.waitForDebugger(); + strReturn = "waitForDebugger on"; + break; + + case ADB: + if (Argc == 2) { + if (Argv[1].contains("ip") || Argv[1].contains("usb")) { + strReturn = SetADB(Argv[1]); + } else { + strReturn = sErrorPrefix + "Unrecognized argument for adb command!"; + } + } else { + strReturn = sErrorPrefix + "Wrong number of arguments for adb command!"; + } + break; + + case TEST: + long lFreeMemory = Runtime.getRuntime().freeMemory(); + long lTotMemory = Runtime.getRuntime().totalMemory(); + long lMaxMemory = Runtime.getRuntime().maxMemory(); + + + if (lFreeMemory > 0) { + strReturn = "Max memory: " + lMaxMemory + "\nTotal Memory: " + lTotMemory + "\nFree memory: " + lFreeMemory; + break; + } + + ContentResolver cr = contextWrapper.getContentResolver(); + Uri ffxFiles = null; + + if (Argv[1].contains("fennec")) { + ffxFiles = Uri.parse("content://" + fenProvider + "/dir"); + } else if (Argv[1].contains("firefox")) { + ffxFiles = Uri.parse("content://" + ffxProvider + "/dir"); + } + +// Uri ffxFiles = Uri.parse("content://org.mozilla.fencp/file"); + String[] columns = new String[] { + "_id", + "isdir", + "filename", + "length" + }; +// String[] columns = new String[] { +// "_id", +// "chunk" +// }; + Cursor myCursor = cr.query( ffxFiles, + columns, // Which columns to return + (Argc > 1 ? Argv[1] : null), // Which rows to return (all rows) + null, // Selection arguments (none) + null); // Put the results in ascending order by name +/* + if (myCursor != null) { + int nRows = myCursor.getCount(); + String [] colNames = myCursor.getColumnNames(); + int nID = 0; + int nBytesRecvd = 0; + + for (int lcv = 0; lcv < nRows; lcv++) { + if (myCursor.moveToPosition(lcv)) { + nID = myCursor.getInt(0); + byte [] buf = myCursor.getBlob(1); + if (buf != null) { + nBytesRecvd += buf.length; + strReturn += new String(buf); + buf = null; + } + } + } + strReturn += "[eof - " + nBytesRecvd + "]"; + myCursor.close(); + } + +*/ + if (myCursor != null) + { + int nRows = myCursor.getCount(); + int nID = 0; + String sFileName = ""; + long lFileSize = 0; + boolean bIsDir = false; + + for (int lcv = 0; lcv < nRows; lcv++) + { + if (myCursor.moveToPosition(lcv)) + { + nID = myCursor.getInt(0); + bIsDir = (myCursor.getInt(1) == 1 ? true : false); + sFileName = myCursor.getString(2); + lFileSize = myCursor.getLong(3); + strReturn += "" + nID + "\t" + (bIsDir ? "<dir> " : " ") + sFileName + "\t" + lFileSize + "\n"; + } + } + myCursor.close(); + } + break; + + case EXEC: + case ENVRUN: + if (Argc >= 2) + { + String [] theArgs = new String [Argc - 1]; + + for (int lcv = 1; lcv < Argc; lcv++) + { + theArgs[lcv - 1] = Argv[lcv]; + } + + strReturn = StartPrg2(theArgs, cmdOut, null, false); + } + else + { + strReturn = sErrorPrefix + "Wrong number of arguments for " + Argv[0] + " command!"; + } + break; + + case EXECSU: + if (Argc >= 2) + { + String [] theArgs = new String [Argc - 1]; + + for (int lcv = 1; lcv < Argc; lcv++) + { + theArgs[lcv - 1] = Argv[lcv]; + } + + strReturn = StartPrg2(theArgs, cmdOut, null, true); + } + else + { + strReturn = sErrorPrefix + "Wrong number of arguments for " + Argv[0] + " command!"; + } + break; + + case EXECCWD: + if (Argc >= 3) + { + String [] theArgs = new String [Argc - 2]; + + for (int lcv = 2; lcv < Argc; lcv++) + { + theArgs[lcv - 2] = Argv[lcv]; + } + + strReturn = StartPrg2(theArgs, cmdOut, Argv[1], false); + } + else + { + strReturn = sErrorPrefix + "Wrong number of arguments for " + Argv[0] + " command!"; + } + break; + + case EXECCWDSU: + if (Argc >= 3) + { + String [] theArgs = new String [Argc - 2]; + + for (int lcv = 2; lcv < Argc; lcv++) + { + theArgs[lcv - 2] = Argv[lcv]; + } + + strReturn = StartPrg2(theArgs, cmdOut, Argv[1], true); + } + else + { + strReturn = sErrorPrefix + "Wrong number of arguments for " + Argv[0] + " command!"; + } + break; + + case RUN: + if (Argc >= 2) + { + String [] theArgs = new String [Argc - 1]; + + for (int lcv = 1; lcv < Argc; lcv++) + { + theArgs[lcv - 1] = Argv[lcv]; + } + + if (Argv[1].contains("/") || Argv[1].contains("\\") || !Argv[1].contains(".")) + strReturn = StartPrg(theArgs, cmdOut, false); + else + strReturn = StartJavaPrg(theArgs, null); + } + else + { + strReturn = sErrorPrefix + "Wrong number of arguments for " + Argv[0] + " command!"; + } + break; + + case KILL: + if (Argc == 2) + strReturn = KillProcess(Argv[1], cmdOut); + else + strReturn = sErrorPrefix + "Wrong number of arguments for kill command!"; + break; + + case DISK: + strReturn = GetDiskInfo((Argc == 2 ? Argv[1] : "/")); + break; + + case UNZP: + strReturn = Unzip(Argv[1], (Argc == 3 ? Argv[2] : "")); + break; + + case ZIP: + strReturn = Zip(Argv[1], (Argc == 3 ? Argv[2] : "")); + break; + + case CHMOD: + if (Argc == 2) + strReturn = ChmodDir(Argv[1]); + else + strReturn = sErrorPrefix + "Wrong number of arguments for chmod command!"; + break; + + case TOPACTIVITY: + strReturn = TopActivity(); + break; + + case HELP: + strReturn = PrintUsage(); + break; + + default: + strReturn = sErrorPrefix + "[" + Argv[0] + "] command"; + if (Argc > 1) + { + strReturn += " with arg(s) ="; + for (int lcv = 1; lcv < Argc; lcv++) + { + strReturn += " [" + Argv[lcv] + "]"; + } + } + strReturn += " is currently not implemented."; + break; + } + + return(strReturn); + } + + private void SendNotification(String tickerText, String expandedText) { + NotificationManager notificationManager = (NotificationManager)contextWrapper.getSystemService(Context.NOTIFICATION_SERVICE); + int icon = R.drawable.ateamlogo; + long when = System.currentTimeMillis(); + + Notification notification = new Notification(icon, tickerText, when); + + notification.flags |= (Notification.FLAG_INSISTENT | Notification.FLAG_AUTO_CANCEL); + notification.defaults |= Notification.DEFAULT_SOUND; + notification.defaults |= Notification.DEFAULT_VIBRATE; + notification.defaults |= Notification.DEFAULT_LIGHTS; + + Context context = contextWrapper.getApplicationContext(); + + // Intent to launch an activity when the extended text is clicked + Intent intent2 = new Intent(contextWrapper, SUTAgentAndroid.class); + PendingIntent launchIntent = PendingIntent.getActivity(context, 0, intent2, 0); + + notification.setLatestEventInfo(context, tickerText, expandedText, launchIntent); + + notificationManager.notify(1959, notification); + } + +private void CancelNotification() + { + NotificationManager notificationManager = (NotificationManager)contextWrapper.getSystemService(Context.NOTIFICATION_SERVICE); + notificationManager.cancel(1959); + } + + public void StartAlert(String sTitle, String sMsg) + { + // start the alert message + SendNotification(sTitle, sMsg); + } + + public void StopAlert() + { + CancelNotification(); + } + + public String [] parseCmdLine2(String theCmdLine) + { + String cmdString; + String workingString; + String workingString2; + String workingString3; + List<String> lst = new ArrayList<String>(); + int nLength = 0; + int nFirstSpace = -1; + + // Null cmd line + if (theCmdLine == null) + { + String [] theArgs = new String [1]; + theArgs[0] = new String(""); + return(theArgs); + } + else + { + nLength = theCmdLine.length(); + nFirstSpace = theCmdLine.indexOf(' '); + } + + if (nFirstSpace == -1) + { + String [] theArgs = new String [1]; + theArgs[0] = new String(theCmdLine); + return(theArgs); + } + + // Get the command + cmdString = new String(theCmdLine.substring(0, nFirstSpace)); + lst.add(cmdString); + + // Jump past the command and trim + workingString = (theCmdLine.substring(nFirstSpace + 1, nLength)).trim(); + + while ((nLength = workingString.length()) > 0) + { + int nEnd = 0; + int nStart = 0; + + // if we have a quote + if (workingString.startsWith("\"") || workingString.startsWith("'")) + { + char quoteChar = '"'; + if (workingString.startsWith("\'")) + quoteChar = '\''; + + // point to the first non quote char + nStart = 1; + // find the matching quote + nEnd = workingString.indexOf(quoteChar, nStart); + + char prevChar; + + while(nEnd != -1) + { + // check to see if the quotation mark has been escaped + prevChar = workingString.charAt(nEnd - 1); + if (prevChar == '\\') + { + // if escaped, point past this quotation mark and find the next + nEnd++; + if (nEnd < nLength) + nEnd = workingString.indexOf(quoteChar, nEnd); + else + nEnd = -1; + } + else + break; + } + + // there isn't one + if (nEnd == -1) + { + // point at the quote + nStart = 0; + // so find the next space + nEnd = workingString.indexOf(' ', nStart); + // there isn't one of those either + if (nEnd == -1) + nEnd = nLength; // Just grab the rest of the cmdline + } + } + else // no quote so find the next space + { + nEnd = workingString.indexOf(' ', nStart); + // there isn't one of those + if (nEnd == -1) + nEnd = nLength; // Just grab the rest of the cmdline + } + + // get the substring + workingString2 = workingString.substring(nStart, nEnd); + + // if we have escaped quotes, convert them into standard ones + while (workingString2.contains("\\\"") || workingString2.contains("\\'")) + { + workingString2 = workingString2.replace("\\\"", "\""); + workingString2 = workingString2.replace("\\'", "'"); + } + + // add it to the list + lst.add(new String(workingString2)); + + // if we are dealing with a quote + if (nStart > 0) + nEnd++; // point past the end one + + // jump past the substring and trim it + workingString = (workingString.substring(nEnd)).trim(); + } + + // ok we're done package up the results + int nItems = lst.size(); + + String [] theArgs = new String [nItems]; + + for (int lcv = 0; lcv < nItems; lcv++) + { + theArgs[lcv] = lst.get(lcv); + } + + return(theArgs); + } + + public String [] parseCmdLine(String theCmdLine) { + String cmdString; + String workingString; + String workingString2; + List<String> lst = new ArrayList<String>(); + int nLength = 0; + int nFirstSpace = -1; + + // Null cmd line + if (theCmdLine == null) + { + String [] theArgs = new String [1]; + theArgs[0] = new String(""); + return(theArgs); + } + else + { + nLength = theCmdLine.length(); + nFirstSpace = theCmdLine.indexOf(' '); + } + + if (nFirstSpace == -1) + { + String [] theArgs = new String [1]; + theArgs[0] = new String(theCmdLine); + return(theArgs); + } + + // Get the command + cmdString = new String(theCmdLine.substring(0, nFirstSpace)); + lst.add(cmdString); + + // Jump past the command and trim + workingString = (theCmdLine.substring(nFirstSpace + 1, nLength)).trim(); + + while ((nLength = workingString.length()) > 0) + { + int nEnd = 0; + int nStart = 0; + + // if we have a quote + if (workingString.startsWith("\"")) + { + // point to the first non quote char + nStart = 1; + // find the matching quote + nEnd = workingString.indexOf('"', nStart); + // there isn't one + if (nEnd == -1) + { + // point at the quote + nStart = 0; + // so find the next space + nEnd = workingString.indexOf(' ', nStart); + // there isn't one of those either + if (nEnd == -1) + nEnd = nLength; // Just grab the rest of the cmdline + } + else + { + nStart = 0; + nEnd++; + } + } + else // no quote so find the next space + { + nEnd = workingString.indexOf(' ', nStart); + + // there isn't one of those + if (nEnd == -1) + nEnd = nLength; // Just grab the rest of the cmdline + } + + // get the substring + workingString2 = workingString.substring(nStart, nEnd); + + // add it to the list + lst.add(new String(workingString2)); + + // jump past the substring and trim it + workingString = (workingString.substring(nEnd)).trim(); + } + + int nItems = lst.size(); + + String [] theArgs = new String [nItems]; + + for (int lcv = 0; lcv < nItems; lcv++) + { + theArgs[lcv] = lst.get(lcv); + } + + return(theArgs); + } + + public String fixFileName(String fileName) + { + String sRet = ""; + String sTmpFileName = ""; + + sRet = fileName.replace('\\', '/'); + + if (sRet.startsWith("/")) + sTmpFileName = sRet; + else + sTmpFileName = currentDir + "/" + sRet; + + sRet = sTmpFileName.replace('\\', '/'); + sTmpFileName = sRet; + sRet = sTmpFileName.replace("//", "/"); + + return(sRet); + } + + public String AddFilesToZip(ZipOutputStream out, String baseDir, String relDir) + { + final int BUFFER = 2048; + String sRet = ""; + String curDir = ""; + String relFN = ""; + BufferedInputStream origin = null; + byte data[] = new byte[BUFFER]; + + if (relDir.length() > 0) + curDir = baseDir + "/" + relDir; + else + curDir = baseDir; + + File f = new File(curDir); + + if (f.isFile()) + { + try { + relFN = ((relDir.length() > 0) ? relDir + "/" + f.getName() : f.getName()); + System.out.println("Adding: "+relFN); + sRet += "Adding: "+ relFN + lineSep; + FileInputStream fi = new FileInputStream(curDir); + origin = new BufferedInputStream(fi, BUFFER); + ZipEntry entry = new ZipEntry(relFN); + out.putNextEntry(entry); + int count; + while((count = origin.read(data, 0, BUFFER)) != -1) + { + out.write(data, 0, count); + } + origin.close(); + } + catch(Exception e) + { + e.printStackTrace(); + } + + return(sRet); + } + + String files[] = f.list(); + + if (files != null) + { + try { + for(int i = 0; i < files.length; i++) + { + f = new File(curDir + "/" + files[i]); + if (f.isDirectory()) + { + if (relDir.length() > 0) + sRet += AddFilesToZip(out, baseDir, relDir + "/" + files[i]); + else + sRet += AddFilesToZip(out, baseDir, files[i]); + } + else + { + relFN = ((relDir.length() > 0) ? relDir + "/" + files[i] : files[i]); + System.out.println("Adding: "+relFN); + sRet += "Adding: "+ relFN + lineSep; + FileInputStream fi = new FileInputStream(curDir + "/" + files[i]); + origin = new BufferedInputStream(fi, BUFFER); + ZipEntry entry = new ZipEntry(relFN); + out.putNextEntry(entry); + int count; + while((count = origin.read(data, 0, BUFFER)) != -1) + { + out.write(data, 0, count); + } + origin.close(); + } + } + } + catch(Exception e) + { + e.printStackTrace(); + } + } + + return(sRet); + } + + public String Zip(String zipFileName, String srcName) + { + String fixedZipFileName = fixFileName(zipFileName); + String fixedSrcName = fixFileName(srcName); + String sRet = ""; + + try { + FileOutputStream dest = new FileOutputStream(fixedZipFileName); + CheckedOutputStream checksum = new CheckedOutputStream(dest, new Adler32()); + ZipOutputStream out = new ZipOutputStream(new BufferedOutputStream(checksum)); + out.setMethod(ZipOutputStream.DEFLATED); + + sRet += AddFilesToZip(out, fixedSrcName, ""); + + out.close(); + System.out.println("checksum: "+checksum.getChecksum().getValue()); + sRet += "checksum: "+checksum.getChecksum().getValue(); + } + catch(Exception e) + { + e.printStackTrace(); + } + + return(sRet); + } + + public String Unzip(String zipFileName, String dstDirectory) + { + String sRet = ""; + String fixedZipFileName = fixFileName(zipFileName); + String fixedDstDirectory = fixFileName(dstDirectory); + String dstFileName = ""; + int nNumExtracted = 0; + boolean bRet = false; + + try { + final int BUFFER = 2048; + BufferedOutputStream dest = null; + ZipFile zipFile = new ZipFile(fixedZipFileName); + int nNumEntries = zipFile.size(); + zipFile.close(); + + FileInputStream fis = new FileInputStream(fixedZipFileName); + CheckedInputStream checksum = new CheckedInputStream(fis, new Adler32()); + ZipInputStream zis = new ZipInputStream(new BufferedInputStream(checksum)); + ZipEntry entry; + + byte [] data = new byte[BUFFER]; + + while((entry = zis.getNextEntry()) != null) + { + System.out.println("Extracting: " + entry); + int count; + if (fixedDstDirectory.length() > 0) + dstFileName = fixedDstDirectory + entry.getName(); + else + dstFileName = entry.getName(); + + String tmpDir = dstFileName.substring(0, dstFileName.lastIndexOf('/')); + File tmpFile = new File(tmpDir); + if (!tmpFile.exists()) + { + bRet = tmpFile.mkdirs(); + } + else + bRet = true; + + if (bRet) + { + // if we aren't just creating a directory + if (dstFileName.lastIndexOf('/') != (dstFileName.length() - 1)) + { + // write out the file + FileOutputStream fos = new FileOutputStream(dstFileName); + dest = new BufferedOutputStream(fos, BUFFER); + while ((count = zis.read(data, 0, BUFFER)) != -1) + { + dest.write(data, 0, count); + } + dest.flush(); + dest.close(); + dest = null; + fos.close(); + fos = null; + } + nNumExtracted++; + } + else + sRet += " - failed" + lineSep; + } + + data = null; + zis.close(); + System.out.println("Checksum: "+checksum.getChecksum().getValue()); + sRet += "Checksum: "+checksum.getChecksum().getValue(); + sRet += lineSep + nNumExtracted + " of " + nNumEntries + " successfully extracted"; + } + catch(Exception e) + { + e.printStackTrace(); + } + + return(sRet); + } + + public String StatProcess(String string) + { + String sRet = ""; + ActivityManager aMgr = (ActivityManager) contextWrapper.getSystemService(Activity.ACTIVITY_SERVICE); + int [] nPids = new int [1]; + + nPids[0] = Integer.parseInt(string); + + android.os.Debug.MemoryInfo[] mi = aMgr.getProcessMemoryInfo(nPids); + + sRet = "Dalvik Private Dirty pages " + mi[0].dalvikPrivateDirty + " kb\n"; + sRet += "Dalvik Proportional Set Size " + mi[0].dalvikPss + " kb\n"; + sRet += "Dalvik Shared Dirty pages " + mi[0].dalvikSharedDirty + " kb\n\n"; + sRet += "Native Private Dirty pages heap " + mi[0].nativePrivateDirty + " kb\n"; + sRet += "Native Proportional Set Size heap " + mi[0].nativePss + " kb\n"; + sRet += "Native Shared Dirty pages heap " + mi[0].nativeSharedDirty + " kb\n\n"; + sRet += "Other Private Dirty pages " + mi[0].otherPrivateDirty + " kb\n"; + sRet += "Other Proportional Set Size " + mi[0].otherPss + " kb\n"; + sRet += "Other Shared Dirty pages " + mi[0].otherSharedDirty + " kb\n\n"; + sRet += "Total Private Dirty Memory " + mi[0].getTotalPrivateDirty() + " kb\n"; + sRet += "Total Proportional Set Size Memory " + mi[0].getTotalPss() + " kb\n"; + sRet += "Total Shared Dirty Memory " + mi[0].getTotalSharedDirty() + " kb"; + + + return(sRet); + } + + public void FixDataLocalPermissions() + { + String chmodResult; + File localDir = new java.io.File("/data/local"); + if (!localDir.canWrite()) { + chmodResult = ChmodDir("/data/local"); + Log.i("SUTAgentAndroid", "Changed permissions on /data/local to make it writable: " + chmodResult); + } + File tmpDir = new java.io.File("/data/local/tmp"); + if (tmpDir.exists() && !tmpDir.isDirectory()) { + if (!tmpDir.delete()) { + Log.e("SUTAgentAndroid", "Could not delete file /data/local/tmp"); + } + } + if (!tmpDir.exists() && !tmpDir.mkdirs()) { + Log.e("SUTAgentAndroid", "Could not create directory /data/local/tmp"); + } + chmodResult = ChmodDir("/data/local/tmp"); + Log.i("SUTAgentAndroid", "Changed permissions on /data/local/tmp to make it writable: " + chmodResult); + } + + public String GetTestRoot() + { + String state = Environment.getExternalStorageState(); + // Ensure sdcard is mounted and NOT read only + if (state.equalsIgnoreCase(Environment.MEDIA_MOUNTED) && + (Environment.MEDIA_MOUNTED_READ_ONLY.compareTo(state) != 0)) + { + return(Environment.getExternalStorageDirectory().getAbsolutePath()); + } + File tmpFile = new java.io.File("/data/local/tmp/tests"); + try{ + tmpFile.createNewFile(); + } catch (IOException e){ + Log.i("SUTAgentAndroid", "Caught exception creating file in /data/local/tmp: " + e.getMessage()); + } + if (tmpFile.exists()) + { + tmpFile.delete(); + return("/data/local"); + } + Log.e("SUTAgentAndroid", "ERROR: Cannot access world writeable test root"); + + return(null); + } + + public String GetAppRoot(String AppName) + { + String sRet = sErrorPrefix + " internal error [no context]"; + Context ctx = contextWrapper.getApplicationContext(); + + if (ctx != null) + { + try { + Context appCtx = ctx.createPackageContext(AppName, 0); + ContextWrapper appCtxW = new ContextWrapper(appCtx); + sRet = appCtxW.getApplicationInfo().dataDir; + appCtxW = null; + appCtx = null; + ctx = null; + System.gc(); + } + catch (NameNotFoundException e) + { + e.printStackTrace(); + } + } + return(sRet); + } + + public String isDirectory(String sDir) + { + String sRet = sErrorPrefix + sDir + " does not exist"; + String tmpDir = fixFileName(sDir); + int nFiles = 0; + + if (tmpDir.contains("org.mozilla.fennec") || tmpDir.contains("org.mozilla.firefox")) { + ContentResolver cr = contextWrapper.getContentResolver(); + Uri ffxFiles = Uri.parse("content://" + (tmpDir.contains("fennec") ? fenProvider : ffxProvider) + "/dir"); + + String[] columns = new String[] { + "_id", + "isdir", + "filename", + "length" + }; + + Cursor myCursor = cr.query( ffxFiles, + columns, // Which columns to return + tmpDir, // Which rows to return (all rows) + null, // Selection arguments (none) + null); // Order clause (none) + if (myCursor != null) { + nFiles = myCursor.getCount(); + + // If no entries the dir is empty + if (nFiles > 0) { + if (myCursor.moveToPosition(0)) { + sRet = ((myCursor.getLong(myCursor.getColumnIndex("isdir")) == 1) ? "TRUE" : "FALSE"); + } + } + myCursor.close(); + } + } else { + File tmpFile = new java.io.File(tmpDir); + + if (tmpFile.exists()) { + sRet = (tmpFile.isDirectory() ? "TRUE" : "FALSE"); + } + else { + try { + pProc = Runtime.getRuntime().exec(this.getSuArgs("ls -l " + sDir)); + RedirOutputThread outThrd = new RedirOutputThread(pProc, null); + outThrd.start(); + outThrd.joinAndStopRedirect(5000); + sRet = outThrd.strOutput; + if (!sRet.contains("No such file or directory") && sRet.startsWith("l")) + sRet = "FALSE"; + } + catch (IOException e) { + sRet = e.getMessage(); + e.printStackTrace(); + } + catch (InterruptedException e) { + e.printStackTrace(); + } + } + } + + return(sRet); + } + + + public String changeDir(String newDir) + { + String tmpDir = fixFileName(newDir); + String sRet = sErrorPrefix + "Couldn't change directory to " + tmpDir; + int nFiles = 0; + + if (tmpDir.contains("org.mozilla.fennec") || tmpDir.contains("org.mozilla.firefox")) { + ContentResolver cr = contextWrapper.getContentResolver(); + Uri ffxFiles = Uri.parse("content://" + (tmpDir.contains("fennec") ? fenProvider : ffxProvider) + "/dir"); + + String[] columns = new String[] { + "_id", + "isdir", + "filename" + }; + + Cursor myCursor = cr.query( ffxFiles, + columns, // Which columns to return + tmpDir, // Which rows to return (all rows) + null, // Selection arguments (none) + null); // Order clause (none) + if (myCursor != null) { + nFiles = myCursor.getCount(); + + if (nFiles > 0) { + if (myCursor.moveToPosition(0)) { + if (myCursor.getLong(myCursor.getColumnIndex("isdir")) == 1) { + currentDir = myCursor.getString(myCursor.getColumnIndex("filename")); + sRet = ""; + } + } + } else { + sRet = sErrorPrefix + tmpDir + " is not a valid directory"; + } + myCursor.close(); + } + } else { + File tmpFile = new java.io.File(tmpDir); + + if (tmpFile.exists()) { + try { + if (tmpFile.isDirectory()) { + currentDir = tmpFile.getCanonicalPath(); + sRet = ""; + } + else + sRet = sErrorPrefix + tmpDir + " is not a valid directory"; + } catch (IOException e) { + e.printStackTrace(); + } + } + } + + return(sRet); + } + + static final String HEXES = "0123456789abcdef"; + + public static String getHex( byte [] raw ) + { + if ( raw == null ) + { + return null; + } + + final StringBuilder hex = new StringBuilder( 2 * raw.length ); + for ( final byte b : raw ) + { + hex.append(HEXES.charAt((b & 0xF0) >> 4)).append(HEXES.charAt((b & 0x0F))); + } + return hex.toString(); + } + + public String HashFile(String fileName) + { + String sTmpFileName = fixFileName(fileName); + String sRet = sErrorPrefix + "Couldn't calculate hash for file " + sTmpFileName; + byte[] buffer = new byte [4096]; + int nRead = 0; + long lTotalRead = 0; + MessageDigest digest = null; + + try { + digest = java.security.MessageDigest.getInstance("MD5"); + } + catch (NoSuchAlgorithmException e) + { + e.printStackTrace(); + } + + if (sTmpFileName.contains("org.mozilla.fennec") || sTmpFileName.contains("org.mozilla.firefox")) { + ContentResolver cr = contextWrapper.getContentResolver(); + Uri ffxFiles = null; + + ffxFiles = Uri.parse("content://" + (sTmpFileName.contains("fennec") ? fenProvider : ffxProvider) + "/file"); + + String[] columns = new String[] { + "_id", + "chunk" + }; + + Cursor myCursor = cr.query( ffxFiles, + columns, // Which columns to return + sTmpFileName, // Which rows to return (all rows) + null, // Selection arguments (none) + null); // Order clause (none) + if (myCursor != null) { + int nRows = myCursor.getCount(); + int nBytesRecvd = 0; + + for (int lcv = 0; lcv < nRows; lcv++) { + if (myCursor.moveToPosition(lcv)) { + byte [] buf = myCursor.getBlob(1); + if (buf != null) { + nBytesRecvd += buf.length; + digest.update(buf, 0, buf.length); + lTotalRead += nRead; + buf = null; + } + } + } + myCursor.close(); + byte [] hash = digest.digest(); + + sRet = getHex(hash); + } + } else { + try { + FileInputStream srcFile = new FileInputStream(sTmpFileName); + while((nRead = srcFile.read(buffer)) != -1) { + digest.update(buffer, 0, nRead); + lTotalRead += nRead; + } + srcFile.close(); + byte [] hash = digest.digest(); + + sRet = getHex(hash); + } + catch (FileNotFoundException e) { + sRet += " file not found"; + } + catch (IOException e) { + sRet += " io exception"; + e.printStackTrace(); + } + } + return(sRet); + } + + public String RemoveFile(String fileName) + { + String sTmpFileName = fixFileName(fileName); + String sRet = sErrorPrefix + "Couldn't delete file " + sTmpFileName; + + if (sTmpFileName.contains("org.mozilla.fennec") || sTmpFileName.contains("org.mozilla.firefox")) { + ContentResolver cr = contextWrapper.getContentResolver(); + Uri ffxFiles = Uri.parse("content://" + (sTmpFileName.contains("fennec") ? fenProvider : ffxProvider) + "/file"); + if (cr.delete(ffxFiles, sTmpFileName, null) == 1) { + sRet = "deleted " + sTmpFileName; + } + } else { + File f = new File(sTmpFileName); + + if (f.delete()) + sRet = "deleted " + sTmpFileName; + } + + return(sRet); + } + + public String PruneDir(String sDir) + { + String sRet = ""; + int nFiles = 0; + String sSubDir = null; + String sTmpDir = fixFileName(sDir); + + if (sTmpDir.contains("org.mozilla.fennec") || sTmpDir.contains("org.mozilla.firefox")) { + ContentResolver cr = contextWrapper.getContentResolver(); + Uri ffxFiles = Uri.parse("content://" + (sTmpDir.contains("fennec") ? fenProvider : ffxProvider) + "/dir"); + if (cr.delete(ffxFiles, sTmpDir, null) > 0) { + sRet = "deleted " + sTmpDir; + } + } else { + File dir = new File(sTmpDir); + + if (dir.isDirectory()) { + sRet = "Deleting file(s) from " + sTmpDir; + + File [] files = dir.listFiles(); + if (files != null) { + if ((nFiles = files.length) > 0) { + for (int lcv = 0; lcv < nFiles; lcv++) { + if (files[lcv].isDirectory()) { + sSubDir = files[lcv].getAbsolutePath(); + sRet += "\n" + PruneDir(sSubDir); + } + else { + if (files[lcv].delete()) { + sRet += "\n\tDeleted " + files[lcv].getName(); + } + else { + sRet += "\n\tUnable to delete " + files[lcv].getName(); + } + } + } + } + else + sRet += "\n\t<empty>"; + } + + if (dir.delete()) { + sRet += "\nDeleting directory " + sTmpDir; + } + else { + sRet += "\nUnable to delete directory " + sTmpDir; + } + } + else { + sRet += sErrorPrefix + sTmpDir + " is not a directory"; + } + } + + return(sRet); + } + + public String PrintDir(String sDir) + { + String sRet = ""; + int nFiles = 0; + String sTmpDir = fixFileName(sDir); + + if (sTmpDir.contains("org.mozilla.fennec") || sTmpDir.contains("org.mozilla.firefox")) { + ContentResolver cr = contextWrapper.getContentResolver(); + Uri ffxFiles = null; + + ffxFiles = Uri.parse("content://" + (sTmpDir.contains("fennec") ? fenProvider : ffxProvider) + "/dir"); + + String[] columns = new String[] { + "_id", + "isdir", + "filename", + "length" + }; + + Cursor myCursor = cr.query( ffxFiles, + columns, // Which columns to return + sTmpDir, // Which rows to return (all rows) + null, // Selection arguments (none) + null); // Order clause (none) + if (myCursor != null) { + nFiles = myCursor.getCount(); + + // If only one entry and the index is -1 this is not a directory + int nNdx = myCursor.getColumnIndex("_id"); + // If no entries the dir is empty + if (nFiles == 1) { + sRet = "<empty>"; + } else { + // Show the entries + for (int lcv = 1; lcv < nFiles; lcv++) { + if (myCursor.moveToPosition(lcv)) { + if ((lcv == 0) && (myCursor.getLong(nNdx) == -1)) { + sRet = sErrorPrefix + sTmpDir + " is not a directory"; + } else { + sRet += myCursor.getString(2); + if (lcv < (nFiles - 1)) + sRet += "\n"; + } + } + } + } + myCursor.close(); + } + } else { + File dir = new File(sTmpDir); + + if (dir.isDirectory()) { + File [] files = dir.listFiles(); + + if (files != null) { + if ((nFiles = files.length) > 0) { + for (int lcv = 0; lcv < nFiles; lcv++) { + sRet += files[lcv].getName(); + if (lcv < (nFiles - 1)) { + sRet += "\n"; + } + } + } + else { + sRet = "<empty>"; + } + } + } + else { + sRet = sErrorPrefix + sTmpDir + " is not a directory"; + } + } + return(sRet); + } + + public String Move(String sTmpSrcFileName, String sTmpDstFileName) { + String sRet = sErrorPrefix + "Could not move " + sTmpSrcFileName + " to " + sTmpDstFileName; + String sTmp = CopyFile(sTmpSrcFileName, sTmpDstFileName); + if (sTmp.contains(" copied to ")) { + sTmp = RemoveFile(sTmpSrcFileName); + if (sTmp.startsWith("deleted ")) { + sRet = sTmpSrcFileName + " moved to " + sTmpDstFileName; + } + } + + return(sRet); + } + + public String CopyFile(String sTmpSrcFileName, String sTmpDstFileName) { + String sRet = sErrorPrefix + "Could not copy " + sTmpSrcFileName + " to " + sTmpDstFileName; + ContentValues cv = null; + File destFile = null; + Uri ffxSrcFiles = null; + Uri ffxDstFiles = null; + FileInputStream srcFile = null; + FileOutputStream dstFile = null; + byte[] buffer = new byte [4096]; + int nRead = 0; + long lTotalRead = 0; + long lTotalWritten = 0; + ContentResolver crIn = null; + ContentResolver crOut = null; + + if (sTmpSrcFileName.contains("org.mozilla.fennec") || sTmpSrcFileName.contains("org.mozilla.firefox")) { + ffxSrcFiles = Uri.parse("content://" + (sTmpSrcFileName.contains("fennec") ? fenProvider : ffxProvider) + "/file"); + crIn = contextWrapper.getContentResolver(); + } else { + try { + srcFile = new FileInputStream(sTmpSrcFileName); + } catch (FileNotFoundException e) { + e.printStackTrace(); + } + } + + if (sTmpDstFileName.contains("org.mozilla.fennec") || sTmpDstFileName.contains("org.mozilla.firefox")) { + ffxDstFiles = Uri.parse("content://" + (sTmpDstFileName.contains("fennec") ? fenProvider : ffxProvider) + "/file"); + crOut = contextWrapper.getContentResolver(); + cv = new ContentValues(); + } else { + try { + dstFile = new FileOutputStream(sTmpDstFileName); + } catch (FileNotFoundException e) { + e.printStackTrace(); + } + } + + if (srcFile != null) { + try { + while((nRead = srcFile.read(buffer)) != -1) { + lTotalRead += nRead; + if (dstFile != null) { + dstFile.write(buffer, 0, nRead); + dstFile.flush(); + } else { + cv.put("length", nRead); + cv.put("chunk", buffer); + if (crOut.update(ffxDstFiles, cv, sTmpDstFileName, null) == 0) + break; + lTotalWritten += nRead; + } + } + + srcFile.close(); + + if (dstFile != null) { + dstFile.flush(); + dstFile.close(); + + destFile = new File(sTmpDstFileName); + lTotalWritten = destFile.length(); + } + + if (lTotalWritten == lTotalRead) { + sRet = sTmpSrcFileName + " copied to " + sTmpDstFileName; + } + else { + sRet = sErrorPrefix + "Failed to copy " + sTmpSrcFileName + " [length = " + lTotalWritten + "] to " + sTmpDstFileName + " [length = " + lTotalRead + "]"; + } + } catch (IOException e) { + e.printStackTrace(); + } + + } else { + String[] columns = new String[] { + "_id", + "chunk", + "length" + }; + + Cursor myCursor = crIn.query(ffxSrcFiles, + columns, // Which columns to return + sTmpSrcFileName, // Which rows to return (all rows) + null, // Selection arguments (none) + null); // Order clause (none) + if (myCursor != null) { + int nRows = myCursor.getCount(); + + byte [] buf = null; + + for (int lcv = 0; lcv < nRows; lcv++) { + if (myCursor.moveToPosition(lcv)) { + buf = myCursor.getBlob(myCursor.getColumnIndex("chunk")); + if (buf != null) { + nRead = buf.length; + try { + lTotalRead += nRead; + if (dstFile != null) { + dstFile.write(buffer, 0, nRead); + dstFile.flush(); + } else { + cv.put("length", nRead); + cv.put("chunk", buffer); + if (crOut.update(ffxDstFiles, cv, sTmpDstFileName, null) == 0) + break; + lTotalWritten += nRead; + } + } catch (IOException e) { + e.printStackTrace(); + } + buf = null; + } + } + } + + if (nRows == -1) { + sRet = sErrorPrefix + sTmpSrcFileName + ",-1\nNo such file or directory"; + } + else { + myCursor.close(); + + if (dstFile != null) { + try { + dstFile.flush(); + dstFile.close(); + + destFile = new File(sTmpDstFileName); + lTotalWritten = destFile.length(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + if (lTotalWritten == lTotalRead) { + sRet = sTmpSrcFileName + " copied to " + sTmpDstFileName; + } + else { + sRet = sErrorPrefix + "Failed to copy " + sTmpSrcFileName + " [length = " + lTotalWritten + "] to " + sTmpDstFileName + " [length = " + lTotalRead + "]"; + } + } + } + else { + sRet = sErrorPrefix + sTmpSrcFileName + ",-1\nUnable to access file (internal error)"; + } + } + + return (sRet); + } + + public String IsDirWritable(String sDir) + { + String sTmpDir = fixFileName(sDir); + String sRet = sErrorPrefix + "[" + sTmpDir + "] is not a directory"; + + if (sTmpDir.contains("org.mozilla.fennec") || sTmpDir.contains("org.mozilla.firefox")) { + ContentResolver cr = contextWrapper.getContentResolver(); + Uri ffxFiles = null; + + ffxFiles = Uri.parse("content://" + (sTmpDir.contains("fennec") ? fenProvider : ffxProvider) + "/dir"); + + String[] columns = new String[] { + "_id", + "isdir", + "filename", + "length", + "writable" + }; + + Cursor myCursor = cr.query( ffxFiles, + columns, // Which columns to return + sTmpDir, // Which rows to return (all rows) + null, // Selection arguments (none) + null); // Order clause (none) + if (myCursor != null) { + if (myCursor.getCount() > 0) { + if (myCursor.moveToPosition(0)) { + if (myCursor.getLong(myCursor.getColumnIndex("isdir")) == 1) { + sRet = "[" + sTmpDir + "] " + ((myCursor.getLong(myCursor.getColumnIndex("writable")) == 1) ? "is" : "is not") + " writable"; + } + } + } + } + } else { + File dir = new File(sTmpDir); + + if (dir.isDirectory()) { + sRet = "[" + sTmpDir + "] " + (dir.canWrite() ? "is" : "is not") + " writable"; + } else { + sRet = sErrorPrefix + "[" + sTmpDir + "] is not a directory"; + } + } + return(sRet); + } + + public String Push(String fileName, BufferedInputStream bufIn, long lSize) + { + byte [] buffer = new byte [8192]; + int nRead = 0; + long lRead = 0; + String sTmpFileName = fixFileName(fileName); + FileOutputStream dstFile = null; + ContentResolver cr = null; + ContentValues cv = null; + Uri ffxFiles = null; + String sRet = sErrorPrefix + "Push failed!"; + + try { + if (sTmpFileName.contains("org.mozilla.fennec") || sTmpFileName.contains("org.mozilla.firefox")) { + cr = contextWrapper.getContentResolver(); + ffxFiles = Uri.parse("content://" + (sTmpFileName.contains("fennec") ? fenProvider : ffxProvider) + "/file"); + cv = new ContentValues(); + } + else { + dstFile = new FileOutputStream(sTmpFileName, false); + } + + while((nRead != -1) && (lRead < lSize)) + { + nRead = bufIn.read(buffer); + if (nRead != -1) { + if (dstFile != null) { + dstFile.write(buffer, 0, nRead); + dstFile.flush(); + } + else { + cv.put("offset", lRead); + cv.put("length", nRead); + cv.put("chunk", buffer); + cr.update(ffxFiles, cv, sTmpFileName, null); + } + lRead += nRead; + } + } + + if (dstFile != null) { + dstFile.flush(); + dstFile.close(); + } + + if (lRead == lSize) { + sRet = HashFile(sTmpFileName); + } + } + catch (IOException e) { + e.printStackTrace(); + } + + buffer = null; + + return(sRet); + } + + public String FTPGetFile(String sServer, String sSrcFileName, String sDstFileName, OutputStream out) + { + byte[] buffer = new byte [4096]; + int nRead = 0; + long lTotalRead = 0; + String sRet = sErrorPrefix + "FTP Get failed for " + sSrcFileName; + String strRet = ""; + int reply = 0; + FileOutputStream outStream = null; + String sTmpDstFileName = fixFileName(sDstFileName); + + FTPClient ftp = new FTPClient(); + try + { + ftp.connect(sServer); + reply = ftp.getReplyCode(); + if(FTPReply.isPositiveCompletion(reply)) + { + ftp.login("anonymous", "b@t.com"); + reply = ftp.getReplyCode(); + if(FTPReply.isPositiveCompletion(reply)) + { + ftp.enterLocalPassiveMode(); + if (ftp.setFileType(FTP.BINARY_FILE_TYPE)) + { + File dstFile = new File(sTmpDstFileName); + outStream = new FileOutputStream(dstFile); + FTPFile [] ftpFiles = ftp.listFiles(sSrcFileName); + if (ftpFiles.length > 0) + { + long lFtpSize = ftpFiles[0].getSize(); + if (lFtpSize <= 0) + lFtpSize = 1; + + InputStream ftpIn = ftp.retrieveFileStream(sSrcFileName); + while ((nRead = ftpIn.read(buffer)) != -1) + { + lTotalRead += nRead; + outStream.write(buffer, 0, nRead); + strRet = "\r" + lTotalRead + " of " + lFtpSize + " bytes received " + ((lTotalRead * 100) / lFtpSize) + "% completed"; + out.write(strRet.getBytes()); + out.flush(); + } + ftpIn.close(); + @SuppressWarnings("unused") + boolean bRet = ftp.completePendingCommand(); + outStream.flush(); + outStream.close(); + strRet = ftp.getReplyString(); + reply = ftp.getReplyCode(); + } + else + { + strRet = sRet; + } + } + ftp.logout(); + ftp.disconnect(); + sRet = "\n" + strRet; + } + else + { + ftp.disconnect(); + System.err.println("FTP server refused login."); + } + } + else + { + ftp.disconnect(); + System.err.println("FTP server refused connection."); + } + } + catch (SocketException e) + { + sRet = e.getMessage(); + strRet = ftp.getReplyString(); + reply = ftp.getReplyCode(); + sRet += "\n" + strRet; + e.printStackTrace(); + } + catch (IOException e) + { + sRet = e.getMessage(); + strRet = ftp.getReplyString(); + reply = ftp.getReplyCode(); + sRet += "\n" + strRet; + e.printStackTrace(); + } + return (sRet); + } + + public String Pull(String fileName, long lOffset, long lLength, OutputStream out) + { + String sTmpFileName = fixFileName(fileName); + String sRet = sErrorPrefix + "Could not read the file " + sTmpFileName; + byte[] buffer = new byte [4096]; + int nRead = 0; + long lSent = 0; + + if (sTmpFileName.contains("org.mozilla.fennec") || sTmpFileName.contains("org.mozilla.firefox")) { + ContentResolver cr = contextWrapper.getContentResolver(); + Uri ffxFiles = null; + + ffxFiles = Uri.parse("content://" + (sTmpFileName.contains("fennec") ? fenProvider : ffxProvider) + "/file"); + + String[] columns = new String[] { + "_id", + "chunk", + "length" + }; + + String [] args = new String [2]; + args[0] = Long.toString(lOffset); + args[1] = Long.toString(lLength); + + Cursor myCursor = cr.query( ffxFiles, + columns, // Which columns to return + sTmpFileName, // Which rows to return (all rows) + args, // Selection arguments (none) + null); // Order clause (none) + if (myCursor != null) { + int nRows = myCursor.getCount(); + long lFileLength = 0; + + for (int lcv = 0; lcv < nRows; lcv++) { + if (myCursor.moveToPosition(lcv)) { + if (lcv == 0) { + lFileLength = myCursor.getLong(2); + String sTmp = sTmpFileName + "," + lFileLength + "\n"; + try { + out.write(sTmp.getBytes()); + } catch (IOException e) { + e.printStackTrace(); + break; + } + } + + if (lLength != 0) { + byte [] buf = myCursor.getBlob(1); + if (buf != null) { + nRead = buf.length; + try { + if ((lSent + nRead) <= lFileLength) { + out.write(buf,0,nRead); + lSent += nRead; + } + else { + nRead = (int) (lFileLength - lSent); + out.write(buf,0,nRead); + Log.d("pull warning", "more bytes read than expected"); + break; + } + } catch (IOException e) { + e.printStackTrace(); + sRet = sErrorPrefix + "Could not write to out " + sTmpFileName; + } + buf = null; + } + } + } + } + if (nRows == 0) { + String sTmp = sTmpFileName + "," + lFileLength + "\n"; + try { + out.write(sTmp.getBytes()); + } catch (IOException e) { + e.printStackTrace(); + } + } + if (nRows == -1) { + sRet = sErrorPrefix + sTmpFileName + ",-1\nNo such file or directory"; + } + else { + myCursor.close(); + sRet = ""; + } + } + else { + sRet = sErrorPrefix + sTmpFileName + ",-1\nUnable to access file (internal error)"; + } + } + else { + try { + File f = new File(sTmpFileName); + long lFileLength = f.length(); + FileInputStream fin = new FileInputStream(f); + if (lFileLength == 0) { + while ((nRead = fin.read(buffer)) != -1) { + lFileLength += nRead; + } + fin.close(); + fin = new FileInputStream(f); + } + + // lLength == -1 return everything between lOffset and eof + // lLength == 0 return file length + // lLength > 0 return lLength bytes + if (lLength == -1) { + lFileLength = lFileLength - lOffset; + } else if (lLength == 0) { + // just return the file length + } else { + lFileLength = ((lLength <= (lFileLength - lOffset)) ? lLength : (lFileLength - lOffset)); + } + + String sTmp = sTmpFileName + "," + lFileLength + "\n"; + out.write(sTmp.getBytes()); + if (lLength != 0) { + if (lOffset > 0) { + fin.skip(lOffset); + } + while ((nRead = fin.read(buffer)) != -1) { + if ((lSent + nRead) <= lFileLength) { + out.write(buffer,0,nRead); + lSent += nRead; + } + else { + nRead = (int) (lFileLength - lSent); + out.write(buffer,0,nRead); + if (lLength != -1) + Log.d("pull warning", "more bytes read than sent"); + break; + } + } + } + fin.close(); + out.flush(); + sRet = ""; + } + catch (FileNotFoundException e) { + sRet = sErrorPrefix + sTmpFileName + ",-1\nNo such file or directory"; + } + catch (IOException e) { + sRet = e.toString(); + } + } + return (sRet); + } + + public String Cat(String fileName, OutputStream out) + { + String sTmpFileName = fixFileName(fileName); + String sRet = sErrorPrefix + "Could not read the file " + sTmpFileName; + byte[] buffer = new byte [4096]; + int nRead = 0; + + if (sTmpFileName.contains("org.mozilla.fennec") || sTmpFileName.contains("org.mozilla.firefox")) { + ContentResolver cr = contextWrapper.getContentResolver(); + Uri ffxFiles = null; + + ffxFiles = Uri.parse("content://" + (sTmpFileName.contains("fennec") ? fenProvider : ffxProvider) + "/file"); + + String[] columns = new String[] { + "_id", + "chunk" + }; + + Cursor myCursor = cr.query( ffxFiles, + columns, // Which columns to return + sTmpFileName, // Which rows to return (all rows) + null, // Selection arguments (none) + null); // Order clause (none) + if (myCursor != null) { + int nRows = myCursor.getCount(); + int nBytesRecvd = 0; + + for (int lcv = 0; lcv < nRows; lcv++) { + if (myCursor.moveToPosition(lcv)) { + byte [] buf = myCursor.getBlob(1); + if (buf != null) { + nBytesRecvd += buf.length; + try { + out.write(buf); + sRet = ""; + } catch (IOException e) { + e.printStackTrace(); + sRet = sErrorPrefix + "Could not write to out " + sTmpFileName; + } + buf = null; + } + } + } + if (nRows == 0) { + sRet = ""; + } + + myCursor.close(); + } + } else { + try { + FileInputStream fin = new FileInputStream(sTmpFileName); + while ((nRead = fin.read(buffer)) != -1) { + out.write(buffer,0,nRead); + } + fin.close(); + out.flush(); + sRet = ""; + } + catch (FileNotFoundException e) { + sRet = sErrorPrefix + sTmpFileName + " No such file or directory"; + } + catch (IOException e) { + sRet = e.toString(); + } + } + return (sRet); + } + + public String MakeDir(String sDir) + { + String sTmpDir = fixFileName(sDir); + String sRet = sErrorPrefix + "Could not create the directory " + sTmpDir; + if (sTmpDir.contains("org.mozilla.fennec") || sTmpDir.contains("org.mozilla.firefox")) { + ContentResolver cr = contextWrapper.getContentResolver(); + Uri ffxFiles = Uri.parse("content://" + (sTmpDir.contains("fennec") ? fenProvider : ffxProvider) + "/dir"); + ContentValues cv = new ContentValues(); + + if (cr.update(ffxFiles, cv, sTmpDir, null) == 1) { + sRet = sDir + " successfully created"; + } + } + else { + File dir = new File(sTmpDir); + + if (dir.mkdirs()) { + sRet = sDir + " successfully created"; + } + } + + return (sRet); + } + // move this to SUTAgentAndroid.java + public String GetScreenInfo() + { + String sRet = ""; + DisplayMetrics metrics = new DisplayMetrics(); + WindowManager wMgr = (WindowManager) contextWrapper.getSystemService(Context.WINDOW_SERVICE); + wMgr.getDefaultDisplay().getMetrics(metrics); + sRet = "X:" + metrics.widthPixels + " Y:" + metrics.heightPixels; + return (sRet); + } + // move this to SUTAgentAndroid.java + public int [] GetScreenXY() + { + int [] nRetXY = new int [2]; + DisplayMetrics metrics = new DisplayMetrics(); + WindowManager wMgr = (WindowManager) contextWrapper.getSystemService(Context.WINDOW_SERVICE); + wMgr.getDefaultDisplay().getMetrics(metrics); + nRetXY[0] = metrics.widthPixels; + nRetXY[1] = metrics.heightPixels; + return(nRetXY); + } + + public String GetRotationInfo() + { + WindowManager wMgr = (WindowManager) contextWrapper.getSystemService(Context.WINDOW_SERVICE); + int nRotationDegrees = 0; // default + switch(wMgr.getDefaultDisplay().getRotation()) + { + case Surface.ROTATION_90: + nRotationDegrees = 90; + break; + case Surface.ROTATION_180: + nRotationDegrees = 180; + break; + case Surface.ROTATION_270: + nRotationDegrees = 270; + break; + } + return "ROTATION:" + nRotationDegrees; + } + + public String SetADB(String sWhat) { + String sRet = ""; + String sTmp = ""; + String sCmd; + + if (sWhat.contains("ip")) { + sCmd = "setprop service.adb.tcp.port 5555"; + } else { + sCmd = "setprop service.adb.tcp.port -1"; + } + + try { + pProc = Runtime.getRuntime().exec(this.getSuArgs(sCmd)); + RedirOutputThread outThrd = new RedirOutputThread(pProc, null); + outThrd.start(); + outThrd.joinAndStopRedirect(5000); + sTmp = outThrd.strOutput; + Log.e("ADB", sTmp); + if (outThrd.nExitCode == 0) { + pProc = Runtime.getRuntime().exec(this.getSuArgs("stop adbd")); + outThrd = new RedirOutputThread(pProc, null); + outThrd.start(); + outThrd.joinAndStopRedirect(5000); + sTmp = outThrd.strOutput; + Log.e("ADB", sTmp); + if (outThrd.nExitCode == 0) { + pProc = Runtime.getRuntime().exec(this.getSuArgs("start adbd")); + outThrd = new RedirOutputThread(pProc, null); + outThrd.start(); + outThrd.joinAndStopRedirect(5000); + sTmp = outThrd.strOutput; + Log.e("ADB", sTmp); + if (outThrd.nExitCode == 0) { + sRet = "Successfully set adb to " + sWhat + "\n"; + } else { + sRet = sErrorPrefix + "Failed to start adbd\n"; + } + } else { + sRet = sErrorPrefix + "Failed to stop adbd\n"; + } + } else { + sRet = sErrorPrefix + "Failed to setprop service.adb.tcp.port 5555\n"; + } + + } + catch (IOException e) + { + sRet = e.getMessage(); + e.printStackTrace(); + } + catch (InterruptedException e) + { + e.printStackTrace(); + } + return(sRet); + } + + public String KillProcess(String sProcName, OutputStream out) + { + String sRet = sErrorPrefix + "Unable to kill " + sProcName + "\n"; + ActivityManager aMgr = (ActivityManager) contextWrapper.getSystemService(Activity.ACTIVITY_SERVICE); + List <ActivityManager.RunningAppProcessInfo> lProcesses = aMgr.getRunningAppProcesses(); + int lcv = 0; + String sFoundProcName = ""; + int nProcs = 0; + boolean bFoundAppProcess = false; + + if (lProcesses != null) + nProcs = lProcesses.size(); + + for (lcv = 0; lcv < nProcs; lcv++) + { + if (lProcesses.get(lcv).processName.contains(sProcName)) + { + sFoundProcName = lProcesses.get(lcv).processName; + bFoundAppProcess = true; + + try + { + pProc = Runtime.getRuntime().exec(this.getSuArgs("kill " + lProcesses.get(lcv).pid)); + RedirOutputThread outThrd = new RedirOutputThread(pProc, null); + outThrd.start(); + outThrd.joinAndStopRedirect(15000); + String sTmp = outThrd.strOutput; + Log.e("KILLPROCESS", sTmp); + } + catch (IOException e) + { + sRet = e.getMessage(); + e.printStackTrace(); + } + catch (InterruptedException e) + { + e.printStackTrace(); + } + } + } + + if (bFoundAppProcess) + { + // Give the messages a chance to be processed + try { + Thread.sleep(2000); + } + catch (InterruptedException e) + { + e.printStackTrace(); + } + + sRet = "Successfully killed " + sProcName + "\n"; + lProcesses = aMgr.getRunningAppProcesses(); + nProcs = 0; + if (lProcesses != null) + nProcs = lProcesses.size(); + for (lcv = 0; lcv < nProcs; lcv++) + { + if (lProcesses.get(lcv).processName.contains(sProcName)) + { + sRet = sErrorPrefix + "Unable to kill " + sProcName + " (couldn't kill " + sFoundProcName +")\n"; + break; + } + } + } + else + { + // To kill processes other than Java applications - processes + // like xpcshell - a different strategy is necessary: use ps + // to find the process' PID. + try + { + pProc = Runtime.getRuntime().exec("ps"); + RedirOutputThread outThrd = new RedirOutputThread(pProc, null); + outThrd.start(); + outThrd.joinAndStopRedirect(10000); + String sTmp = outThrd.strOutput; + StringTokenizer stokLines = new StringTokenizer(sTmp, "\n"); + while(stokLines.hasMoreTokens()) + { + String sLine = stokLines.nextToken(); + StringTokenizer stokColumns = new StringTokenizer(sLine, " \t\n"); + stokColumns.nextToken(); + String sPid = stokColumns.nextToken(); + stokColumns.nextToken(); + stokColumns.nextToken(); + stokColumns.nextToken(); + stokColumns.nextToken(); + stokColumns.nextToken(); + stokColumns.nextToken(); + String sName = null; + if (stokColumns.hasMoreTokens()) + { + sName = stokColumns.nextToken(); + if (sName.contains(sProcName)) + { + NewKillProc(sPid, out); + sRet = "Successfully killed " + sName + "\n"; + } + } + } + } + catch (Exception e) + { + e.printStackTrace(); + } + } + + return (sRet); + } + + public boolean IsProcessDead(String sProcName) + { + boolean bRet = false; + ActivityManager aMgr = (ActivityManager) contextWrapper.getSystemService(Activity.ACTIVITY_SERVICE); + List <ActivityManager.ProcessErrorStateInfo> lProcesses = aMgr.getProcessesInErrorState(); + int lcv = 0; + + if (lProcesses != null) + { + for (lcv = 0; lcv < lProcesses.size(); lcv++) + { + if (lProcesses.get(lcv).processName.contentEquals(sProcName) && + lProcesses.get(lcv).condition != ActivityManager.ProcessErrorStateInfo.NO_ERROR) + { + bRet = true; + break; + } + } + } + + return (bRet); + } + + public String GetProcessInfo() + { + String sRet = ""; + ActivityManager aMgr = (ActivityManager) contextWrapper.getSystemService(Activity.ACTIVITY_SERVICE); + List <ActivityManager.RunningAppProcessInfo> lProcesses = aMgr.getRunningAppProcesses(); + int nProcs = 0; + int lcv = 0; + String strProcName = ""; + int nPID = 0; + int nUser = 0; + + if (lProcesses != null) + nProcs = lProcesses.size(); + + for (lcv = 0; lcv < nProcs; lcv++) + { + strProcName = lProcesses.get(lcv).processName; + nPID = lProcesses.get(lcv).pid; + nUser = lProcesses.get(lcv).uid; + sRet += nUser + "\t" + nPID + "\t" + strProcName; + if (lcv < (nProcs - 1)) + sRet += "\n"; + } + + return (sRet); + } + + public String GetOSInfo() + { + String sRet = ""; + + sRet = Build.DISPLAY; + + return (sRet); + } + + public String GetPowerInfo() + { + String sRet = ""; + + sRet = "Power status:\n AC power " + SUTAgentAndroid.sACStatus + "\n"; + sRet += " Battery charge " + SUTAgentAndroid.sPowerStatus + "\n"; + sRet += " Remaining charge: " + SUTAgentAndroid.nChargeLevel + "%\n"; + sRet += " Battery Temperature: " + (((float)(SUTAgentAndroid.nBatteryTemp))/10) + " (c)\n"; + return (sRet); + } + + public String GetSutUserInfo() + { + String sRet = ""; + try { + // based on patch in https://bugzilla.mozilla.org/show_bug.cgi?id=811763 + Context context = contextWrapper.getApplicationContext(); + Object userManager = context.getSystemService("user"); + if (userManager != null) { + // if userManager is non-null that means we're running on 4.2+ and so the rest of this + // should just work + Object userHandle = android.os.Process.class.getMethod("myUserHandle", (Class[])null).invoke(null); + Object userSerial = userManager.getClass().getMethod("getSerialNumberForUser", userHandle.getClass()).invoke(userManager, userHandle); + sRet += "User Serial:" + userSerial.toString(); + } + } catch (Exception e) { + // Guard against any unexpected failures + e.printStackTrace(); + } + + return sRet; + } + + public String GetTemperatureInfo() + { + String sTempVal = "unknown"; + String sDeviceFile = "/sys/bus/platform/devices/temp_sensor_hwmon.0/temp1_input"; + try { + pProc = Runtime.getRuntime().exec(this.getSuArgs("cat " + sDeviceFile)); + RedirOutputThread outThrd = new RedirOutputThread(pProc, null); + outThrd.start(); + outThrd.joinAndStopRedirect(5000); + String output = outThrd.strOutput; + // this only works on pandas (with the temperature sensors turned + // on), other platforms we just get a file not found error... we'll + // just return "unknown" for that case + try { + sTempVal = String.valueOf(Integer.parseInt(output.trim()) / 1000.0); + } catch (NumberFormatException e) { + // not parsed! probably not a panda + } + } catch (IOException e) { + e.printStackTrace(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + + return "Temperature: " + sTempVal; + } + + public String GetDiskInfo(String sPath) + { + String sRet = ""; + StatFs statFS = new StatFs(sPath); + + long nBlockCount = statFS.getBlockCount(); + long nBlockSize = statFS.getBlockSize(); + long nBlocksAvail = statFS.getAvailableBlocks(); + // Free is often the same as Available, but can include reserved + // blocks that are not available to normal applications. + // long nBlocksFree = statFS.getFreeBlocks(); + + sRet = sPath + ": " + (nBlockCount * nBlockSize) + " total, " + (nBlocksAvail * nBlockSize) + " available"; + + return (sRet); + } + + public String GetMemoryInfo() + { + String sRet = "PA:" + GetMemoryConfig() + ", FREE: " + GetMemoryUsage(); + return (sRet); + } + + public long GetMemoryConfig() + { + ActivityManager aMgr = (ActivityManager) contextWrapper.getSystemService(Activity.ACTIVITY_SERVICE); + ActivityManager.MemoryInfo outInfo = new ActivityManager.MemoryInfo(); + aMgr.getMemoryInfo(outInfo); + long lMem = outInfo.availMem; + + return (lMem); + } + + public long GetMemoryUsage() + { + + String load = ""; + try { + RandomAccessFile reader = new RandomAccessFile("/proc/meminfo", "r"); + load = reader.readLine(); // Read in the MemTotal + load = reader.readLine(); // Read in the MemFree + } catch (IOException ex) { + return (0); + } + + String[] toks = load.split(" "); + int i = 1; + for (i=1; i < toks.length; i++) { + String val = toks[i].trim(); + if (!val.equals("")) { + break; + } + } + if (i <= toks.length) { + long lMem = Long.parseLong(toks[i].trim()); + return (lMem * 1024); + } + return (0); + } + + public String UpdateCallBack(String sFileName) + { + String sRet = sErrorPrefix + "No file specified"; + String sIP = ""; + String sPort = ""; + int nEnd = 0; + int nStart = 0; + + if ((sFileName == null) || (sFileName.length() == 0)) + return(sRet); + + Context ctx = contextWrapper.getApplicationContext(); + try { + FileInputStream fis = ctx.openFileInput(sFileName); + int nBytes = fis.available(); + if (nBytes > 0) + { + byte [] buffer = new byte [nBytes + 1]; + int nRead = fis.read(buffer, 0, nBytes); + fis.close(); + ctx.deleteFile(sFileName); + if (nRead > 0) + { + String sBuffer = new String(buffer); + nEnd = sBuffer.indexOf(','); + if (nEnd > 0) + { + sIP = (sBuffer.substring(0, nEnd)).trim(); + nStart = nEnd + 1; + nEnd = sBuffer.indexOf('\r', nStart); + if (nEnd > 0) + { + sPort = (sBuffer.substring(nStart, nEnd)).trim(); + Thread.sleep(5000); + sRet = RegisterTheDevice(sIP, sPort, sBuffer.substring(nEnd + 1)); + } + } + } + } + } + catch (FileNotFoundException e) + { + sRet = sErrorPrefix + "Nothing to do"; + } + catch (IOException e) + { + sRet = sErrorPrefix + "Couldn't send info to " + sIP + ":" + sPort; + } + catch (InterruptedException e) + { + e.printStackTrace(); + } + return(sRet); + } + + public String RegisterTheDevice(String sSrvr, String sPort, String sData) + { + String sRet = ""; + String line = ""; + +// Debug.waitForDebugger(); + + if (sSrvr != null && sPort != null && sData != null) + { + try + { + int nPort = Integer.parseInt(sPort); + Socket socket = new Socket(sSrvr, nPort); + PrintWriter out = new PrintWriter(socket.getOutputStream(), false); + BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream())); + out.println(sData); + if ( out.checkError() == false ) + { + socket.setSoTimeout(30000); + while (socket.isInputShutdown() == false) + { + line = in.readLine(); + + if (line != null) + { + line = line.toLowerCase(); + sRet += line; + // ok means we're done + if (line.contains("ok")) + break; + } + else + { + // end of stream reached + break; + } + } + } + out.close(); + in.close(); + socket.close(); + } + catch(NumberFormatException e) + { + sRet += "reg NumberFormatException thrown [" + e.getLocalizedMessage() + "]"; + e.printStackTrace(); + } + catch (UnknownHostException e) + { + sRet += "reg UnknownHostException thrown [" + e.getLocalizedMessage() + "]"; + e.printStackTrace(); + } + catch (IOException e) + { + sRet += "reg IOException thrown [" + e.getLocalizedMessage() + "]"; + e.printStackTrace(); + } + } + return(sRet); + } + + public String GetInternetData(String sHost, String sPort, String sURL) + { + String sRet = ""; + String sNewURL = ""; + HttpClient httpClient = new DefaultHttpClient(); + try + { + sNewURL = "http://" + sHost + ((sPort.length() > 0) ? (":" + sPort) : "") + sURL; + + HttpGet request = new HttpGet(sNewURL); + HttpResponse response = httpClient.execute(request); + int status = response.getStatusLine().getStatusCode(); + // we assume that the response body contains the error message + if (status != HttpStatus.SC_OK) + { + ByteArrayOutputStream ostream = new ByteArrayOutputStream(); + response.getEntity().writeTo(ostream); + Log.e("HTTP CLIENT", ostream.toString()); + } + else + { + InputStream content = response.getEntity().getContent(); + byte [] data = new byte [2048]; + int nRead = content.read(data); + sRet = new String(data, 0, nRead); + content.close(); // this will also close the connection + } + } + catch (IllegalArgumentException e) + { + sRet = e.getLocalizedMessage(); + e.printStackTrace(); + } + catch (ClientProtocolException e) + { + sRet = e.getLocalizedMessage(); + e.printStackTrace(); + } + catch (IOException e) + { + sRet = e.getLocalizedMessage(); + e.printStackTrace(); + } + + return(sRet); + } + + public String GetTimeZone() + { + String sRet = ""; + TimeZone tz; + + tz = TimeZone.getDefault(); + Date now = new Date(); + sRet = tz.getDisplayName(tz.inDaylightTime(now), TimeZone.LONG); + + return(sRet); + } + + public String SetTimeZone(String sTimeZone) + { + String sRet = "Unable to set timezone to " + sTimeZone; + TimeZone tz = null; + AlarmManager amgr = null; + + if ((sTimeZone.length() > 0) && (sTimeZone.startsWith("GMT"))) + { + amgr = (AlarmManager) contextWrapper.getSystemService(Context.ALARM_SERVICE); + if (amgr != null) + amgr.setTimeZone(sTimeZone); + } + else + { + String [] zoneNames = TimeZone.getAvailableIDs(); + int nNumMatches = zoneNames.length; + int lcv = 0; + + for (lcv = 0; lcv < nNumMatches; lcv++) + { + if (zoneNames[lcv].equalsIgnoreCase(sTimeZone)) + break; + } + + if (lcv < nNumMatches) + { + amgr = (AlarmManager) contextWrapper.getSystemService(Context.ALARM_SERVICE); + if (amgr != null) + amgr.setTimeZone(zoneNames[lcv]); + } + } + + if (amgr != null) + { + tz = TimeZone.getDefault(); + Date now = new Date(); + sRet = tz.getDisplayName(tz.inDaylightTime(now), TimeZone.LONG); + } + + return(sRet); + } + + public String GetSystemTime() + { + String sRet = ""; + Calendar cal = Calendar.getInstance(); + SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd hh:mm:ss:SSS"); + sRet = sdf.format(cal.getTime()); + + return (sRet); + } + + public String SetSystemTime(String sDate, String sTime, OutputStream out) { + String sRet = ""; + String sM = ""; + String sMillis = ""; + + if (((sDate != null) && (sTime != null)) && + (sDate.contains("/") || sDate.contains(".")) && + (sTime.contains(":"))) { + int year = Integer.parseInt(sDate.substring(0,4)); + int month = Integer.parseInt(sDate.substring(5,7)); + int day = Integer.parseInt(sDate.substring(8,10)); + + int hour = Integer.parseInt(sTime.substring(0,2)); + int mins = Integer.parseInt(sTime.substring(3,5)); + int secs = Integer.parseInt(sTime.substring(6,8)); + + Calendar cal = new GregorianCalendar(TimeZone.getDefault()); + cal.set(year, month - 1, day, hour, mins, secs); + long lMillisecs = cal.getTime().getTime(); + + sM = Long.toString(lMillisecs); + sMillis = sM.substring(0, sM.length() - 3) + "." + sM.substring(sM.length() - 3); + + } else { + sRet += "Invalid argument(s)"; + } + + // if we have an argument + if (sMillis.length() > 0) { + try { + pProc = Runtime.getRuntime().exec(this.getSuArgs("date -u " + sMillis)); + RedirOutputThread outThrd = new RedirOutputThread(pProc, null); + outThrd.start(); + outThrd.joinAndStopRedirect(10000); + sRet += GetSystemTime(); + } catch (IOException e) { + sRet = e.getMessage(); + e.printStackTrace(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + + return (sRet); + } + + public String GetClok() + { + long lMillisecs = System.currentTimeMillis(); + String sRet = ""; + + if (lMillisecs > 0) + sRet = Long.toString(lMillisecs); + + return(sRet); + } + + public String GetUptime() + { + String sRet = ""; + long lHold = 0; + long lUptime = SystemClock.elapsedRealtime(); + int nDays = 0; + int nHours = 0; + int nMinutes = 0; + int nSecs = 0; + int nMilliseconds = 0; + + if (lUptime > 0) + { + nDays = (int)(lUptime / (24L * 60L * 60L * 1000L)); + lHold = lUptime % (24L * 60L * 60L * 1000L); + nHours = (int)(lHold / (60L * 60L * 1000L)); + lHold %= 60L * 60L * 1000L; + nMinutes = (int)(lHold / (60L * 1000L)); + lHold %= 60L * 1000L; + nSecs = (int)(lHold / 1000L); + nMilliseconds = (int)(lHold % 1000); + sRet = "" + nDays + " days " + nHours + " hours " + nMinutes + " minutes " + nSecs + " seconds " + nMilliseconds + " ms"; + } + + return (sRet); + } + + public String GetUptimeMillis() + { + return Long.toString(SystemClock.uptimeMillis()); + } + + public String GetSutUptimeMillis() + { + long now = System.currentTimeMillis(); + return "SUTagent running for "+Long.toString(now - SUTAgentAndroid.nCreateTimeMillis)+" ms"; + } + + public String NewKillProc(String sProcId, OutputStream out) + { + String sRet = ""; + + try + { + pProc = Runtime.getRuntime().exec("kill "+sProcId); + RedirOutputThread outThrd = new RedirOutputThread(pProc, out); + outThrd.start(); + outThrd.joinAndStopRedirect(5000); + } + catch (IOException e) + { + sRet = e.getMessage(); + e.printStackTrace(); + } + catch (InterruptedException e) + { + e.printStackTrace(); + } + + return(sRet); + } + + public String SendPing(String sIPAddr, OutputStream out) + { + String sRet = ""; + String [] theArgs = new String [4]; + + theArgs[0] = "ping"; + theArgs[1] = "-c"; + theArgs[2] = "3"; + theArgs[3] = sIPAddr; + + try + { + pProc = Runtime.getRuntime().exec(theArgs); + RedirOutputThread outThrd = new RedirOutputThread(pProc, out); + outThrd.start(); + outThrd.joinAndStopRedirect(5000); + if (out == null) + sRet = outThrd.strOutput; + } + catch (IOException e) + { + sRet = e.getMessage(); + e.printStackTrace(); + } + catch (InterruptedException e) + { + e.printStackTrace(); + } + + return (sRet); + } + + public String GetTmpDir() + { + String sRet = ""; + Context ctx = contextWrapper.getApplicationContext(); + File dir = ctx.getFilesDir(); + ctx = null; + try { + sRet = dir.getCanonicalPath(); + } + catch (IOException e) + { + e.printStackTrace(); + } + return(sRet); + } + + public String PrintFileTimestamp(String sFile) + { + String sRet = ""; + String sTmpFileName = fixFileName(sFile); + long lModified = -1; + + if (sTmpFileName.contains("org.mozilla.fennec") || sTmpFileName.contains("org.mozilla.firefox")) { + ContentResolver cr = contextWrapper.getContentResolver(); + Uri ffxFiles = Uri.parse("content://" + (sTmpFileName.contains("fennec") ? fenProvider : ffxProvider) + "/dir"); + + String[] columns = new String[] { + "_id", + "isdir", + "filename", + "length", + "ts" + }; + + Cursor myCursor = cr.query( ffxFiles, + columns, // Which columns to return + sTmpFileName, // Which rows to return (all rows) + null, // Selection arguments (none) + null); // Order clause (none) + if (myCursor != null) { + if (myCursor.getCount() > 0) { + if (myCursor.moveToPosition(0)) { + lModified = myCursor.getLong(myCursor.getColumnIndex("ts")); + } + } + myCursor.close(); + } + } + else { + File theFile = new File(sTmpFileName); + + if (theFile.exists()) { + lModified = theFile.lastModified(); + } + } + + if (lModified != -1) { + Date dtModified = new Date(lModified); + SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd hh:mm:ss:SSS"); + sRet = "Last modified: " + sdf.format(dtModified); + } + else { + sRet = sErrorPrefix + "[" + sTmpFileName + "] doesn't exist"; + } + + return(sRet); + } + + public String GetIniData(String sSection, String sKey, String sFile) + { + String sRet = ""; + String sComp = ""; + String sLine = ""; + boolean bFound = false; + BufferedReader in = null; + String sTmpFileName = fixFileName(sFile); + + try { + in = new BufferedReader(new FileReader(sTmpFileName)); + sComp = "[" + sSection + "]"; + while ((sLine = in.readLine()) != null) + { + if (sLine.equalsIgnoreCase(sComp)) + { + bFound = true; + break; + } + } + + if (bFound) + { + sComp = (sKey + " =").toLowerCase(); + while ((sLine = in.readLine()) != null) + { + if (sLine.toLowerCase().contains(sComp)) + { + String [] temp = null; + temp = sLine.split("="); + if (temp != null) + { + if (temp.length > 1) + sRet = temp[1].trim(); + } + break; + } + } + } + in.close(); + } + catch (FileNotFoundException e) + { + sComp = e.toString(); + } + catch (IOException e) + { + sComp = e.toString(); + } + return (sRet); + } + + public String RunReboot(OutputStream out, String sCallBackIP, String sCallBackPort) + { + String sRet = ""; + Context ctx = contextWrapper.getApplicationContext(); + + try { + if ((sCallBackIP != null) && (sCallBackPort != null) && + (sCallBackIP.length() > 0) && (sCallBackPort.length() > 0)) { + FileOutputStream fos = ctx.openFileOutput("update.info", Context.MODE_WORLD_READABLE | Context.MODE_WORLD_WRITEABLE); + String sBuffer = sCallBackIP + "," + sCallBackPort + "\rSystem rebooted\r"; + fos.write(sBuffer.getBytes()); + fos.flush(); + fos.close(); + fos = null; + } + } catch (FileNotFoundException e) { + sRet = sErrorPrefix + "Callback file creation error [rebt] call failed " + e.getMessage(); + e.printStackTrace(); + } catch (IOException e) { + sRet = sErrorPrefix + "Callback file error [rebt] call failed " + e.getMessage(); + e.printStackTrace(); + } + + try { + // Tell all of the data channels we are rebooting + ((ASMozStub)this.contextWrapper).SendToDataChannel("Rebooting ..."); + + pProc = Runtime.getRuntime().exec(this.getSuArgs("reboot")); + RedirOutputThread outThrd = new RedirOutputThread(pProc, out); + outThrd.start(); + outThrd.joinAndStopRedirect(10000); + } catch (IOException e) { + sRet = e.getMessage(); + e.printStackTrace(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + + return (sRet); + } + + private String [] getSuArgs(String cmdString) + { + String [] theArgs = new String [3]; + theArgs[0] = "su"; + theArgs[1] = "-c"; + // as a security measure, ICS and later resets LD_LIBRARY_PATH. reset + // it here when executing the command + theArgs[2] = "LD_LIBRARY_PATH=/vendor/lib:/system/lib " + cmdString; + return theArgs; + } + + public String UnInstallApp(String sApp, OutputStream out, boolean reboot) + { + String sRet = ""; + + try + { + if (reboot == true) { + pProc = Runtime.getRuntime().exec(this.getSuArgs("pm uninstall " + sApp + ";reboot;exit")); + } else { + pProc = Runtime.getRuntime().exec(this.getSuArgs("pm uninstall " + sApp + ";exit")); + } + + RedirOutputThread outThrd = new RedirOutputThread(pProc, out); + outThrd.start(); + try { + outThrd.joinAndStopRedirect(60000); + int nRet = pProc.exitValue(); + sRet = "\nuninst complete [" + nRet + "]"; + } + catch (IllegalThreadStateException itse) { + itse.printStackTrace(); + sRet = "\nuninst command timed out"; + } + } + catch (IOException e) + { + sRet = e.getMessage(); + e.printStackTrace(); + } + catch (InterruptedException e) + { + e.printStackTrace(); + } + + return (sRet); + } + + public String InstallApp(String sApp, OutputStream out) + { + String sRet = ""; + File srcFile = new File(sApp); + + try + { + // on android 4.2 and above, we want to pass the "-d" argument to pm so that version + // downgrades are allowed... (option unsupported in earlier versions) + String sPmCmd; + + if (android.os.Build.VERSION.SDK_INT >= 17) { // JELLY_BEAN_MR1 + sPmCmd = "pm install -r -d " + sApp + " Cleanup;exit"; + } else { + sPmCmd = "pm install -r " + sApp + " Cleanup;exit"; + } + pProc = Runtime.getRuntime().exec(this.getSuArgs(sPmCmd)); + RedirOutputThread outThrd3 = new RedirOutputThread(pProc, out); + outThrd3.start(); + try { + outThrd3.joinAndStopRedirect(60000); + int nRet3 = pProc.exitValue(); + sRet = "\ninstallation complete [" + nRet3 + "]"; + } + catch (IllegalThreadStateException itse) { + itse.printStackTrace(); + sRet = "\npm install command timed out"; + } + try { + out.write(sRet.getBytes()); + out.flush(); + } + catch (IOException e1) + { + e1.printStackTrace(); + } + } + catch (IOException e) + { + sRet = e.getMessage(); + e.printStackTrace(); + } + catch (InterruptedException e) + { + e.printStackTrace(); + } + + return (sRet); + } + + public String StrtUpdtOMatic(String sPkgName, String sPkgFileName, String sCallBackIP, String sCallBackPort) + { + String sRet = ""; + + Context ctx = contextWrapper.getApplicationContext(); + PackageManager pm = ctx.getPackageManager(); + + Intent prgIntent = new Intent(); + prgIntent.setPackage("com.mozilla.watcher"); + + try { + PackageInfo pi = pm.getPackageInfo("com.mozilla.watcher", PackageManager.GET_SERVICES | PackageManager.GET_INTENT_FILTERS); + ServiceInfo [] si = pi.services; + for (int i = 0; i < si.length; i++) + { + ServiceInfo s = si[i]; + if (s.name.length() > 0) + { + prgIntent.setClassName(s.packageName, s.name); + break; + } + } + } + catch (NameNotFoundException e) + { + e.printStackTrace(); + sRet = sErrorPrefix + "watcher is not properly installed"; + return(sRet); + } + + prgIntent.putExtra("command", "updt"); + prgIntent.putExtra("pkgName", sPkgName); + prgIntent.putExtra("pkgFile", sPkgFileName); + prgIntent.putExtra("reboot", true); + + try + { + if ((sCallBackIP != null) && (sCallBackPort != null) && + (sCallBackIP.length() > 0) && (sCallBackPort.length() > 0)) + { + FileOutputStream fos = ctx.openFileOutput("update.info", Context.MODE_WORLD_READABLE | Context.MODE_WORLD_WRITEABLE); + String sBuffer = sCallBackIP + "," + sCallBackPort + "\rupdate started " + sPkgName + " " + sPkgFileName + "\r"; + fos.write(sBuffer.getBytes()); + fos.flush(); + fos.close(); + fos = null; + prgIntent.putExtra("outFile", ctx.getFilesDir() + "/update.info"); + } + else { + if (prgIntent.hasExtra("outFile")) { + System.out.println("outFile extra unset from intent"); + prgIntent.removeExtra("outFile"); + } + } + + ComponentName cn = contextWrapper.startService(prgIntent); + if (cn != null) + sRet = "exit"; + else + sRet = sErrorPrefix + "Unable to use watcher service"; + } + catch(ActivityNotFoundException anf) + { + sRet = sErrorPrefix + "Activity Not Found Exception [updt] call failed"; + anf.printStackTrace(); + } + catch (FileNotFoundException e) + { + sRet = sErrorPrefix + "File creation error [updt] call failed"; + e.printStackTrace(); + } + catch (IOException e) + { + sRet = sErrorPrefix + "File error [updt] call failed"; + e.printStackTrace(); + } + + ctx = null; + + return (sRet); + } + + public String StartJavaPrg(String [] sArgs, Intent preIntent) + { + String sRet = ""; + String sArgList = ""; + String sUrl = ""; +// String sRedirFileName = ""; + Intent prgIntent = null; + + Context ctx = contextWrapper.getApplicationContext(); + PackageManager pm = ctx.getPackageManager(); + + if (preIntent == null) + prgIntent = new Intent(); + else + prgIntent = preIntent; + + prgIntent.setPackage(sArgs[0]); + prgIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + + try { + PackageInfo pi = pm.getPackageInfo(sArgs[0], PackageManager.GET_ACTIVITIES | PackageManager.GET_INTENT_FILTERS); + ActivityInfo [] ai = pi.activities; + for (int i = 0; i < ai.length; i++) + { + ActivityInfo a = ai[i]; + if (a.name.length() > 0) + { + prgIntent.setClassName(a.packageName, a.name); + break; + } + } + } + catch (NameNotFoundException e) + { + e.printStackTrace(); + } + + if (sArgs.length > 1) + { + if (sArgs[0].contains("android.browser")) + prgIntent.setAction(Intent.ACTION_VIEW); + else + prgIntent.setAction(Intent.ACTION_MAIN); + + if (sArgs[0].contains("fennec") || sArgs[0].contains("firefox")) + { + sArgList = ""; + sUrl = ""; + + for (int lcv = 1; lcv < sArgs.length; lcv++) + { + if (sArgs[lcv].contains("://")) + { + prgIntent.setAction(Intent.ACTION_VIEW); + sUrl = sArgs[lcv]; + } + else + { + if (sArgs[lcv].equals(">")) + { + lcv++; + if (lcv < sArgs.length) + lcv++; +// sRedirFileName = sArgs[lcv++]; + } + else + sArgList += " " + sArgs[lcv]; + } + } + + if (sArgList.length() > 0) + prgIntent.putExtra("args", sArgList.trim()); + + if (sUrl.length() > 0) + prgIntent.setData(Uri.parse(sUrl.trim())); + } + else + { + for (int lcv = 1; lcv < sArgs.length; lcv++) + sArgList += " " + sArgs[lcv]; + + prgIntent.setData(Uri.parse(sArgList.trim())); + } + } + else + { + prgIntent.setAction(Intent.ACTION_MAIN); + } + + try + { + contextWrapper.startActivity(prgIntent); + FindProcThread findProcThrd = new FindProcThread(contextWrapper, sArgs[0]); + findProcThrd.start(); + findProcThrd.join(7000); + if (!findProcThrd.bFoundIt && !findProcThrd.bStillRunning) { + sRet = "Unable to start " + sArgs[0] + ""; + } + } + catch(ActivityNotFoundException anf) + { + anf.printStackTrace(); + } + catch (InterruptedException e) { + e.printStackTrace(); + } + + ctx = null; + return (sRet); + } + + public String StartPrg(String [] progArray, OutputStream out, boolean startAsRoot) + { + String sRet = ""; + int lcv = 0; + + try { + if (startAsRoot) + { + // we need to requote the program string here, in case + // there's spaces or other characters which need quoting + // before being passed to su + List<String> quotedProgList = new ArrayList<String>(); + for (String arg : progArray) + { + String quotedArg = arg; + quotedArg = quotedArg.replace("\"", "\\\""); + quotedArg = quotedArg.replace("\'", "\\\'"); + if (quotedArg.contains(" ")) + { + quotedArg = "\"" + quotedArg + "\""; + } + quotedProgList.add(quotedArg); + } + pProc = Runtime.getRuntime().exec(this.getSuArgs(TextUtils.join(" ", quotedProgList))); + } + else + { + pProc = Runtime.getRuntime().exec(progArray); + } + RedirOutputThread outThrd = new RedirOutputThread(pProc, out); + outThrd.start(); + while (lcv < 30) { + try { + outThrd.join(10000); + int nRetCode = pProc.exitValue(); + sRet = "return code [" + nRetCode + "]"; + break; + } + catch (IllegalThreadStateException itse) { + lcv++; + } + } + outThrd.stopRedirect(); + } + catch (IOException e) + { + e.printStackTrace(); + } + catch (InterruptedException e) + { + e.printStackTrace(); + sRet = "Timed out!"; + } + + return (sRet); + } + + public String StartPrg2(String [] progArray, OutputStream out, String cwd, boolean startAsRoot) + { + String sRet = ""; + + int nArraySize = 0; + int nArgs = progArray.length - 1; // 1st arg is the environment string + int lcv = 0; + int temp = 0; + + String sEnvString = progArray[0]; + + if (!sEnvString.contains("=") && (sEnvString.length() > 0)) + { + if (sEnvString.contains("/") || sEnvString.contains("\\") || !sEnvString.contains(".")) + sRet = StartPrg(progArray, out, startAsRoot); + else + sRet = StartJavaPrg(progArray, null); + return(sRet); + } + + // Set up command line args stripping off the environment string + String [] theArgs = new String [nArgs]; + for (lcv = 0; lcv < nArgs; lcv++) + { + theArgs[lcv] = progArray[lcv + 1]; + } + + try + { + String [] envStrings = sEnvString.split(","); + Map<String, String> newEnv = new HashMap<String, String>(); + + for (lcv = 0; lcv < envStrings.length; lcv++) + { + temp = envStrings[lcv].indexOf("="); + if (temp > 0) + { + newEnv.put( envStrings[lcv].substring(0, temp), + envStrings[lcv].substring(temp + 1, envStrings[lcv].length())); + } + } + + Map<String, String> sysEnv = System.getenv(); + + nArraySize = sysEnv.size(); + + for (Map.Entry<String, String> entry : newEnv.entrySet()) + { + if (!sysEnv.containsKey(entry.getKey())) + { + nArraySize++; + } + } + + String[] envArray = new String[nArraySize]; + + int i = 0; + int offset; + String sKey = ""; + String sValue = ""; + + for (Map.Entry<String, String> entry : sysEnv.entrySet()) + { + sKey = entry.getKey(); + if (newEnv.containsKey(sKey)) + { + sValue = newEnv.get(sKey); + if ((offset = sValue.indexOf("$" + sKey)) != -1) + { + envArray[i++] = sKey + + "=" + + sValue.substring(0, offset) + + entry.getValue() + + sValue.substring(offset + sKey.length() + 1); + } + else + envArray[i++] = sKey + "=" + sValue; + newEnv.remove(sKey); + } + else + envArray[i++] = entry.getKey() + "=" + entry.getValue(); + } + + for (Map.Entry<String, String> entry : newEnv.entrySet()) + { + envArray[i++] = entry.getKey() + "=" + entry.getValue(); + } + + if (theArgs[0].contains("/") || theArgs[0].contains("\\") || !theArgs[0].contains(".")) + { + if (cwd != null) + { + File f = new File(cwd); + pProc = Runtime.getRuntime().exec(theArgs, envArray, f); + } + else + { + pProc = Runtime.getRuntime().exec(theArgs, envArray); + } + + RedirOutputThread outThrd = new RedirOutputThread(pProc, out); + outThrd.start(); + + lcv = 0; + + while (lcv < 30) { + try { + outThrd.join(10000); + int nRetCode = pProc.exitValue(); + sRet = "return code [" + nRetCode + "]"; + lcv = 30; + } + catch (IllegalThreadStateException itse) { + lcv++; + } + } + outThrd.stopRedirect(); + } + else + { + Intent preIntent = new Intent(); + for (lcv = 0; lcv < envArray.length; lcv++) + { + preIntent.putExtra("env" + lcv, envArray[lcv]); + } + sRet = StartJavaPrg(theArgs, preIntent); + } + } + catch(UnsupportedOperationException e) + { + if (e != null) + e.printStackTrace(); + } + catch(ClassCastException e) + { + if (e != null) + e.printStackTrace(); + } + catch(IllegalArgumentException e) + { + if (e != null) + e.printStackTrace(); + } + catch(NullPointerException e) + { + if (e != null) + e.printStackTrace(); + } + catch (IOException e) + { + e.printStackTrace(); + } + catch (InterruptedException e) + { + e.printStackTrace(); + sRet = "Timed out!"; + } + + return (sRet); + } + + public String ChmodDir(String sDir) + { + String sRet = ""; + int nFiles = 0; + String sSubDir = null; + String sTmpDir = fixFileName(sDir); + + File dir = new File(sTmpDir); + + if (dir.isDirectory()) { + sRet = "Changing permissions for " + sTmpDir; + + File [] files = dir.listFiles(); + if (files != null) { + if ((nFiles = files.length) > 0) { + for (int lcv = 0; lcv < nFiles; lcv++) { + if (files[lcv].isDirectory()) { + sSubDir = files[lcv].getAbsolutePath(); + sRet += "\n" + ChmodDir(sSubDir); + } + else { + // set the new file's permissions to rwxrwxrwx, if possible + try { + Process pProc = Runtime.getRuntime().exec(this.getSuArgs("chmod 777 "+files[lcv])); + RedirOutputThread outThrd = new RedirOutputThread(pProc, null); + outThrd.start(); + outThrd.joinAndStopRedirect(5000); + sRet += "\n\tchmod " + files[lcv].getName() + " ok"; + } catch (InterruptedException e) { + sRet += "\n\ttimeout waiting for chmod " + files[lcv].getName(); + } catch (IOException e) { + sRet += "\n\tunable to chmod " + files[lcv].getName(); + } + } + } + } + else + sRet += "\n\t<empty>"; + } + } + + // set the new directory's (or file's) permissions to rwxrwxrwx, if possible + try { + Process pProc = Runtime.getRuntime().exec(this.getSuArgs("chmod 777 "+sTmpDir)); + RedirOutputThread outThrd = new RedirOutputThread(pProc, null); + outThrd.start(); + outThrd.joinAndStopRedirect(5000); + sRet += "\n\tchmod " + sTmpDir + " ok"; + } catch (InterruptedException e) { + sRet += "\n\ttimeout waiting for chmod " + sTmpDir; + } catch (IOException e) { + sRet += "\n\tunable to chmod " + sTmpDir; + } + + return(sRet); + } + + public String TopActivity() + { + String sRet = ""; + ActivityManager aMgr = (ActivityManager) contextWrapper.getSystemService(Activity.ACTIVITY_SERVICE); + List< ActivityManager.RunningTaskInfo > taskInfo = null; + ComponentName componentInfo = null; + if (aMgr != null) + { + taskInfo = aMgr.getRunningTasks(1); + } + if (taskInfo != null) + { + // topActivity: "The activity component at the top of the history stack of the task. + // This is what the user is currently doing." + componentInfo = taskInfo.get(0).topActivity; + } + if (componentInfo != null) + { + sRet = componentInfo.getPackageName(); + } + return(sRet); + } + + private String PrintUsage() + { + String sRet = + "run [cmdline] - start program no wait\n" + + "exec [env pairs] [cmdline] - start program no wait optionally pass env\n" + + " key=value pairs (comma separated)\n" + + "execcwd [env pairs] [cmdline] - start program from specified directory\n" + + "execsu [env pairs] [cmdline] - start program as privileged user\n" + + "execcwdsu [env pairs] [cmdline] - start program from specified directory as privileged user\n" + + "kill [program name] - kill program no path\n" + + "killall - kill all processes started\n" + + "ps - list of running processes\n" + + "info - list of device info\n" + + " [os] - os version for device\n" + + " [id] - unique identifier for device\n" + + " [uptime] - uptime for device\n" + + " [uptimemillis] - uptime for device in milliseconds\n" + + " [sutuptimemillis] - uptime for SUT in milliseconds\n" + + " [systime] - current system time\n" + + " [screen] - width, height and bits per pixel for device\n" + + " [memory] - physical, free, available, storage memory\n" + + " for device\n" + + " [processes] - list of running processes see 'ps'\n" + + "deadman timeout - set the duration for the deadman timer\n" + + "alrt [on/off] - start or stop sysalert behavior\n" + + "disk [arg] - prints disk space info\n" + + "cp file1 file2 - copy file1 to file2\n" + + "time file - timestamp for file\n" + + "hash file - generate hash for file\n" + + "cd directory - change cwd\n" + + "cat file - cat file\n" + + "cwd - display cwd\n" + + "mv file1 file2 - move file1 to file2\n" + + "push filename - push file to device\n" + + "rm file - delete file\n" + + "rmdr directory - delete directory even if not empty\n" + + "mkdr directory - create directory\n" + + "dirw directory - tests whether the directory is writable\n" + + "isdir directory - test whether the directory exists\n" + + "chmod directory|file - change permissions of directory and contents (or file) to 777\n" + + "stat processid - stat process\n" + + "dead processid - print whether the process is alive or hung\n" + + "mems - dump memory stats\n" + + "ls - print directory\n" + + "tmpd - print temp directory\n" + + "ping [hostname/ipaddr] - ping a network device\n" + + "unzp zipfile destdir - unzip the zipfile into the destination dir\n" + + "zip zipfile src - zip the source file/dir into zipfile\n" + + "rebt - reboot device\n" + + "inst /path/filename.apk - install the referenced apk file\n" + + "uninst packagename - uninstall the referenced package and reboot\n" + + "uninstall packagename - uninstall the referenced package without a reboot\n" + + "updt pkgname pkgfile - unpdate the referenced package\n" + + "clok - the current device time expressed as the" + + " number of millisecs since epoch\n" + + "settime date time - sets the device date and time\n" + + " (YYYY/MM/DD HH:MM:SS)\n" + + "tzset timezone - sets the device timezone format is\n" + + " GMTxhh:mm x = +/- or a recognized Olsen string\n" + + "tzget - returns the current timezone set on the device\n" + + "rebt - reboot device\n" + + "adb ip|usb - set adb to use tcp/ip on port 5555 or usb\n" + + "activity - print package name of top (foreground) activity\n" + + "quit - disconnect SUTAgent\n" + + "exit - close SUTAgent\n" + + "ver - SUTAgent version\n" + + "help - you're reading it"; + return (sRet); + } +} diff --git a/build/mobile/sutagent/android/FindProcThread.java b/build/mobile/sutagent/android/FindProcThread.java new file mode 100644 index 000000000..f62d3b78d --- /dev/null +++ b/build/mobile/sutagent/android/FindProcThread.java @@ -0,0 +1,68 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package com.mozilla.SUTAgentAndroid.service; + +import java.util.List; + +import android.app.Activity; +import android.app.ActivityManager; +import android.content.ContextWrapper; + +public class FindProcThread extends Thread { + ContextWrapper contextWrapper = null; + String sProcNameToFind = ""; + boolean bFoundIt = false; + boolean bStillRunning = true; + + public FindProcThread(ContextWrapper ctx, String sProcessName) { + super("FindProcThread"); + this.contextWrapper = ctx; + this.sProcNameToFind = sProcessName; + this.bFoundIt = false; + } + + public void run() { + ActivityManager aMgr = (ActivityManager) contextWrapper.getSystemService(Activity.ACTIVITY_SERVICE); + List <ActivityManager.RunningAppProcessInfo> lProcesses; + int lcv = 0; + int nNumLoops = 0; + String strProcName = ""; + int nPID = 0; + + if (aMgr == null) + return; + + + // While we are still looping looking for the process in the list and we haven't found it + while (bStillRunning && !bFoundIt) { + lProcesses = aMgr.getRunningAppProcesses(); + if (lProcesses != null) { + for (lcv = 0; lcv < lProcesses.size(); lcv++) { + if (lProcesses.get(lcv).processName.contains(sProcNameToFind)) { + strProcName = lProcesses.get(lcv).processName; + nPID = lProcesses.get(lcv).pid; + bFoundIt = true; + break; + } + } + if (bFoundIt) // This saves you half a second of wait time if we've found it in the list + continue; + } + try { + Thread.sleep(500); // Sleep half a second + if (++nNumLoops > 10) { // loop up to 10 times + bStillRunning = false; + } + lProcesses = null; + System.gc(); + Thread.yield(); + } catch (InterruptedException e) { + e.printStackTrace(); + bStillRunning = false; + } + } + } + +} diff --git a/build/mobile/sutagent/android/Makefile.in b/build/mobile/sutagent/android/Makefile.in new file mode 100644 index 000000000..8fdc64b0e --- /dev/null +++ b/build/mobile/sutagent/android/Makefile.in @@ -0,0 +1,79 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +DEPTH = @DEPTH@ +topsrcdir = @top_srcdir@ +srcdir = @srcdir@ +VPATH = @srcdir@ + +include $(DEPTH)/config/autoconf.mk + +ANDROID_APK_NAME := sutAgentAndroid + +JAVAFILES = \ + AlertLooperThread.java \ + ASMozStub.java \ + CmdWorkerThread.java \ + DataWorkerThread.java \ + DoAlert.java \ + DoCommand.java \ + FindProcThread.java \ + Power.java \ + RedirOutputThread.java \ + RunCmdThread.java \ + RunDataThread.java \ + SUTAgentAndroid.java \ + WifiConfiguration.java \ + R.java \ + $(NULL) + +RES_FILES = \ + res/drawable/icon.png \ + res/drawable/ateamlogo.png \ + res/drawable/ic_stat_first.png \ + res/drawable/ic_stat_neterror.png \ + res/drawable/ic_stat_warning.png \ + res/layout/main.xml \ + res/values/strings.xml \ + $(NULL) + +GARBAGE += \ + AndroidManifest.xml \ + classes.dex \ + $(ANDROID_APK_NAME).ap_ \ + $(ANDROID_APK_NAME)-unsigned-unaligned.apk \ + $(ANDROID_APK_NAME)-unaligned.apk \ + $(ANDROID_APK_NAME).apk \ + $(NULL) + +GARBAGE_DIRS += network-libs + +EXTRA_JARS = $(srcdir)/network-libs/commons-net-2.0.jar:$(srcdir)/network-libs/jmdns.jar + +JAVA_CLASSPATH = $(ANDROID_SDK)/android.jar:$(EXTRA_JARS) + +include $(topsrcdir)/config/rules.mk + +# include Android specific java flags - using these instead of what's in rules.mk +include $(topsrcdir)/config/android-common.mk + +tools:: $(ANDROID_APK_NAME).apk + +classes.dex: $(JAVAFILES) + $(JAVAC) $(JAVAC_FLAGS) -d classes $(addprefix $(srcdir)/,$(JAVAFILES)) + $(DX) --dex --output=$@ classes $(subst :, ,$(EXTRA_JARS)) + +$(ANDROID_APK_NAME).ap_: AndroidManifest.xml + $(AAPT) package -f -M $< -I $(ANDROID_SDK)/android.jar -S res -F $@ + +$(ANDROID_APK_NAME)-unsigned-unaligned.apk: $(ANDROID_APK_NAME).ap_ classes.dex + cp $< $@ + $(ZIP) -0 $@ classes.dex + +$(ANDROID_APK_NAME)-unaligned.apk: $(ANDROID_APK_NAME)-unsigned-unaligned.apk + cp $< $@ + $(DEBUG_JARSIGNER) $@ + +$(ANDROID_APK_NAME).apk: $(ANDROID_APK_NAME)-unaligned.apk + $(ZIPALIGN) -f -v 4 $< $@ diff --git a/build/mobile/sutagent/android/Power.java b/build/mobile/sutagent/android/Power.java new file mode 100644 index 000000000..47497e547 --- /dev/null +++ b/build/mobile/sutagent/android/Power.java @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.os; + +import java.io.IOException; + +/** + * Class that provides access to some of the power management functions. + * + * {@hide} + */ +public class Power +{ + // can't instantiate this class + private Power() + { + } + + /** + * Wake lock that ensures that the CPU is running. The screen might + * not be on. + */ + public static final int PARTIAL_WAKE_LOCK = 1; + + /** + * Wake lock that ensures that the screen is on. + */ + public static final int FULL_WAKE_LOCK = 2; + + public static native void acquireWakeLock(int lock, String id); + public static native void releaseWakeLock(String id); + + /** + * Brightness value for fully off + */ + public static final int BRIGHTNESS_OFF = 0; + + /** + * Brightness value for dim backlight + */ + public static final int BRIGHTNESS_DIM = 20; + + /** + * Brightness value for fully on + */ + public static final int BRIGHTNESS_ON = 255; + + /** + * Brightness value to use when battery is low + */ + public static final int BRIGHTNESS_LOW_BATTERY = 10; + + /** + * Threshold for BRIGHTNESS_LOW_BATTERY (percentage) + * Screen will stay dim if battery level is <= LOW_BATTERY_THRESHOLD + */ + public static final int LOW_BATTERY_THRESHOLD = 10; + + /** + * Turn the screen on or off + * + * @param on Whether you want the screen on or off + */ + public static native int setScreenState(boolean on); + + public static native int setLastUserActivityTimeout(long ms); + + /** + * Turn the device off. + * + * This method is considered deprecated in favor of + * {@link android.policy.ShutdownThread.shutdownAfterDisablingRadio()}. + * + * @deprecated + * @hide + */ + @Deprecated + public static native void shutdown(); + + /** + * Reboot the device. + * @param reason code to pass to the kernel (e.g. "recovery"), or null. + * + * @throws IOException if reboot fails for some reason (eg, lack of + * permission) + */ + public static native void reboot(String reason) throws IOException; +} diff --git a/build/mobile/sutagent/android/R.java b/build/mobile/sutagent/android/R.java new file mode 100644 index 000000000..92c351506 --- /dev/null +++ b/build/mobile/sutagent/android/R.java @@ -0,0 +1,37 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* AUTO-GENERATED FILE. DO NOT MODIFY. + * + * This class was automatically generated by the + * aapt tool from the resource data it found. It + * should not be modified by hand. + */ + +package com.mozilla.SUTAgentAndroid; + +public final class R { + public static final class attr { + } + public static final class drawable { + public static final int ateamlogo=0x7f020000; + public static final int ic_stat_first=0x7f020001; + public static final int ic_stat_neterror=0x7f020002; + public static final int ic_stat_second=0x7f020003; + public static final int ic_stat_warning=0x7f020004; + public static final int icon=0x7f020005; + } + public static final class id { + public static final int Button01=0x7f050001; + public static final int Textview01=0x7f050000; + } + public static final class layout { + public static final int main=0x7f030000; + } + public static final class string { + public static final int app_name=0x7f040001; + public static final int foreground_service_started=0x7f040002; + public static final int hello=0x7f040000; + } +} diff --git a/build/mobile/sutagent/android/RedirOutputThread.java b/build/mobile/sutagent/android/RedirOutputThread.java new file mode 100644 index 000000000..a70bcddf6 --- /dev/null +++ b/build/mobile/sutagent/android/RedirOutputThread.java @@ -0,0 +1,197 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package com.mozilla.SUTAgentAndroid.service; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.PrintWriter; + +public class RedirOutputThread extends Thread + { + OutputStream out; + InputStream sutErr; + InputStream sutOut; + Process pProc; + String strOutput; + int nExitCode = -1; + private volatile boolean stopRedirRequested = false; + private volatile boolean redirStopped = false; + + public RedirOutputThread(Process pProc, OutputStream out) + { + super("RedirOutputThread"); + if (pProc != null) + { + this.pProc = pProc; + sutErr = pProc.getErrorStream(); // Stderr + sutOut = pProc.getInputStream(); // Stdout + } + if (out != null) + this.out = out; + + strOutput = ""; + } + + public void run() + { + boolean bStillRunning = true; + int nBytesOut = 0; + int nBytesErr = 0; + int nBytesRead = 0; + PrintWriter pOut = null; + byte[] buffer = new byte[1024]; + + if (out != null) + pOut = new PrintWriter(out); + else + bStillRunning = true; + + while (bStillRunning) + { + try + { + // If there's no output to collect, sleep for a while + // rather than checking again immediately, to avoid + // using up cpu capacity in a tight loop. + if (sutOut.available() == 0 && sutErr.available() == 0) + { + Thread.sleep(50); + } + if ((nBytesOut = sutOut.available()) > 0) + { + if (nBytesOut > buffer.length) + { + buffer = null; + System.gc(); + buffer = new byte[nBytesOut]; + } + nBytesRead = sutOut.read(buffer, 0, nBytesOut); + if (nBytesRead == -1) + bStillRunning = false; + else + { + String sRep = new String(buffer,0,nBytesRead).replace("\n", "\r\n"); + if (pOut != null) + { + pOut.print(sRep); + pOut.flush(); + } + else + strOutput += sRep; + } + } + + if ((nBytesErr = sutErr.available()) > 0) + { + if (nBytesErr > buffer.length) + { + buffer = null; + System.gc(); + buffer = new byte[nBytesErr]; + } + nBytesRead = sutErr.read(buffer, 0, nBytesErr); + if (nBytesRead == -1) + bStillRunning = false; + else + { + String sRep = new String(buffer,0,nBytesRead).replace("\n", "\r\n"); + if (pOut != null) + { + pOut.print(sRep); + pOut.flush(); + } + else + strOutput += sRep; + } + } + + bStillRunning = (stopRedirRequested == false) && + (IsProcRunning(pProc) || (sutOut.available() > 0) || (sutErr.available() > 0)); + } + catch (IOException e) + { + e.printStackTrace(); + } + catch (java.lang.IllegalArgumentException e) + { + // Bug 743766: InputStream.available() unexpectedly throws this sometimes + e.printStackTrace(); + } + catch (InterruptedException e) + { + e.printStackTrace(); + } + } + + // notify stopRedirect that redirection has stopped + redirStopped = true; + if (stopRedirRequested) + { + synchronized(this) + { + notifyAll(); + } + } + + // wait for process to end; if it has not already ended, then destroy it + try + { + pProc.waitFor(); + } + catch (InterruptedException e) + { + e.printStackTrace(); + pProc.destroy(); + } + buffer = null; + System.gc(); + } + + private boolean IsProcRunning(Process pProc) + { + boolean bRet = false; + + try + { + nExitCode = pProc.exitValue(); + } + catch (IllegalThreadStateException z) + { + nExitCode = -1; + bRet = true; + } + + return(bRet); + } + + public synchronized void stopRedirect() + { + stopRedirRequested = true; + // wait for notification that redirection has stopped + if (!redirStopped) + { + try + { + // max wait time is somewhat arbitrary and provided "just in case"; + // we expect to be notified as soon as run() completes its current + // sleep and checks the stopRedirRequested flag + wait(500); + } + catch (InterruptedException e) + { + e.printStackTrace(); + } + } + } + + public void joinAndStopRedirect(long millis) throws InterruptedException + { + super.join(millis); + if (out != null) + stopRedirect(); + } + + } diff --git a/build/mobile/sutagent/android/RunCmdThread.java b/build/mobile/sutagent/android/RunCmdThread.java new file mode 100644 index 000000000..807ed8c02 --- /dev/null +++ b/build/mobile/sutagent/android/RunCmdThread.java @@ -0,0 +1,273 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package com.mozilla.SUTAgentAndroid.service; + +import java.io.IOException; +import java.io.InputStream; +import java.net.ServerSocket; +import java.net.Socket; +import java.net.SocketTimeoutException; +import java.util.ArrayList; +import java.util.List; + +import com.mozilla.SUTAgentAndroid.R; +import com.mozilla.SUTAgentAndroid.SUTAgentAndroid; + +import android.app.Notification; +import android.app.NotificationManager; +import android.app.PendingIntent; +import android.content.Context; +import android.content.Intent; +import android.os.Handler; + +public class RunCmdThread extends Thread + { + private ServerSocket SvrSocket = null; + private Socket socket = null; + private Handler handler = null; + boolean bListening = true; + boolean bNetError = false; + List<CmdWorkerThread> theWorkers = new ArrayList<CmdWorkerThread>(); + android.app.Service svc = null; + + public RunCmdThread(ServerSocket socket, android.app.Service service, Handler handler) + { + super("RunCmdThread"); + this.SvrSocket = socket; + this.svc = service; + this.handler = handler; + } + + public void StopListening() + { + bListening = false; + } + + public void run() { + try { + SvrSocket.setSoTimeout(5000); + while (bListening) + { + try + { + socket = SvrSocket.accept(); + CmdWorkerThread theWorker = new CmdWorkerThread(this, socket); + theWorker.start(); + theWorkers.add(theWorker); + } + catch (SocketTimeoutException toe) + { + continue; + } + catch (IOException e) + { + e.printStackTrace(); + continue; + } + } + + int nNumWorkers = theWorkers.size(); + for (int lcv = 0; lcv < nNumWorkers; lcv++) + { + if (theWorkers.get(lcv).isAlive()) + { + theWorkers.get(lcv).StopListening(); + while(theWorkers.get(lcv).isAlive()) + ; + } + } + + theWorkers.clear(); + + SvrSocket.close(); + + svc.stopSelf(); + +// SUTAgentAndroid.me.finish(); + } + catch (IOException e) + { + e.printStackTrace(); + } + return; + } + + private String SendPing(String sIPAddr) + { + Process pProc; + String sRet = ""; + String [] theArgs = new String [4]; + boolean bStillRunning = true; + int nBytesOut = 0; + int nBytesErr = 0; + int nBytesRead = 0; + byte[] buffer = new byte[1024]; + + theArgs[0] = "ping"; + theArgs[1] = "-c"; + theArgs[2] = "3"; + theArgs[3] = sIPAddr; + + try + { + pProc = Runtime.getRuntime().exec(theArgs); + + InputStream sutOut = pProc.getInputStream(); + InputStream sutErr = pProc.getErrorStream(); + + while (bStillRunning) + { + try + { + if ((nBytesOut = sutOut.available()) > 0) + { + if (nBytesOut > buffer.length) + { + buffer = null; + System.gc(); + buffer = new byte[nBytesOut]; + } + nBytesRead = sutOut.read(buffer, 0, nBytesOut); + if (nBytesRead == -1) + bStillRunning = false; + else + { + String sRep = new String(buffer,0,nBytesRead).replace("\n", "\r\n"); + sRet += sRep; + sRep = null; + } + } + + if ((nBytesErr = sutErr.available()) > 0) + { + if (nBytesErr > buffer.length) + { + buffer = null; + System.gc(); + buffer = new byte[nBytesErr]; + } + nBytesRead = sutErr.read(buffer, 0, nBytesErr); + if (nBytesRead == -1) + bStillRunning = false; + else + { + String sRep = new String(buffer,0,nBytesRead).replace("\n", "\r\n"); + sRet += sRep; + sRep = null; + } + } + + bStillRunning = (IsProcRunning(pProc) || (sutOut.available() > 0) || (sutErr.available() > 0)); + } + catch (IOException e) + { + e.printStackTrace(); + } + + if ((bStillRunning == true) && (nBytesErr == 0) && (nBytesOut == 0)) + { + try { + sleep(2000); + } + catch (InterruptedException e) { + e.printStackTrace(); + } + } + } + + pProc.destroy(); + pProc = null; + } + catch (IOException e) + { + sRet = e.getMessage(); + e.printStackTrace(); + } + + return (sRet); + } + + private boolean IsProcRunning(Process pProc) + { + boolean bRet = false; + @SuppressWarnings("unused") + int nExitCode = 0; + + try + { + nExitCode = pProc.exitValue(); + } + catch (IllegalThreadStateException z) + { + bRet = true; + } + catch (Exception e) + { + e.printStackTrace(); + } + + return(bRet); + } + + private void SendNotification(String tickerText, String expandedText) + { + NotificationManager notificationManager = (NotificationManager)svc.getSystemService(Context.NOTIFICATION_SERVICE); + +// int icon = android.R.drawable.stat_notify_more; +// int icon = R.drawable.ic_stat_first; +// int icon = R.drawable.ic_stat_second; +// int icon = R.drawable.ic_stat_neterror; + int icon = R.drawable.ateamlogo; + long when = System.currentTimeMillis(); + + Notification notification = new Notification(icon, tickerText, when); + + notification.flags |= (Notification.FLAG_INSISTENT | Notification.FLAG_AUTO_CANCEL); + notification.defaults |= Notification.DEFAULT_SOUND; + notification.defaults |= Notification.DEFAULT_VIBRATE; + notification.defaults |= Notification.DEFAULT_LIGHTS; + + Context context = svc.getApplicationContext(); + + // Intent to launch an activity when the extended text is clicked + Intent intent2 = new Intent(svc, SUTAgentAndroid.class); + PendingIntent launchIntent = PendingIntent.getActivity(context, 0, intent2, 0); + + notification.setLatestEventInfo(context, tickerText, expandedText, launchIntent); + + notificationManager.notify(1959, notification); + } + + private void CancelNotification() + { + NotificationManager notificationManager = (NotificationManager)svc.getSystemService(Context.NOTIFICATION_SERVICE); + notificationManager.cancel(1959); + } + + class doCancelNotification implements Runnable + { + public void run() + { + CancelNotification(); + } + }; + + class doSendNotification implements Runnable + { + private String sTitle = ""; + private String sBText = ""; + + doSendNotification(String sTitle, String sBodyText) + { + this.sTitle = sTitle; + this.sBText = sBodyText; + } + + public void run() + { + SendNotification(sTitle, sBText); + } + }; +} diff --git a/build/mobile/sutagent/android/RunDataThread.java b/build/mobile/sutagent/android/RunDataThread.java new file mode 100644 index 000000000..77837372f --- /dev/null +++ b/build/mobile/sutagent/android/RunDataThread.java @@ -0,0 +1,92 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package com.mozilla.SUTAgentAndroid.service; + +import java.io.IOException; +import java.net.ServerSocket; +import java.net.Socket; +import java.net.SocketTimeoutException; +import java.util.ArrayList; +import java.util.List; +import java.util.Timer; + +public class RunDataThread extends Thread + { + Timer heartBeatTimer; + + private ServerSocket SvrSocket = null; + private Socket socket = null; + boolean bListening = true; + List<DataWorkerThread> theWorkers = new ArrayList<DataWorkerThread>(); + android.app.Service svc = null; + + public RunDataThread(ServerSocket socket, android.app.Service service) + { + super("RunDataThread"); + this.SvrSocket = socket; + this.svc = service; + } + + public void StopListening() + { + bListening = false; + } + + public void SendToDataChannel(String strToSend) + { + int nNumWorkers = theWorkers.size(); + for (int lcv = 0; lcv < nNumWorkers; lcv++) + { + if (theWorkers.get(lcv).isAlive()) + { + theWorkers.get(lcv).SendString(strToSend); + } + } + return; + } + + public void run() { + try { + SvrSocket.setSoTimeout(5000); + while (bListening) + { + try + { + socket = SvrSocket.accept(); + DataWorkerThread theWorker = new DataWorkerThread(this, socket); + theWorker.start(); + theWorkers.add(theWorker); + } + catch (SocketTimeoutException toe) + { + continue; + } + } + + int nNumWorkers = theWorkers.size(); + for (int lcv = 0; lcv < nNumWorkers; lcv++) + { + if (theWorkers.get(lcv).isAlive()) + { + theWorkers.get(lcv).StopListening(); + while(theWorkers.get(lcv).isAlive()) + ; + } + } + + theWorkers.clear(); + + SvrSocket.close(); + + svc.stopSelf(); + } + catch (IOException e) + { +// Toast.makeText(SUTAgentAndroid.me.getApplicationContext(), e.getMessage(), Toast.LENGTH_LONG).show(); + e.printStackTrace(); + } + return; + } + } diff --git a/build/mobile/sutagent/android/SUTAgentAndroid.java b/build/mobile/sutagent/android/SUTAgentAndroid.java new file mode 100644 index 000000000..ab0f995b3 --- /dev/null +++ b/build/mobile/sutagent/android/SUTAgentAndroid.java @@ -0,0 +1,893 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package com.mozilla.SUTAgentAndroid; + +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.io.PrintWriter; +import java.net.InetAddress; +import org.apache.http.conn.util.InetAddressUtils; +import java.net.NetworkInterface; +import java.net.SocketException; +import java.net.UnknownHostException; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.Enumeration; +import java.util.Formatter; +import java.util.List; +import java.util.Timer; + +import com.mozilla.SUTAgentAndroid.service.ASMozStub; +import com.mozilla.SUTAgentAndroid.service.DoCommand; +import android.app.Activity; +import android.bluetooth.BluetoothAdapter; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.pm.ActivityInfo; +import android.content.res.Configuration; +import android.net.Uri; +import android.net.wifi.SupplicantState; +import android.net.wifi.WifiConfiguration; +import android.net.wifi.WifiInfo; +import android.net.wifi.WifiManager; +import android.net.wifi.WifiManager.WifiLock; +import android.os.BatteryManager; +import android.os.Build; +import android.os.Build.VERSION; +import android.os.Bundle; +import android.os.Handler; +import android.telephony.TelephonyManager; +import android.util.Log; +import android.view.Menu; +import android.view.MenuItem; +import android.view.View; +import android.view.View.OnClickListener; +import android.widget.Button; +import android.widget.TextView; +import android.widget.Toast; + +public class SUTAgentAndroid extends Activity + { + final Handler mHandler = new Handler(); + + public static final int START_PRG = 1959; + MenuItem mExitMenuItem; + Timer timer = null; + + public static String sUniqueID = null; + public static String sLocalIPAddr = null; + public static String sACStatus = null; + public static String sPowerStatus = null; + public static int nChargeLevel = 0; + public static int nBatteryTemp = 0; + public static long nCreateTimeMillis = System.currentTimeMillis(); + + String lineSep = System.getProperty("line.separator"); + public PrintWriter dataOut = null; + + private static boolean bNetworkingStarted = false; + private static String RegSvrIPAddr = ""; + private static String RegSvrIPPort = ""; + private static String HardwareID = ""; + private static String Pool = ""; + private static String Abi = ""; + private static String sRegString = ""; + private static boolean LogCommands = false; + + private WifiLock wl = null; + + private BroadcastReceiver battReceiver = null; + + private TextView tv = null; + + public boolean onCreateOptionsMenu(Menu menu) + { + mExitMenuItem = menu.add("Exit"); + mExitMenuItem.setIcon(android.R.drawable.ic_menu_close_clear_cancel); + return super.onCreateOptionsMenu(menu); + } + + public boolean onMenuItemSelected(int featureId, MenuItem item) + { + if (item == mExitMenuItem) + { + finish(); + } + return super.onMenuItemSelected(featureId, item); + } + + public static String getRegSvrIPAddr() + { + return(RegSvrIPAddr); + } + + public void pruneCommandLog(String datestamp, String testroot) + { + + String today = ""; + String yesterday = ""; + + // test root can be null (if getTestRoot fails), handle that: + if (testroot == null) { + testroot = ""; + } + + try { + SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss:SSS"); + Date dateObj = sdf.parse(datestamp); + SimpleDateFormat sdf_file = new SimpleDateFormat("yyyy-MM-dd"); + + today = sdf_file.format(dateObj); + yesterday = sdf_file.format(new Date(dateObj.getTime() - 1000*60*60*24)); + } catch (ParseException pe) {} + + File dir = new File(testroot); + + if (!dir.isDirectory()) + return; + + File [] files = dir.listFiles(); + if (files == null) + return; + + for (int iter = 0; iter < files.length; iter++) { + String fName = files[iter].getName(); + if (fName.endsWith("sutcommands.txt")) { + if (fName.endsWith(today + "-sutcommands.txt") || fName.endsWith(yesterday + "-sutcommands.txt")) + continue; + + if (files[iter].delete()) + Log.i("SUTAgentAndroid", "Deleted old command logfile: " + files[iter]); + else + Log.e("SUTAgentAndroid", "Unable to delete old command logfile: " + files[iter]); + } + } + } + + /** Called when the activity is first created. */ + @Override + public void onCreate(Bundle savedInstanceState) + { + super.onCreate(savedInstanceState); + + setContentView(R.layout.main); + + fixScreenOrientation(); + + DoCommand dc = new DoCommand(getApplication()); + + dc.FixDataLocalPermissions(); + + // Get configuration settings from "ini" file + File dir = getFilesDir(); + File iniFile = new File(dir, "SUTAgent.ini"); + String sIniFile = iniFile.getAbsolutePath(); + + String lc = dc.GetIniData("General", "LogCommands", sIniFile); + if (lc != "" && Integer.parseInt(lc) == 1) { + SUTAgentAndroid.LogCommands = true; + } + SUTAgentAndroid.RegSvrIPAddr = dc.GetIniData("Registration Server", "IPAddr", sIniFile); + SUTAgentAndroid.RegSvrIPPort = dc.GetIniData("Registration Server", "PORT", sIniFile); + SUTAgentAndroid.HardwareID = dc.GetIniData("Registration Server", "HARDWARE", sIniFile); + SUTAgentAndroid.Pool = dc.GetIniData("Registration Server", "POOL", sIniFile); + SUTAgentAndroid.Abi = android.os.Build.CPU_ABI; + log(dc, "onCreate"); + + tv = (TextView) this.findViewById(R.id.Textview01); + + if (getLocalIpAddress() == null) + setUpNetwork(sIniFile); + + String macAddress = "Unknown"; + if (android.os.Build.VERSION.SDK_INT > 8) { + try { + NetworkInterface iface = NetworkInterface.getByInetAddress(InetAddress.getAllByName(getLocalIpAddress())[0]); + if (iface != null) + { + byte[] mac = iface.getHardwareAddress(); + if (mac != null) + { + StringBuilder sb = new StringBuilder(); + Formatter f = new Formatter(sb); + for (int i = 0; i < mac.length; i++) + { + f.format("%02x%s", mac[i], (i < mac.length - 1) ? ":" : ""); + } + macAddress = sUniqueID = sb.toString(); + } + } + } + catch (UnknownHostException ex) {} + catch (SocketException ex) {} + } + else + { + // Fall back to getting info from wifiman on older versions of Android, + // which don't support the NetworkInterface interface + WifiManager wifiMan = (WifiManager)getSystemService(Context.WIFI_SERVICE); + if (wifiMan != null) + { + WifiInfo wifi = wifiMan.getConnectionInfo(); + if (wifi != null) + macAddress = wifi.getMacAddress(); + if (macAddress != null) + sUniqueID = macAddress; + } + } + + if (sUniqueID == null) + { + BluetoothAdapter ba = BluetoothAdapter.getDefaultAdapter(); + if ((ba != null) && (ba.isEnabled() != true)) + { + ba.enable(); + while(ba.getState() != BluetoothAdapter.STATE_ON) + { + try { + Thread.sleep(1000); + } + catch (InterruptedException e) + { + e.printStackTrace(); + } + } + + sUniqueID = ba.getAddress(); + + ba.disable(); + while(ba.getState() != BluetoothAdapter.STATE_OFF) + { + try { + Thread.sleep(1000); + } + catch (InterruptedException e) + { + e.printStackTrace(); + } + } + } + else + { + if (ba != null) + { + sUniqueID = ba.getAddress(); + sUniqueID.toLowerCase(); + } + } + } + + if (sUniqueID == null) + { + TelephonyManager mTelephonyMgr = (TelephonyManager)getSystemService(TELEPHONY_SERVICE); + if (mTelephonyMgr != null) + { + sUniqueID = mTelephonyMgr.getDeviceId(); + if (sUniqueID == null) + { + sUniqueID = "0011223344556677"; + } + } + } + + String hwid = getHWID(this); + + sLocalIPAddr = getLocalIpAddress(); + Toast.makeText(getApplication().getApplicationContext(), "SUTAgent [" + sLocalIPAddr + "] ...", Toast.LENGTH_LONG).show(); + + String sConfig = "Unique ID: " + sUniqueID + lineSep; + sConfig += "HWID: " + hwid + lineSep; + sConfig += "ABI: " + Abi + lineSep; + sConfig += "OS Info" + lineSep; + sConfig += "\t" + dc.GetOSInfo() + lineSep; + sConfig += "Screen Info" + lineSep; + int [] xy = dc.GetScreenXY(); + sConfig += "\t Width: " + xy[0] + lineSep; + sConfig += "\t Height: " + xy[1] + lineSep; + sConfig += "Memory Info" + lineSep; + sConfig += "\t" + dc.GetMemoryInfo() + lineSep; + sConfig += "Network Info" + lineSep; + sConfig += "\tMac Address: " + macAddress + lineSep; + sConfig += "\tIP Address: " + sLocalIPAddr + lineSep; + + displayStatus(sConfig); + + sRegString = "NAME=" + sUniqueID; + sRegString += "&IPADDR=" + sLocalIPAddr; + sRegString += "&CMDPORT=" + 20701; + sRegString += "&DATAPORT=" + 20700; + sRegString += "&OS=Android-" + dc.GetOSInfo(); + sRegString += "&SCRNWIDTH=" + xy[0]; + sRegString += "&SCRNHEIGHT=" + xy[1]; + sRegString += "&BPP=8"; + sRegString += "&MEMORY=" + dc.GetMemoryConfig(); + sRegString += "&HARDWARE=" + HardwareID; + sRegString += "&POOL=" + Pool; + sRegString += "&ABI=" + Abi; + + String sTemp = Uri.encode(sRegString,"=&"); + sRegString = "register " + sTemp; + + pruneCommandLog(dc.GetSystemTime(), dc.GetTestRoot()); + + if (!bNetworkingStarted) + { + Thread thread = new Thread(null, doStartService, "StartServiceBkgnd"); + thread.start(); + bNetworkingStarted = true; + + Thread thread2 = new Thread(null, doRegisterDevice, "RegisterDeviceBkgnd"); + thread2.start(); + } + + monitorBatteryState(); + + // If we are returning from an update let'em know we're back + Thread thread3 = new Thread(null, doUpdateCallback, "UpdateCallbackBkgnd"); + thread3.start(); + + final Button goButton = (Button) findViewById(R.id.Button01); + goButton.setOnClickListener(new OnClickListener() { + public void onClick(View v) { + finish(); + } + }); + } + + private class UpdateStatus implements Runnable { + public String sText = ""; + + UpdateStatus(String sStatus) { + sText = sStatus; + } + + public void run() { + displayStatus(sText); + } + } + + public synchronized void displayStatus(String sStatus) { + String sTVText = (String) tv.getText(); + sTVText += sStatus; + tv.setText(sTVText); + } + + public void fixScreenOrientation() + { + setRequestedOrientation((getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) ? + ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE : ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); + } + + protected void onActivityResult(int requestCode, int resultCode, Intent data) + { + if (requestCode == START_PRG) + { + Toast.makeText(getApplication().getApplicationContext(), "SUTAgent startprg finished ...", Toast.LENGTH_LONG).show(); + } + } + + @Override + public void onDestroy() + { + DoCommand dc = new DoCommand(getApplication()); + super.onDestroy(); + if (isFinishing()) + { + log(dc, "onDestroy - finishing"); + Intent listenerSvc = new Intent(this, ASMozStub.class); + listenerSvc.setAction("com.mozilla.SUTAgentAndroid.service.LISTENER_SERVICE"); + stopService(listenerSvc); + bNetworkingStarted = false; + + unregisterReceiver(battReceiver); + + if (wl != null) + wl.release(); + + System.exit(0); + } + else + { + log(dc, "onDestroy - not finishing"); + } + } + + private void logMemory(String caller) + { + DoCommand dc = new DoCommand(getApplication()); + if (dc != null) + { + log(dc, caller); + log(dc, dc.GetMemoryInfo()); + String procInfo = dc.GetProcessInfo(); + if (procInfo != null) + { + String lines[] = procInfo.split("\n"); + for (String line : lines) + { + if (line.contains("mozilla")) + { + log(dc, line); + String words[] = line.split("\t"); + if ((words != null) && (words.length > 1)) + { + log(dc, dc.StatProcess(words[1])); + } + } + } + } + } + else + { + Log.e("SUTAgentAndroid", "logMemory: unable to log to file!"); + } + } + + @Override + public void onLowMemory() + { + System.gc(); + logMemory("onLowMemory"); + } + + @Override + public void onTrimMemory(int level) + { + System.gc(); + logMemory("onTrimMemory"+level); + } + + private void monitorBatteryState() + { + battReceiver = new BroadcastReceiver() + { + public void onReceive(Context context, Intent intent) + { + StringBuilder sb = new StringBuilder(); + + int rawlevel = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1); // charge level from 0 to scale inclusive + int scale = intent.getIntExtra(BatteryManager.EXTRA_SCALE, -1); // Max value for charge level + int status = intent.getIntExtra(BatteryManager.EXTRA_STATUS, -1); + int health = intent.getIntExtra(BatteryManager.EXTRA_HEALTH, -1); + boolean present = intent.getBooleanExtra(BatteryManager.EXTRA_PRESENT, false); + int plugged = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1); //0 if the device is not plugged in; 1 if plugged into an AC power adapter; 2 if plugged in via USB. +// int voltage = intent.getIntExtra(BatteryManager.EXTRA_VOLTAGE, -1); // voltage in millivolts + nBatteryTemp = intent.getIntExtra(BatteryManager.EXTRA_TEMPERATURE, -1); // current battery temperature in tenths of a degree Centigrade +// String technology = intent.getStringExtra(BatteryManager.EXTRA_TECHNOLOGY); + + nChargeLevel = -1; // percentage, or -1 for unknown + if (rawlevel >= 0 && scale > 0) + { + nChargeLevel = (rawlevel * 100) / scale; + } + + if (plugged > 0) + sACStatus = "ONLINE"; + else + sACStatus = "OFFLINE"; + + if (present == false) + sb.append("NO BATTERY"); + else + { + if (nChargeLevel < 10) + sb.append("Critical"); + else if (nChargeLevel < 33) + sb.append("LOW"); + else if (nChargeLevel > 80) + sb.append("HIGH"); + } + + if (BatteryManager.BATTERY_HEALTH_OVERHEAT == health) + { + sb.append("Overheated "); + sb.append((((float)(nBatteryTemp))/10)); + sb.append("(C)"); + } + else + { + switch(status) + { + case BatteryManager.BATTERY_STATUS_UNKNOWN: + // old emulator; maybe also when plugged in with no battery + if (present == true) + sb.append(" UNKNOWN"); + break; + case BatteryManager.BATTERY_STATUS_CHARGING: + sb.append(" CHARGING"); + break; + case BatteryManager.BATTERY_STATUS_DISCHARGING: + sb.append(" DISCHARGING"); + break; + case BatteryManager.BATTERY_STATUS_NOT_CHARGING: + sb.append(" NOTCHARGING"); + break; + case BatteryManager.BATTERY_STATUS_FULL: + sb.append(" FULL"); + break; + default: + if (present == true) + sb.append("Unknown"); + break; + } + } + + sPowerStatus = sb.toString(); + } + }; + + IntentFilter battFilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED); + registerReceiver(battReceiver, battFilter); + } + + public boolean setUpNetwork(String sIniFile) + { + boolean bRet = false; + int lcv = 0; + int lcv2 = 0; + WifiManager wifi = (WifiManager) getSystemService(Context.WIFI_SERVICE); + WifiConfiguration wc = new WifiConfiguration(); + DoCommand tmpdc = new DoCommand(getApplication()); + + String ssid = tmpdc.GetIniData("Network Settings", "SSID", sIniFile); + String auth = tmpdc.GetIniData("Network Settings", "AUTH", sIniFile); + String encr = tmpdc.GetIniData("Network Settings", "ENCR", sIniFile); + String key = tmpdc.GetIniData("Network Settings", "KEY", sIniFile); + String eap = tmpdc.GetIniData("Network Settings", "EAP", sIniFile); + String adhoc = tmpdc.GetIniData("Network Settings", "ADHOC", sIniFile); + + Toast.makeText(getApplication().getApplicationContext(), "Starting and configuring network", Toast.LENGTH_LONG).show(); +/* + ContentResolver cr = getContentResolver(); + int nRet; + try { + nRet = Settings.System.getInt(cr, Settings.System.WIFI_USE_STATIC_IP); + String foo2 = "" + nRet; + } catch (SettingNotFoundException e1) { + e1.printStackTrace(); + } +*/ +/* + wc.SSID = "\"Mozilla-Build\""; + wc.preSharedKey = "\"MozillaBuildQA500\""; + wc.hiddenSSID = true; + wc.status = WifiConfiguration.Status.ENABLED; + wc.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.OPEN); + wc.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.TKIP); + wc.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.CCMP); + wc.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK); + wc.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.TKIP); + wc.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.CCMP); + wc.allowedProtocols.set(WifiConfiguration.Protocol.RSN); +*/ + wc.SSID = "\"" + ssid + "\""; +// wc.SSID = "\"Mozilla-G\""; +// wc.SSID = "\"Mozilla\""; + + if (auth.contentEquals("wpa2")) + { + wc.allowedProtocols.set(WifiConfiguration.Protocol.RSN); + wc.preSharedKey = null; + } + + if (encr.contentEquals("aes")) + { + wc.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.CCMP); + wc.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.CCMP); + } + + if (eap.contentEquals("peap")) + { + wc.eap.setValue("PEAP"); + wc.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_EAP); + wc.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.IEEE8021X); + } + + wc.hiddenSSID = false; + wc.status = WifiConfiguration.Status.ENABLED; + + wc.password.setValue("\"password\""); + wc.identity.setValue("\"bmoss@mozilla.com\""); + + if (!wifi.isWifiEnabled()) + wifi.setWifiEnabled(true); + + while(wifi.getWifiState() != WifiManager.WIFI_STATE_ENABLED) + { + Thread.yield(); + if (++lcv > 10000) + return(bRet); + } + + wl = wifi.createWifiLock(WifiManager.WIFI_MODE_FULL, "SUTAgent"); + if (wl != null) + wl.acquire(); + + WifiConfiguration foo = null; + int nNetworkID = -1; + + List<WifiConfiguration> connsLst = wifi.getConfiguredNetworks(); + int nConns = connsLst.size(); + for (int i = 0; i < nConns; i++) + { + + foo = connsLst.get(i); + if (foo.SSID.equalsIgnoreCase(wc.SSID)) + { + nNetworkID = foo.networkId; + wc.networkId = foo.networkId; + break; + } + } + + int res; + + if (nNetworkID != -1) + { + res = wifi.updateNetwork(wc); + } + else + { + res = wifi.addNetwork(wc); + } + + Log.d("WifiPreference", "add Network returned " + res ); + + boolean b = wifi.enableNetwork(res, true); + Log.d("WifiPreference", "enableNetwork returned " + b ); + + wifi.saveConfiguration(); + + WifiInfo wi = wifi.getConnectionInfo(); + SupplicantState ss = wi.getSupplicantState(); + + lcv = 0; + lcv2 = 0; + + while (ss.compareTo(SupplicantState.COMPLETED) != 0) + { + try { + Thread.sleep(1000); + } + catch (InterruptedException e) + { + e.printStackTrace(); + } + + if (wi != null) + wi = null; + if (ss != null) + ss = null; + wi = wifi.getConnectionInfo(); + ss = wi.getSupplicantState(); + if (++lcv > 60) + { + if (++lcv2 > 5) + { + Toast.makeText(getApplication().getApplicationContext(), "Unable to start and configure network", Toast.LENGTH_LONG).show(); + return(bRet); + } + else + { + Toast.makeText(getApplication().getApplicationContext(), "Resetting wifi interface", Toast.LENGTH_LONG).show(); + if (wl != null) + wl.release(); + wifi.setWifiEnabled(false); + while(wifi.getWifiState() != WifiManager.WIFI_STATE_DISABLED) + { + Thread.yield(); + } + + wifi.setWifiEnabled(true); + while(wifi.getWifiState() != WifiManager.WIFI_STATE_ENABLED) + { + Thread.yield(); + } + b = wifi.enableNetwork(res, true); + Log.d("WifiPreference", "enableNetwork returned " + b ); + if (wl != null) + wl.acquire(); + lcv = 0; + } + } + } + + lcv = 0; + while(getLocalIpAddress() == null) + { + if (++lcv > 10000) + return(bRet); + } + + Toast.makeText(getApplication().getApplicationContext(), "Network started and configured", Toast.LENGTH_LONG).show(); + bRet = true; + + return(bRet); + } + + // If there is an update.info file callback the server and send the status + private Runnable doUpdateCallback = new Runnable() { + public void run() { + DoCommand dc = new DoCommand(getApplication()); + String sRet = dc.UpdateCallBack("update.info"); + if (sRet.length() > 0) { + if (sRet.contains("ok")) { + sRet = "Callback Server contacted successfully" + lineSep; + } else if (sRet.contains("Nothing to do")) { + sRet = ""; + } else { + sRet = "Callback Server NOT contacted successfully" + lineSep; + } + } + if (sRet.length() > 0) + mHandler.post(new UpdateStatus(sRet)); + dc = null; + } + }; + + // registers with the reg server defined in the SUTAgent.ini file + private Runnable doRegisterDevice = new Runnable() { + public void run() { + DoCommand dc = new DoCommand(getApplication()); + String sRet = ""; + if (RegSvrIPAddr.length() > 0) { + String sRegRet = dc.RegisterTheDevice(RegSvrIPAddr, RegSvrIPPort, sRegString); + if (sRegRet.contains("ok")) { + sRet += "Registered with testserver" + lineSep; + sRet += "\tIPAddress: " + RegSvrIPAddr + lineSep; + if (RegSvrIPPort.length() > 0) + sRet += "\tPort: " + RegSvrIPPort + lineSep; + } else { + sRet += "Not registered with testserver" + lineSep; + } + } else { + sRet += "Not registered with testserver" + lineSep; + } + + if (sRet.length() > 0) + mHandler.post(new UpdateStatus(sRet)); + dc = null; + } + }; + + // this starts the listener service for the command and data channels + private Runnable doStartService = new Runnable() + { + public void run() + { + Intent listenerService = new Intent(); + listenerService.setAction("com.mozilla.SUTAgentAndroid.service.LISTENER_SERVICE"); + startService(listenerService); + } + }; + + static String sHWID = null; + public static String getHWID(Context cx) { + if (sHWID != null) + return sHWID; + + // If we're on SDK version > 8, use Build.SERIAL + if (android.os.Build.VERSION.SDK_INT > 8) { + sHWID = android.os.Build.SERIAL; + } + + if (sHWID != null) + return sHWID; + + // Otherwise, try from the telephony manager + TelephonyManager mTelephonyMgr = (TelephonyManager) cx.getSystemService(TELEPHONY_SERVICE); + if (mTelephonyMgr != null) { + sHWID = mTelephonyMgr.getDeviceId(); + } + + if (sHWID != null) + return sHWID; + + // Otherwise, try WIFI_SERVICE and use the wifi manager + WifiManager wifiMan = (WifiManager) cx.getSystemService(Context.WIFI_SERVICE); + if (wifiMan != null) { + WifiInfo wifi = wifiMan.getConnectionInfo(); + if (wifi != null) { + sHWID = "wifimac" + wifi.getMacAddress(); + } + } + + if (sHWID != null) + return sHWID; + + sHWID = "0011223344556677"; + + return sHWID; + } + + public static InetAddress getLocalInetAddress() throws SocketException + { + for (Enumeration<NetworkInterface> en = NetworkInterface.getNetworkInterfaces(); en.hasMoreElements();) + { + NetworkInterface intf = en.nextElement(); + for (Enumeration<InetAddress> enumIpAddr = intf.getInetAddresses(); enumIpAddr.hasMoreElements();) + { + InetAddress inetAddress = enumIpAddr.nextElement(); + if (!inetAddress.isLoopbackAddress() && InetAddressUtils.isIPv4Address(inetAddress.getHostAddress())) + { + return inetAddress; + } + } + } + + return null; + } + + public String getLocalIpAddress() + { + try { + InetAddress inetAddress = getLocalInetAddress(); + if (inetAddress != null) + return inetAddress.getHostAddress().toString(); + } + catch (SocketException ex) + { + Toast.makeText(getApplication().getApplicationContext(), ex.toString(), Toast.LENGTH_LONG).show(); + } + return null; + } + + public static void log(DoCommand dc, String message) + { + Log.i("SUTAgentAndroid", message); + + if (SUTAgentAndroid.LogCommands == false) + { + return; + } + + if (message == null) + { + Log.e("SUTAgentAndroid", "bad arguments in log()!"); + return; + } + String fileDateStr = "00"; + String testRoot = dc.GetTestRoot(); + String datestamp = dc.GetSystemTime(); + if (testRoot == null || datestamp == null) + { + Log.e("SUTAgentAndroid", "Unable to get testRoot or datestamp in log!"); + return; + } + + + try + { + SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss:SSS"); + Date dateStr = sdf.parse(datestamp); + SimpleDateFormat sdf_file = new SimpleDateFormat("yyyy-MM-dd"); + fileDateStr = sdf_file.format(dateStr); + } + catch (ParseException pe) {} + String logFile = testRoot + "/" + fileDateStr + "-sutcommands.txt"; + PrintWriter pw = null; + try + { + pw = new PrintWriter(new FileWriter(logFile, true)); + pw.println(datestamp + " : " + message); + } + catch (IOException ioe) + { + Log.e("SUTAgentAndroid", "exception with file writer on: " + logFile); + } + finally + { + if (pw != null) + { + pw.close(); + } + } + + } +} diff --git a/build/mobile/sutagent/android/WifiConfiguration.java b/build/mobile/sutagent/android/WifiConfiguration.java new file mode 100644 index 000000000..f923d2cb2 --- /dev/null +++ b/build/mobile/sutagent/android/WifiConfiguration.java @@ -0,0 +1,480 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net.wifi; + +import android.os.Parcelable; +import android.os.Parcel; + +import java.util.BitSet; + +/** + * A class representing a configured Wi-Fi network, including the + * security configuration. Android will not necessarily support + * all of these security schemes initially. + */ +public class WifiConfiguration implements Parcelable { + + /** {@hide} */ + public static final String ssidVarName = "ssid"; + /** {@hide} */ + public static final String bssidVarName = "bssid"; + /** {@hide} */ + public static final String pskVarName = "psk"; + /** {@hide} */ + public static final String[] wepKeyVarNames = { "wep_key0", "wep_key1", "wep_key2", "wep_key3" }; + /** {@hide} */ + public static final String wepTxKeyIdxVarName = "wep_tx_keyidx"; + /** {@hide} */ + public static final String priorityVarName = "priority"; + /** {@hide} */ + public static final String hiddenSSIDVarName = "scan_ssid"; + + public class EnterpriseField { + private String varName; + private String value; + + private EnterpriseField(String varName) { + this.varName = varName; + this.value = null; + } + + public void setValue(String value) { + this.value = value; + } + + public String varName() { + return varName; + } + + public String value() { + return value; + } + } + + public EnterpriseField eap = new EnterpriseField("eap"); + public EnterpriseField phase2 = new EnterpriseField("phase2"); + public EnterpriseField identity = new EnterpriseField("identity"); + public EnterpriseField anonymous_identity = new EnterpriseField("anonymous_identity"); + public EnterpriseField password = new EnterpriseField("password"); + public EnterpriseField client_cert = new EnterpriseField("client_cert"); + public EnterpriseField private_key = new EnterpriseField("private_key"); + public EnterpriseField ca_cert = new EnterpriseField("ca_cert"); + + public EnterpriseField[] enterpriseFields = { + eap, phase2, identity, anonymous_identity, password, client_cert, + private_key, ca_cert }; + + /** + * Recognized key management schemes. + */ + public static class KeyMgmt { + private KeyMgmt() { } + + /** WPA is not used; plaintext or static WEP could be used. */ + public static final int NONE = 0; + /** WPA pre-shared key (requires {@code preSharedKey} to be specified). */ + public static final int WPA_PSK = 1; + /** WPA using EAP authentication. Generally used with an external authentication server. */ + public static final int WPA_EAP = 2; + /** IEEE 802.1X using EAP authentication and (optionally) dynamically + * generated WEP keys. */ + public static final int IEEE8021X = 3; + + public static final String varName = "key_mgmt"; + + public static final String[] strings = { "NONE", "WPA_PSK", "WPA_EAP", "IEEE8021X" }; + } + + /** + * Recognized security protocols. + */ + public static class Protocol { + private Protocol() { } + + /** WPA/IEEE 802.11i/D3.0 */ + public static final int WPA = 0; + /** WPA2/IEEE 802.11i */ + public static final int RSN = 1; + + public static final String varName = "proto"; + + public static final String[] strings = { "WPA", "RSN" }; + } + + /** + * Recognized IEEE 802.11 authentication algorithms. + */ + public static class AuthAlgorithm { + private AuthAlgorithm() { } + + /** Open System authentication (required for WPA/WPA2) */ + public static final int OPEN = 0; + /** Shared Key authentication (requires static WEP keys) */ + public static final int SHARED = 1; + /** LEAP/Network EAP (only used with LEAP) */ + public static final int LEAP = 2; + + public static final String varName = "auth_alg"; + + public static final String[] strings = { "OPEN", "SHARED", "LEAP" }; + } + + /** + * Recognized pairwise ciphers for WPA. + */ + public static class PairwiseCipher { + private PairwiseCipher() { } + + /** Use only Group keys (deprecated) */ + public static final int NONE = 0; + /** Temporal Key Integrity Protocol [IEEE 802.11i/D7.0] */ + public static final int TKIP = 1; + /** AES in Counter mode with CBC-MAC [RFC 3610, IEEE 802.11i/D7.0] */ + public static final int CCMP = 2; + + public static final String varName = "pairwise"; + + public static final String[] strings = { "NONE", "TKIP", "CCMP" }; + } + + /** + * Recognized group ciphers. + * <pre> + * CCMP = AES in Counter mode with CBC-MAC [RFC 3610, IEEE 802.11i/D7.0] + * TKIP = Temporal Key Integrity Protocol [IEEE 802.11i/D7.0] + * WEP104 = WEP (Wired Equivalent Privacy) with 104-bit key + * WEP40 = WEP (Wired Equivalent Privacy) with 40-bit key (original 802.11) + * </pre> + */ + public static class GroupCipher { + private GroupCipher() { } + + /** WEP40 = WEP (Wired Equivalent Privacy) with 40-bit key (original 802.11) */ + public static final int WEP40 = 0; + /** WEP104 = WEP (Wired Equivalent Privacy) with 104-bit key */ + public static final int WEP104 = 1; + /** Temporal Key Integrity Protocol [IEEE 802.11i/D7.0] */ + public static final int TKIP = 2; + /** AES in Counter mode with CBC-MAC [RFC 3610, IEEE 802.11i/D7.0] */ + public static final int CCMP = 3; + + public static final String varName = "group"; + + public static final String[] strings = { "WEP40", "WEP104", "TKIP", "CCMP" }; + } + + /** Possible status of a network configuration. */ + public static class Status { + private Status() { } + + /** this is the network we are currently connected to */ + public static final int CURRENT = 0; + /** supplicant will not attempt to use this network */ + public static final int DISABLED = 1; + /** supplicant will consider this network available for association */ + public static final int ENABLED = 2; + + public static final String[] strings = { "current", "disabled", "enabled" }; + } + + /** + * The ID number that the supplicant uses to identify this + * network configuration entry. This must be passed as an argument + * to most calls into the supplicant. + */ + public int networkId; + + /** + * The current status of this network configuration entry. + * @see Status + */ + public int status; + /** + * The network's SSID. Can either be an ASCII string, + * which must be enclosed in double quotation marks + * (e.g., {@code "MyNetwork"}, or a string of + * hex digits,which are not enclosed in quotes + * (e.g., {@code 01a243f405}). + */ + public String SSID; + /** + * When set, this network configuration entry should only be used when + * associating with the AP having the specified BSSID. The value is + * a string in the format of an Ethernet MAC address, e.g., + * <code>XX:XX:XX:XX:XX:XX</code> where each <code>X</code> is a hex digit. + */ + public String BSSID; + + /** + * Pre-shared key for use with WPA-PSK. + * <p/> + * When the value of this key is read, the actual key is + * not returned, just a "*" if the key has a value, or the null + * string otherwise. + */ + public String preSharedKey; + /** + * Up to four WEP keys. Either an ASCII string enclosed in double + * quotation marks (e.g., {@code "abcdef"} or a string + * of hex digits (e.g., {@code 0102030405}). + * <p/> + * When the value of one of these keys is read, the actual key is + * not returned, just a "*" if the key has a value, or the null + * string otherwise. + */ + public String[] wepKeys; + + /** Default WEP key index, ranging from 0 to 3. */ + public int wepTxKeyIndex; + + /** + * Priority determines the preference given to a network by {@code wpa_supplicant} + * when choosing an access point with which to associate. + */ + public int priority; + + /** + * This is a network that does not broadcast its SSID, so an + * SSID-specific probe request must be used for scans. + */ + public boolean hiddenSSID; + + /** + * The set of key management protocols supported by this configuration. + * See {@link KeyMgmt} for descriptions of the values. + * Defaults to WPA-PSK WPA-EAP. + */ + public BitSet allowedKeyManagement; + /** + * The set of security protocols supported by this configuration. + * See {@link Protocol} for descriptions of the values. + * Defaults to WPA RSN. + */ + public BitSet allowedProtocols; + /** + * The set of authentication protocols supported by this configuration. + * See {@link AuthAlgorithm} for descriptions of the values. + * Defaults to automatic selection. + */ + public BitSet allowedAuthAlgorithms; + /** + * The set of pairwise ciphers for WPA supported by this configuration. + * See {@link PairwiseCipher} for descriptions of the values. + * Defaults to CCMP TKIP. + */ + public BitSet allowedPairwiseCiphers; + /** + * The set of group ciphers supported by this configuration. + * See {@link GroupCipher} for descriptions of the values. + * Defaults to CCMP TKIP WEP104 WEP40. + */ + public BitSet allowedGroupCiphers; + + + public WifiConfiguration() { + networkId = -1; + SSID = null; + BSSID = null; + priority = 0; + hiddenSSID = false; + allowedKeyManagement = new BitSet(); + allowedProtocols = new BitSet(); + allowedAuthAlgorithms = new BitSet(); + allowedPairwiseCiphers = new BitSet(); + allowedGroupCiphers = new BitSet(); + wepKeys = new String[4]; + for (int i = 0; i < wepKeys.length; i++) + wepKeys[i] = null; + for (EnterpriseField field : enterpriseFields) { + field.setValue(null); + } + } + + public String toString() { + StringBuffer sbuf = new StringBuffer(); + if (this.status == WifiConfiguration.Status.CURRENT) { + sbuf.append("* "); + } else if (this.status == WifiConfiguration.Status.DISABLED) { + sbuf.append("- "); + } + sbuf.append("ID: ").append(this.networkId).append(" SSID: ").append(this.SSID). + append(" BSSID: ").append(this.BSSID).append(" PRIO: ").append(this.priority). + append('\n'); + sbuf.append(" KeyMgmt:"); + for (int k = 0; k < this.allowedKeyManagement.size(); k++) { + if (this.allowedKeyManagement.get(k)) { + sbuf.append(" "); + if (k < KeyMgmt.strings.length) { + sbuf.append(KeyMgmt.strings[k]); + } else { + sbuf.append("??"); + } + } + } + sbuf.append(" Protocols:"); + for (int p = 0; p < this.allowedProtocols.size(); p++) { + if (this.allowedProtocols.get(p)) { + sbuf.append(" "); + if (p < Protocol.strings.length) { + sbuf.append(Protocol.strings[p]); + } else { + sbuf.append("??"); + } + } + } + sbuf.append('\n'); + sbuf.append(" AuthAlgorithms:"); + for (int a = 0; a < this.allowedAuthAlgorithms.size(); a++) { + if (this.allowedAuthAlgorithms.get(a)) { + sbuf.append(" "); + if (a < AuthAlgorithm.strings.length) { + sbuf.append(AuthAlgorithm.strings[a]); + } else { + sbuf.append("??"); + } + } + } + sbuf.append('\n'); + sbuf.append(" PairwiseCiphers:"); + for (int pc = 0; pc < this.allowedPairwiseCiphers.size(); pc++) { + if (this.allowedPairwiseCiphers.get(pc)) { + sbuf.append(" "); + if (pc < PairwiseCipher.strings.length) { + sbuf.append(PairwiseCipher.strings[pc]); + } else { + sbuf.append("??"); + } + } + } + sbuf.append('\n'); + sbuf.append(" GroupCiphers:"); + for (int gc = 0; gc < this.allowedGroupCiphers.size(); gc++) { + if (this.allowedGroupCiphers.get(gc)) { + sbuf.append(" "); + if (gc < GroupCipher.strings.length) { + sbuf.append(GroupCipher.strings[gc]); + } else { + sbuf.append("??"); + } + } + } + sbuf.append('\n').append(" PSK: "); + if (this.preSharedKey != null) { + sbuf.append('*'); + } + + for (EnterpriseField field : enterpriseFields) { + sbuf.append('\n').append(" " + field.varName() + ": "); + String value = field.value(); + if (value != null) sbuf.append(value); + } + sbuf.append('\n'); + return sbuf.toString(); + } + + /** + * Construct a WifiConfiguration from a scanned network + * @param scannedAP the scan result used to construct the config entry + * TODO: figure out whether this is a useful way to construct a new entry. + * + public WifiConfiguration(ScanResult scannedAP) { + networkId = -1; + SSID = scannedAP.SSID; + BSSID = scannedAP.BSSID; + } + */ + + private static BitSet readBitSet(Parcel src) { + int cardinality = src.readInt(); + + BitSet set = new BitSet(); + for (int i = 0; i < cardinality; i++) + set.set(src.readInt()); + + return set; + } + + private static void writeBitSet(Parcel dest, BitSet set) { + int nextSetBit = -1; + + dest.writeInt(set.cardinality()); + + while ((nextSetBit = set.nextSetBit(nextSetBit + 1)) != -1) + dest.writeInt(nextSetBit); + } + + /** Implement the Parcelable interface {@hide} */ + public int describeContents() { + return 0; + } + + /** Implement the Parcelable interface {@hide} */ + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(networkId); + dest.writeInt(status); + dest.writeString(SSID); + dest.writeString(BSSID); + dest.writeString(preSharedKey); + for (String wepKey : wepKeys) + dest.writeString(wepKey); + dest.writeInt(wepTxKeyIndex); + dest.writeInt(priority); + dest.writeInt(hiddenSSID ? 1 : 0); + + writeBitSet(dest, allowedKeyManagement); + writeBitSet(dest, allowedProtocols); + writeBitSet(dest, allowedAuthAlgorithms); + writeBitSet(dest, allowedPairwiseCiphers); + writeBitSet(dest, allowedGroupCiphers); + + for (EnterpriseField field : enterpriseFields) { + dest.writeString(field.value()); + } + } + + /** Implement the Parcelable interface {@hide} */ + public static final Creator<WifiConfiguration> CREATOR = + new Creator<WifiConfiguration>() { + public WifiConfiguration createFromParcel(Parcel in) { + WifiConfiguration config = new WifiConfiguration(); + config.networkId = in.readInt(); + config.status = in.readInt(); + config.SSID = in.readString(); + config.BSSID = in.readString(); + config.preSharedKey = in.readString(); + for (int i = 0; i < config.wepKeys.length; i++) + config.wepKeys[i] = in.readString(); + config.wepTxKeyIndex = in.readInt(); + config.priority = in.readInt(); + config.hiddenSSID = in.readInt() != 0; + config.allowedKeyManagement = readBitSet(in); + config.allowedProtocols = readBitSet(in); + config.allowedAuthAlgorithms = readBitSet(in); + config.allowedPairwiseCiphers = readBitSet(in); + config.allowedGroupCiphers = readBitSet(in); + + for (EnterpriseField field : config.enterpriseFields) { + field.setValue(in.readString()); + } + return config; + } + + public WifiConfiguration[] newArray(int size) { + return new WifiConfiguration[size]; + } + }; +} diff --git a/build/mobile/sutagent/android/default.properties b/build/mobile/sutagent/android/default.properties new file mode 100644 index 000000000..a2d729ba6 --- /dev/null +++ b/build/mobile/sutagent/android/default.properties @@ -0,0 +1,17 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +# This file is automatically generated by Android Tools. +# Do not modify this file -- YOUR CHANGES WILL BE ERASED! +# +# This file must be checked in Version Control Systems. +# +# To customize properties used by the Ant build system use, +# "build.properties", and override values to adapt the script to your +# project structure. + +# Indicates whether an apk should be generated for each density. +split.density=false +# Project target. +target=android-5 diff --git a/build/mobile/sutagent/android/fencp/AndroidManifest.xml b/build/mobile/sutagent/android/fencp/AndroidManifest.xml new file mode 100644 index 000000000..65bb6ca9d --- /dev/null +++ b/build/mobile/sutagent/android/fencp/AndroidManifest.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- This Source Code Form is subject to the terms of the Mozilla Public + - License, v. 2.0. If a copy of the MPL was not distributed with this + - file, You can obtain one at http://mozilla.org/MPL/2.0/. --> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="org.mozilla.fencp" + android:versionCode="1" + android:versionName="1.0" android:sharedUserId="org.mozilla.fennec.sharedID"> + <application android:icon="@drawable/icon" android:label="@string/app_name" android:debuggable="true"> + <activity android:label="@string/app_name" android:name="FenCP"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> + </activity> + + <provider android:name="FenCPFP" + android:enabled="true" + android:authorities="org.mozilla.fencp" + android:exported="true"> + </provider> +</application> + <uses-sdk android:minSdkVersion="6" /> + +</manifest>
\ No newline at end of file diff --git a/build/mobile/sutagent/android/fencp/DirCursor.java b/build/mobile/sutagent/android/fencp/DirCursor.java new file mode 100644 index 000000000..b69f8d9dc --- /dev/null +++ b/build/mobile/sutagent/android/fencp/DirCursor.java @@ -0,0 +1,178 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +package org.mozilla.fencp; + +import java.io.File; +import java.io.IOException; + +import android.database.MatrixCursor; + +public class DirCursor extends MatrixCursor { + public static final String _ID = "_id"; + public static final String ISDIR = "isdir"; + public static final String FILENAME = "filename"; + public static final String LENGTH = "length"; + public static final String TIMESTAMP = "ts"; + public static final String WRITABLE = "writable"; + static final String[] DEFCOLUMNS = new String[] { + _ID, + ISDIR, + FILENAME, + LENGTH, + TIMESTAMP, + WRITABLE + }; + private String dirPath = null; + private String [] theColumns = null; + + public DirCursor(String[] columnNames, String sPath) { + super((columnNames == null ? DEFCOLUMNS : columnNames)); + theColumns = (columnNames == null ? DEFCOLUMNS : columnNames); + dirPath = sPath; + doLoadCursor(dirPath); + } + + public DirCursor(String[] columnNames, int initialCapacity, String sPath) { + super((columnNames == null ? DEFCOLUMNS : columnNames), initialCapacity); + theColumns = (columnNames == null ? DEFCOLUMNS : columnNames); + dirPath = sPath; + doLoadCursor(dirPath); + } + + private void doLoadCursor(String sDir) { + File dir = new File(sDir); + int nFiles = 0; + int nCols = theColumns.length; + int lcvFiles = 0; + int nCIndex = 0; + Object [] vals = new Object[nCols]; + + if (vals == null) + return; + + if (dir.isDirectory()) { + try { + nCIndex = getColumnIndex(_ID); + if (nCIndex > -1) + vals[nCIndex] = -1; + + nCIndex = getColumnIndex(ISDIR); + if (nCIndex > -1) + vals[nCIndex] = 1; + + nCIndex = getColumnIndex(FILENAME); + if (nCIndex > -1) + try { + vals[nCIndex] = dir.getCanonicalPath(); + } catch (IOException e) { + vals[nCIndex] = dir.getName(); + } + + nCIndex = getColumnIndex(LENGTH); + if (nCIndex > -1) + vals[nCIndex] = 0; + + nCIndex = getColumnIndex(TIMESTAMP); + if (nCIndex > -1) + vals[nCIndex] = 0; + + nCIndex = getColumnIndex(WRITABLE); + if (nCIndex > -1) + vals[nCIndex] = (dir.canWrite() ? 1 : 0); + + addRow(vals); + } + catch (IllegalArgumentException iae) { + iae.printStackTrace(); + } + + File [] files = dir.listFiles(); + if (files != null) { + if ((nFiles = files.length) > 0) { + for (lcvFiles = 0; lcvFiles < nFiles; lcvFiles++) { + nCIndex = getColumnIndex(_ID); + if (nCIndex > -1) + vals[nCIndex] = lcvFiles; + + nCIndex = getColumnIndex(ISDIR); + if (nCIndex > -1) + vals[nCIndex] = (files[lcvFiles].isDirectory() ? 1 : 0); + + nCIndex = getColumnIndex(FILENAME); + if (nCIndex > -1) + vals[nCIndex] = files[lcvFiles].getName(); + + nCIndex = getColumnIndex(LENGTH); + if (nCIndex > -1) + vals[nCIndex] = (files[lcvFiles].isDirectory() ? -1 : files[lcvFiles].length()); + + try { + addRow(vals); + } catch (IllegalArgumentException iae) { + iae.printStackTrace(); + } + } + } + } + } else { + if (dir.isFile()) { + nCIndex = getColumnIndex(_ID); + if (nCIndex > -1) + vals[nCIndex] = -1; + + nCIndex = getColumnIndex(ISDIR); + if (nCIndex > -1) + vals[nCIndex] = 0; + + nCIndex = getColumnIndex(FILENAME); + if (nCIndex > -1) + vals[nCIndex] = dir.getName(); + + nCIndex = getColumnIndex(LENGTH); + if (nCIndex > -1) + vals[nCIndex] = dir.length(); + + nCIndex = getColumnIndex(TIMESTAMP); + if (nCIndex > -1) { + vals[nCIndex] = dir.lastModified(); + } + + try { + addRow(vals); + } + catch (IllegalArgumentException iae) { + iae.printStackTrace(); + } + } + else { + try { + nCIndex = getColumnIndex(_ID); + if (nCIndex > -1) + vals[nCIndex] = -1; + + nCIndex = getColumnIndex(ISDIR); + if (nCIndex > -1) + vals[nCIndex] = 0; + + nCIndex = getColumnIndex(FILENAME); + if (nCIndex > -1) + vals[nCIndex] = null; + + nCIndex = getColumnIndex(LENGTH); + if (nCIndex > -1) + vals[nCIndex] = 0; + + nCIndex = getColumnIndex(TIMESTAMP); + if (nCIndex > -1) + vals[nCIndex] = 0; + + addRow(vals); + } + catch (IllegalArgumentException iae) { + iae.printStackTrace(); + } + } + } + } +} diff --git a/build/mobile/sutagent/android/fencp/FenCP.java b/build/mobile/sutagent/android/fencp/FenCP.java new file mode 100644 index 000000000..1910698de --- /dev/null +++ b/build/mobile/sutagent/android/fencp/FenCP.java @@ -0,0 +1,16 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +package org.mozilla.fencp; + +import android.app.Activity; +import android.os.Bundle; + +public class FenCP extends Activity { + /** Called when the activity is first created. */ + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.main); + } +}
\ No newline at end of file diff --git a/build/mobile/sutagent/android/fencp/FenCPFP.java b/build/mobile/sutagent/android/fencp/FenCPFP.java new file mode 100644 index 000000000..a5bea9a0c --- /dev/null +++ b/build/mobile/sutagent/android/fencp/FenCPFP.java @@ -0,0 +1,193 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +package org.mozilla.fencp; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; + +import android.content.ContentProvider; +import android.content.ContentValues; +import android.content.UriMatcher; +import android.database.Cursor; +import android.net.Uri; + +public class FenCPFP extends ContentProvider { + public static final String PROVIDER_NAME = "org.mozilla.fencp"; + public static final Uri CONTENT_URI = Uri.parse("content://" + PROVIDER_NAME + "/file"); + + public static final String _ID = "_id"; + public static final String ISDIR = "isdir"; + public static final String FILENAME = "filename"; + public static final String LENGTH = "length"; + public static final String CHUNK = "chunk"; + static String[] dircolumns = new String[] { + _ID, + ISDIR, + FILENAME, + LENGTH + }; + + static String[] filecolumns = new String[] { + _ID, + CHUNK + }; + + private static final int DIR = 1; + private static final int FILE_NAME = 2; + + private static final UriMatcher uriMatcher; + static { + uriMatcher = new UriMatcher(UriMatcher.NO_MATCH); + uriMatcher.addURI(PROVIDER_NAME, "dir", DIR); + uriMatcher.addURI(PROVIDER_NAME, "file", FILE_NAME); + } + + public int PruneDir(String sTmpDir) { + int nRet = 0; + int nFiles = 0; + String sSubDir = null; + + File dir = new File(sTmpDir); + + if (dir.isDirectory()) { + File [] files = dir.listFiles(); + if (files != null) { + if ((nFiles = files.length) > 0) { + for (int lcv = 0; lcv < nFiles; lcv++) { + if (files[lcv].isDirectory()) { + sSubDir = files[lcv].getAbsolutePath(); + nRet += PruneDir(sSubDir); + } + else { + if (files[lcv].delete()) { + nRet++; + } + } + } + } + } + if (dir.delete()) { + nRet++; + } + if ((nFiles + 1) > nRet) { + nRet = -1; + } + } + + return(nRet); + } + + @Override + public int delete(Uri uri, String selection, String[] selectionArgs) { + int nFiles = 0; + switch (uriMatcher.match(uri)) { + case FILE_NAME: + File f = new File(selection); + if (f.delete()) + nFiles = 1; + break; + + case DIR: + nFiles = PruneDir(selection); + break; + + default: + break; + } + return nFiles; + } + + @Override + public String getType(Uri uri) + { + switch (uriMatcher.match(uri)) + { + //---get directory--- + case DIR: + return "vnd.android.cursor.dir/vnd.mozilla.dir "; + //---get a particular file--- + case FILE_NAME: + return "vnd.android.cursor.item/vnd.mozilla.file "; + //---Unknown--- + default: + throw new IllegalArgumentException("Unsupported URI: " + uri); + } + } + + @Override + public Uri insert(Uri uri, ContentValues values) { + return null; + } + + @Override + public boolean onCreate() { + return true; + } + + @Override + public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { + Cursor retCursor = null; + + switch(uriMatcher.match(uri)) { + case DIR: + retCursor = new DirCursor(projection, selection); + break; + + case FILE_NAME: + retCursor = new FileCursor(projection, selection, selectionArgs); + break; + + default: + break; + } + return (retCursor); + } + + @Override + public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { + int nRet = 0; + FileOutputStream dstFile = null; + + switch(uriMatcher.match(uri)) { + case DIR: + File dir = new File(selection); + if (dir.mkdirs()) + nRet = 1; + break; + + case FILE_NAME: + try { + long lOffset = values.getAsLong("offset"); + byte [] buf = values.getAsByteArray(CHUNK); + int nLength = values.getAsInteger(LENGTH); + if ((buf != null) && (nLength > 0)) { + File f = new File(selection); + dstFile = new FileOutputStream(f, (lOffset == 0 ? false : true)); + dstFile.write(buf,0, nLength); + dstFile.flush(); + dstFile.close(); + nRet = nLength; + } + } catch (FileNotFoundException fnfe) { + fnfe.printStackTrace(); + } catch (IOException ioe) { + try { + dstFile.flush(); + } catch (IOException e) { + } + try { + dstFile.close(); + } catch (IOException e) { + } + } + break; + + default: + break; + } + return nRet; + } +} diff --git a/build/mobile/sutagent/android/fencp/FileCursor.java b/build/mobile/sutagent/android/fencp/FileCursor.java new file mode 100644 index 000000000..d04870c92 --- /dev/null +++ b/build/mobile/sutagent/android/fencp/FileCursor.java @@ -0,0 +1,169 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +package org.mozilla.fencp; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import android.database.AbstractWindowedCursor; +import android.database.CursorWindow; + +public class FileCursor extends AbstractWindowedCursor { + public static final String _ID = "_id"; + public static final String CHUNK = "chunk"; + public static final String LENGTH = "length"; + static final String[] DEFCOLUMNS = new String[] { + _ID, + CHUNK, + LENGTH + }; + private String filePath = null; + private String [] theColumns = null; + + private static final int BUFSIZE = 4096; + private long lFileSize = 0; + private int nCount = 0; + private File theFile = null; + private byte [] theBuffer = null; + private long lOffset = 0; + private long lLength = -1; + + public FileCursor(String[] columnNames, String sFilePath, String [] selectionArgs) { + super(); + theColumns = (columnNames == null ? DEFCOLUMNS : columnNames); + filePath = sFilePath; + nCount = -1; + + if ((selectionArgs != null) && (selectionArgs.length > 0)) { + lOffset = Long.parseLong(selectionArgs[0]); + lLength = Long.parseLong(selectionArgs[1]); + } + + if (filePath.length() > 0) { + theFile = new File(filePath); + if (theFile.exists() && theFile.canRead()) { + lFileSize = theFile.length(); + + // lLength == -1 return everything between lOffset and eof + // lLength == 0 return file length + // lLength > 0 return lLength bytes + if (lLength == -1) { + lFileSize = lFileSize - lOffset; + } else if (lLength == 0) { + // just return the file length + } else { + lFileSize = ((lLength <= (lFileSize - lOffset)) ? lLength : (lFileSize - lOffset)); + } + + if (lLength != 0) { + nCount = (int) (lFileSize / BUFSIZE); + if ((lFileSize % BUFSIZE) > 0) + nCount++; + } else { + nCount = 1; + } + + mRowIdColumnIndex = 0; + } + } + } + + public String getColumnName (int columnIndex) { + return theColumns[columnIndex]; + } + + @Override + public String[] getColumnNames() { + return theColumns; + } + + @Override + public int getCount() { + return nCount; + } + + @Override + public boolean onMove(int oldPosition, int newPosition) { + boolean bRet = true; + + // get rid of old data + mWindow.clear(); + bRet = mWindow.setNumColumns(theColumns.length); + fillWindow(newPosition, mWindow); + + return bRet; + } + + @Override + public void fillWindow (int pos, CursorWindow window) { + int nNumRows = window.getNumRows(); + int nCIndex = 0; + window.setStartPosition(0); + + if (pos > -1) { + if (nNumRows == 0) { + window.allocRow(); + nNumRows = window.getNumRows(); + } + + if (nNumRows == 1) { + nCIndex = getColumnIndex(LENGTH); + if (nCIndex > -1) { + window.putLong(lFileSize, 0, nCIndex); + } + nCIndex = getColumnIndex(_ID); + if (nCIndex > -1) { + window.putLong((long)pos, 0, nCIndex); + } + nCIndex = getColumnIndex(CHUNK); + if (nCIndex > -1) { + if (lLength != 0) { + byte[] value = getABlob (pos, 1); + window.putBlob(value, 0, nCIndex); + } + } + } + window.setStartPosition(pos); + } + return; + } + + public byte[] getABlob (int row, int column) { + int nRead = 0; + int nOffset = 0; + int nBufSize = 0; + + if ((column == 1) && (theFile != null)) { + try { + FileInputStream fin = new FileInputStream(theFile); + nOffset = row * BUFSIZE; + if (row < (nCount - 1)) { + nBufSize = BUFSIZE; + } else { + nBufSize = (int) (lFileSize - nOffset); + } + theBuffer = new byte[nBufSize]; + + if (theBuffer != null) { + if (fin.skip(nOffset + lOffset) == (nOffset + lOffset)) { + if ((nRead = fin.read(theBuffer, 0, nBufSize)) != -1) { + if (nRead != nBufSize) { + return null; + } + } + } + } + + fin.close(); + } catch (FileNotFoundException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + return theBuffer; + } +} diff --git a/build/mobile/sutagent/android/fencp/Makefile.in b/build/mobile/sutagent/android/fencp/Makefile.in new file mode 100644 index 000000000..7b29ea0ba --- /dev/null +++ b/build/mobile/sutagent/android/fencp/Makefile.in @@ -0,0 +1,66 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +DEPTH = @DEPTH@ +topsrcdir = @top_srcdir@ +srcdir = @srcdir@ +VPATH = @srcdir@ + +include $(DEPTH)/config/autoconf.mk + +ANDROID_APK_NAME := FenCP + +JAVAFILES = \ + DirCursor.java \ + FenCP.java \ + FenCPFP.java \ + FileCursor.java \ + R.java \ + $(NULL) + +RES_FILES = \ + res/drawable-hdpi/icon.png \ + res/drawable-ldpi/icon.png \ + res/drawable-mdpi/icon.png \ + res/layout/main.xml \ + res/values/strings.xml \ + $(NULL) + +GARBAGE += \ + AndroidManifest.xml \ + classes.dex \ + $(ANDROID_APK_NAME).ap_ \ + $(ANDROID_APK_NAME)-unsigned-unaligned.apk \ + $(ANDROID_APK_NAME)-unaligned.apk \ + $(ANDROID_APK_NAME).apk \ + $(NULL) + +GARBAGE_DIRS += network-libs + +JAVA_CLASSPATH = $(ANDROID_SDK)/android.jar + +include $(topsrcdir)/config/rules.mk + +# include Android specific java flags - using these instead of what's in rules.mk +include $(topsrcdir)/config/android-common.mk + +tools:: $(ANDROID_APK_NAME).apk + +classes.dex: $(JAVAFILES) + $(JAVAC) $(JAVAC_FLAGS) -d classes $(addprefix $(srcdir)/,$(JAVAFILES)) + $(DX) --dex --output=$@ classes + +$(ANDROID_APK_NAME).ap_: AndroidManifest.xml + $(AAPT) package -f -M $< -I $(ANDROID_SDK)/android.jar -S res -F $@ + +$(ANDROID_APK_NAME)-unsigned-unaligned.apk: $(ANDROID_APK_NAME).ap_ classes.dex + cp $< $@ + $(ZIP) -0 $@ classes.dex + +$(ANDROID_APK_NAME)-unaligned.apk: $(ANDROID_APK_NAME)-unsigned-unaligned.apk + cp $< $@ + $(DEBUG_JARSIGNER) $@ + +$(ANDROID_APK_NAME).apk: $(ANDROID_APK_NAME)-unaligned.apk + $(ZIPALIGN) -f -v 4 $< $@ diff --git a/build/mobile/sutagent/android/fencp/R.java b/build/mobile/sutagent/android/fencp/R.java new file mode 100644 index 000000000..62f638607 --- /dev/null +++ b/build/mobile/sutagent/android/fencp/R.java @@ -0,0 +1,27 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* AUTO-GENERATED FILE. DO NOT MODIFY. + * + * This class was automatically generated by the + * aapt tool from the resource data it found. It + * should not be modified by hand. + */ + +package org.mozilla.fencp; + +public final class R { + public static final class attr { + } + public static final class drawable { + public static final int icon=0x7f020000; + } + public static final class layout { + public static final int main=0x7f030000; + } + public static final class string { + public static final int app_name=0x7f040001; + public static final int hello=0x7f040000; + } +} diff --git a/build/mobile/sutagent/android/fencp/default.properties b/build/mobile/sutagent/android/fencp/default.properties new file mode 100644 index 000000000..06596fbb7 --- /dev/null +++ b/build/mobile/sutagent/android/fencp/default.properties @@ -0,0 +1,15 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +# This file is automatically generated by Android Tools. +# Do not modify this file -- YOUR CHANGES WILL BE ERASED! +# +# This file must be checked in Version Control Systems. +# +# To customize properties used by the Ant build system use, +# "build.properties", and override values to adapt the script to your +# project structure. + +# Project target. +target=android-6 diff --git a/build/mobile/sutagent/android/fencp/moz.build b/build/mobile/sutagent/android/fencp/moz.build new file mode 100644 index 000000000..bba1d688b --- /dev/null +++ b/build/mobile/sutagent/android/fencp/moz.build @@ -0,0 +1,8 @@ +# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +MODULE = 'FenCP' + diff --git a/build/mobile/sutagent/android/fencp/res/drawable-hdpi/icon.png b/build/mobile/sutagent/android/fencp/res/drawable-hdpi/icon.png Binary files differnew file mode 100644 index 000000000..8074c4c57 --- /dev/null +++ b/build/mobile/sutagent/android/fencp/res/drawable-hdpi/icon.png diff --git a/build/mobile/sutagent/android/fencp/res/drawable-ldpi/icon.png b/build/mobile/sutagent/android/fencp/res/drawable-ldpi/icon.png Binary files differnew file mode 100644 index 000000000..1095584ec --- /dev/null +++ b/build/mobile/sutagent/android/fencp/res/drawable-ldpi/icon.png diff --git a/build/mobile/sutagent/android/fencp/res/drawable-mdpi/icon.png b/build/mobile/sutagent/android/fencp/res/drawable-mdpi/icon.png Binary files differnew file mode 100644 index 000000000..a07c69fa5 --- /dev/null +++ b/build/mobile/sutagent/android/fencp/res/drawable-mdpi/icon.png diff --git a/build/mobile/sutagent/android/fencp/res/layout/main.xml b/build/mobile/sutagent/android/fencp/res/layout/main.xml new file mode 100644 index 000000000..1256b1a7c --- /dev/null +++ b/build/mobile/sutagent/android/fencp/res/layout/main.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- This Source Code Form is subject to the terms of the Mozilla Public + - License, v. 2.0. If a copy of the MPL was not distributed with this + - file, You can obtain one at http://mozilla.org/MPL/2.0/. --> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="vertical" + android:layout_width="fill_parent" + android:layout_height="fill_parent" + > +<TextView + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:text="@string/hello" + /> +</LinearLayout> diff --git a/build/mobile/sutagent/android/fencp/res/values/strings.xml b/build/mobile/sutagent/android/fencp/res/values/strings.xml new file mode 100644 index 000000000..1daeb1a24 --- /dev/null +++ b/build/mobile/sutagent/android/fencp/res/values/strings.xml @@ -0,0 +1,9 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- This Source Code Form is subject to the terms of the Mozilla Public + - License, v. 2.0. If a copy of the MPL was not distributed with this + - file, You can obtain one at http://mozilla.org/MPL/2.0/. --> + +<resources> + <string name="hello">Hello World, FennecCP!</string> + <string name="app_name">FennecCP</string> +</resources> diff --git a/build/mobile/sutagent/android/ffxcp/AndroidManifest.xml b/build/mobile/sutagent/android/ffxcp/AndroidManifest.xml new file mode 100644 index 000000000..f070f6672 --- /dev/null +++ b/build/mobile/sutagent/android/ffxcp/AndroidManifest.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- This Source Code Form is subject to the terms of the Mozilla Public + - License, v. 2.0. If a copy of the MPL was not distributed with this + - file, You can obtain one at http://mozilla.org/MPL/2.0/. --> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="org.mozilla.ffxcp" + android:versionCode="1" + android:versionName="1.0" android:sharedUserId="org.mozilla.firefox.sharedID"> + <application android:icon="@drawable/icon" android:label="@string/app_name" android:debuggable="true"> + <activity android:label="@string/app_name" android:name="ffxcp"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> + </activity> + + <provider android:name="FfxCPFP" + android:enabled="true" + android:authorities="org.mozilla.ffxcp" + android:exported="true"> + </provider> + </application> + <uses-sdk android:minSdkVersion="6" /> + +</manifest>
\ No newline at end of file diff --git a/build/mobile/sutagent/android/ffxcp/DirCursor.java b/build/mobile/sutagent/android/ffxcp/DirCursor.java new file mode 100644 index 000000000..076edbcda --- /dev/null +++ b/build/mobile/sutagent/android/ffxcp/DirCursor.java @@ -0,0 +1,178 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +package org.mozilla.ffxcp; + +import java.io.File; +import java.io.IOException; + +import android.database.MatrixCursor; + +public class DirCursor extends MatrixCursor { + public static final String _ID = "_id"; + public static final String ISDIR = "isdir"; + public static final String FILENAME = "filename"; + public static final String LENGTH = "length"; + public static final String TIMESTAMP = "ts"; + public static final String WRITABLE = "writable"; + static final String[] DEFCOLUMNS = new String[] { + _ID, + ISDIR, + FILENAME, + LENGTH, + TIMESTAMP, + WRITABLE + }; + private String dirPath = null; + private String [] theColumns = null; + + public DirCursor(String[] columnNames, String sPath) { + super((columnNames == null ? DEFCOLUMNS : columnNames)); + theColumns = (columnNames == null ? DEFCOLUMNS : columnNames); + dirPath = sPath; + doLoadCursor(dirPath); + } + + public DirCursor(String[] columnNames, int initialCapacity, String sPath) { + super((columnNames == null ? DEFCOLUMNS : columnNames), initialCapacity); + theColumns = (columnNames == null ? DEFCOLUMNS : columnNames); + dirPath = sPath; + doLoadCursor(dirPath); + } + + private void doLoadCursor(String sDir) { + File dir = new File(sDir); + int nFiles = 0; + int nCols = theColumns.length; + int lcvFiles = 0; + int nCIndex = 0; + Object [] vals = new Object[nCols]; + + if (vals == null) + return; + + if (dir.isDirectory()) { + try { + nCIndex = getColumnIndex(_ID); + if (nCIndex > -1) + vals[nCIndex] = -1; + + nCIndex = getColumnIndex(ISDIR); + if (nCIndex > -1) + vals[nCIndex] = 1; + + nCIndex = getColumnIndex(FILENAME); + if (nCIndex > -1) + try { + vals[nCIndex] = dir.getCanonicalPath(); + } catch (IOException e) { + vals[nCIndex] = dir.getName(); + } + + nCIndex = getColumnIndex(LENGTH); + if (nCIndex > -1) + vals[nCIndex] = 0; + + nCIndex = getColumnIndex(TIMESTAMP); + if (nCIndex > -1) + vals[nCIndex] = 0; + + nCIndex = getColumnIndex(WRITABLE); + if (nCIndex > -1) + vals[nCIndex] = (dir.canWrite() ? 1 : 0); + + addRow(vals); + } + catch (IllegalArgumentException iae) { + iae.printStackTrace(); + } + + File [] files = dir.listFiles(); + if (files != null) { + if ((nFiles = files.length) > 0) { + for (lcvFiles = 0; lcvFiles < nFiles; lcvFiles++) { + nCIndex = getColumnIndex(_ID); + if (nCIndex > -1) + vals[nCIndex] = lcvFiles; + + nCIndex = getColumnIndex(ISDIR); + if (nCIndex > -1) + vals[nCIndex] = (files[lcvFiles].isDirectory() ? 1 : 0); + + nCIndex = getColumnIndex(FILENAME); + if (nCIndex > -1) + vals[nCIndex] = files[lcvFiles].getName(); + + nCIndex = getColumnIndex(LENGTH); + if (nCIndex > -1) + vals[nCIndex] = (files[lcvFiles].isDirectory() ? -1 : files[lcvFiles].length()); + + try { + addRow(vals); + } catch (IllegalArgumentException iae) { + iae.printStackTrace(); + } + } + } + } + } else { + if (dir.isFile()) { + nCIndex = getColumnIndex(_ID); + if (nCIndex > -1) + vals[nCIndex] = -1; + + nCIndex = getColumnIndex(ISDIR); + if (nCIndex > -1) + vals[nCIndex] = 0; + + nCIndex = getColumnIndex(FILENAME); + if (nCIndex > -1) + vals[nCIndex] = dir.getName(); + + nCIndex = getColumnIndex(LENGTH); + if (nCIndex > -1) + vals[nCIndex] = dir.length(); + + nCIndex = getColumnIndex(TIMESTAMP); + if (nCIndex > -1) { + vals[nCIndex] = dir.lastModified(); + } + + try { + addRow(vals); + } + catch (IllegalArgumentException iae) { + iae.printStackTrace(); + } + } + else { + try { + nCIndex = getColumnIndex(_ID); + if (nCIndex > -1) + vals[nCIndex] = -1; + + nCIndex = getColumnIndex(ISDIR); + if (nCIndex > -1) + vals[nCIndex] = 0; + + nCIndex = getColumnIndex(FILENAME); + if (nCIndex > -1) + vals[nCIndex] = null; + + nCIndex = getColumnIndex(LENGTH); + if (nCIndex > -1) + vals[nCIndex] = 0; + + nCIndex = getColumnIndex(TIMESTAMP); + if (nCIndex > -1) + vals[nCIndex] = 0; + + addRow(vals); + } + catch (IllegalArgumentException iae) { + iae.printStackTrace(); + } + } + } + } +} diff --git a/build/mobile/sutagent/android/ffxcp/FfxCPFP.java b/build/mobile/sutagent/android/ffxcp/FfxCPFP.java new file mode 100644 index 000000000..de5868950 --- /dev/null +++ b/build/mobile/sutagent/android/ffxcp/FfxCPFP.java @@ -0,0 +1,193 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +package org.mozilla.ffxcp; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; + +import android.content.ContentProvider; +import android.content.ContentValues; +import android.content.UriMatcher; +import android.database.Cursor; +import android.net.Uri; + +public class FfxCPFP extends ContentProvider { + public static final String PROVIDER_NAME = "org.mozilla.ffxcp"; + public static final Uri CONTENT_URI = Uri.parse("content://" + PROVIDER_NAME + "/file"); + + public static final String _ID = "_id"; + public static final String ISDIR = "isdir"; + public static final String FILENAME = "filename"; + public static final String LENGTH = "length"; + public static final String CHUNK = "chunk"; + static String[] dircolumns = new String[] { + _ID, + ISDIR, + FILENAME, + LENGTH + }; + + static String[] filecolumns = new String[] { + _ID, + CHUNK + }; + + private static final int DIR = 1; + private static final int FILE_NAME = 2; + + private static final UriMatcher uriMatcher; + static { + uriMatcher = new UriMatcher(UriMatcher.NO_MATCH); + uriMatcher.addURI(PROVIDER_NAME, "dir", DIR); + uriMatcher.addURI(PROVIDER_NAME, "file", FILE_NAME); + } + + public int PruneDir(String sTmpDir) { + int nRet = 0; + int nFiles = 0; + String sSubDir = null; + + File dir = new File(sTmpDir); + + if (dir.isDirectory()) { + File [] files = dir.listFiles(); + if (files != null) { + if ((nFiles = files.length) > 0) { + for (int lcv = 0; lcv < nFiles; lcv++) { + if (files[lcv].isDirectory()) { + sSubDir = files[lcv].getAbsolutePath(); + nRet += PruneDir(sSubDir); + } + else { + if (files[lcv].delete()) { + nRet++; + } + } + } + } + } + if (dir.delete()) { + nRet++; + } + if ((nFiles + 1) > nRet) { + nRet = -1; + } + } + + return(nRet); + } + + @Override + public int delete(Uri uri, String selection, String[] selectionArgs) { + int nFiles = 0; + switch (uriMatcher.match(uri)) { + case FILE_NAME: + File f = new File(selection); + if (f.delete()) + nFiles = 1; + break; + + case DIR: + nFiles = PruneDir(selection); + break; + + default: + break; + } + return nFiles; + } + + @Override + public String getType(Uri uri) + { + switch (uriMatcher.match(uri)) + { + //---get directory--- + case DIR: + return "vnd.android.cursor.dir/vnd.mozilla.dir "; + //---get a particular file--- + case FILE_NAME: + return "vnd.android.cursor.item/vnd.mozilla.file "; + //---Unknown--- + default: + throw new IllegalArgumentException("Unsupported URI: " + uri); + } + } + + @Override + public Uri insert(Uri uri, ContentValues values) { + return null; + } + + @Override + public boolean onCreate() { + return true; + } + + @Override + public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { + Cursor retCursor = null; + + switch(uriMatcher.match(uri)) { + case DIR: + retCursor = new DirCursor(projection, selection); + break; + + case FILE_NAME: + retCursor = new FileCursor(projection, selection, selectionArgs); + break; + + default: + break; + } + return (retCursor); + } + + @Override + public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { + int nRet = 0; + FileOutputStream dstFile = null; + + switch(uriMatcher.match(uri)) { + case DIR: + File dir = new File(selection); + if (dir.mkdirs()) + nRet = 1; + break; + + case FILE_NAME: + try { + long lOffset = values.getAsLong("offset"); + byte [] buf = values.getAsByteArray(CHUNK); + int nLength = values.getAsInteger(LENGTH); + if ((buf != null) && (nLength > 0)) { + File f = new File(selection); + dstFile = new FileOutputStream(f, (lOffset == 0 ? false : true)); + dstFile.write(buf,0, nLength); + dstFile.flush(); + dstFile.close(); + nRet = nLength; + } + } catch (FileNotFoundException fnfe) { + fnfe.printStackTrace(); + } catch (IOException ioe) { + try { + dstFile.flush(); + } catch (IOException e) { + } + try { + dstFile.close(); + } catch (IOException e) { + } + } + break; + + default: + break; + } + return nRet; + } +} diff --git a/build/mobile/sutagent/android/ffxcp/FileCursor.java b/build/mobile/sutagent/android/ffxcp/FileCursor.java new file mode 100644 index 000000000..af0e0eb44 --- /dev/null +++ b/build/mobile/sutagent/android/ffxcp/FileCursor.java @@ -0,0 +1,170 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +package org.mozilla.ffxcp; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; + +import android.database.AbstractWindowedCursor; +import android.database.CursorWindow; + +public class FileCursor extends AbstractWindowedCursor { + public static final String _ID = "_id"; + public static final String CHUNK = "chunk"; + public static final String LENGTH = "length"; + static final String[] DEFCOLUMNS = new String[] { + _ID, + CHUNK, + LENGTH + }; + private String filePath = null; + private String [] theColumns = null; + + private static final int BUFSIZE = 4096; + private long lFileSize = 0; + private int nCount = 0; + private File theFile = null; + private byte [] theBuffer = null; + private long lOffset = 0; + private long lLength = -1; + + public FileCursor(String[] columnNames, String sFilePath, String [] selectionArgs) { + super(); + theColumns = (columnNames == null ? DEFCOLUMNS : columnNames); + filePath = sFilePath; + nCount = -1; + + if ((selectionArgs != null) && (selectionArgs.length > 0)) { + lOffset = Long.parseLong(selectionArgs[0]); + lLength = Long.parseLong(selectionArgs[1]); + } + + if (filePath.length() > 0) { + theFile = new File(filePath); + if (theFile.exists() && theFile.canRead()) { + lFileSize = theFile.length(); + + // lLength == -1 return everything between lOffset and eof + // lLength == 0 return file length + // lLength > 0 return lLength bytes + if (lLength == -1) { + lFileSize = lFileSize - lOffset; + } else if (lLength == 0) { + // just return the file length + } else { + lFileSize = ((lLength <= (lFileSize - lOffset)) ? lLength : (lFileSize - lOffset)); + } + + if (lLength != 0) { + nCount = (int) (lFileSize / BUFSIZE); + if ((lFileSize % BUFSIZE) > 0) + nCount++; + } else { + nCount = 1; + } + + mRowIdColumnIndex = 0; + } + } + } + + public String getColumnName (int columnIndex) { + return theColumns[columnIndex]; + } + + @Override + public String[] getColumnNames() { + return theColumns; + } + + @Override + public int getCount() { + return nCount; + } + + @Override + public boolean onMove(int oldPosition, int newPosition) { + boolean bRet = true; + + // get rid of old data + mWindow.clear(); + bRet = mWindow.setNumColumns(theColumns.length); + fillWindow(newPosition, mWindow); + + return bRet; + } + + @Override + public void fillWindow (int pos, CursorWindow window) { + int nNumRows = window.getNumRows(); + int nCIndex = 0; + window.setStartPosition(0); + + if (pos > -1) { + if (nNumRows == 0) { + window.allocRow(); + nNumRows = window.getNumRows(); + } + + if (nNumRows == 1) { + nCIndex = getColumnIndex(LENGTH); + if (nCIndex > -1) { + window.putLong(lFileSize, 0, nCIndex); + } + nCIndex = getColumnIndex(_ID); + if (nCIndex > -1) { + window.putLong((long)pos, 0, nCIndex); + } + nCIndex = getColumnIndex(CHUNK); + if (nCIndex > -1) { + if (lLength != 0) { + byte[] value = getABlob (pos, 1); + window.putBlob(value, 0, nCIndex); + } + } + } + window.setStartPosition(pos); + } + return; + } + + public byte[] getABlob (int row, int column) { + int nRead = 0; + int nOffset = 0; + int nBufSize = 0; + + if ((column == 1) && (theFile != null)) { + try { + FileInputStream fin = new FileInputStream(theFile); + nOffset = row * BUFSIZE; + if (row < (nCount - 1)) { + nBufSize = BUFSIZE; + } else { + nBufSize = (int) (lFileSize - nOffset); + } + theBuffer = new byte[nBufSize]; + + if (theBuffer != null) { + if (fin.skip(nOffset + lOffset) == (nOffset + lOffset)) { + if ((nRead = fin.read(theBuffer, 0, nBufSize)) != -1) { + if (nRead != nBufSize) { + return null; + } + } + } + } + + fin.close(); + } catch (FileNotFoundException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + return theBuffer; + } +} diff --git a/build/mobile/sutagent/android/ffxcp/Makefile.in b/build/mobile/sutagent/android/ffxcp/Makefile.in new file mode 100644 index 000000000..2cabb7579 --- /dev/null +++ b/build/mobile/sutagent/android/ffxcp/Makefile.in @@ -0,0 +1,66 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +DEPTH = @DEPTH@ +topsrcdir = @top_srcdir@ +srcdir = @srcdir@ +VPATH = @srcdir@ + +include $(DEPTH)/config/autoconf.mk + +ANDROID_APK_NAME := FfxCP + +JAVAFILES = \ + DirCursor.java \ + ffxcp.java \ + FfxCPFP.java \ + FileCursor.java \ + R.java \ + $(NULL) + +RES_FILES = \ + res/drawable-hdpi/icon.png \ + res/drawable-ldpi/icon.png \ + res/drawable-mdpi/icon.png \ + res/layout/main.xml \ + res/values/strings.xml \ + $(NULL) + +GARBAGE += \ + AndroidManifest.xml \ + classes.dex \ + $(ANDROID_APK_NAME).ap_ \ + $(ANDROID_APK_NAME)-unsigned-unaligned.apk \ + $(ANDROID_APK_NAME)-unaligned.apk \ + $(ANDROID_APK_NAME).apk \ + $(NULL) + +GARBAGE_DIRS += network-libs + +JAVA_CLASSPATH = $(ANDROID_SDK)/android.jar + +include $(topsrcdir)/config/rules.mk + +# include Android specific java flags - using these instead of what's in rules.mk +include $(topsrcdir)/config/android-common.mk + +tools:: $(ANDROID_APK_NAME).apk + +classes.dex: $(JAVAFILES) + $(JAVAC) $(JAVAC_FLAGS) -d classes $(addprefix $(srcdir)/,$(JAVAFILES)) + $(DX) --dex --output=$@ classes + +$(ANDROID_APK_NAME).ap_: AndroidManifest.xml + $(AAPT) package -f -M $< -I $(ANDROID_SDK)/android.jar -S res -F $@ + +$(ANDROID_APK_NAME)-unsigned-unaligned.apk: $(ANDROID_APK_NAME).ap_ classes.dex + cp $< $@ + $(ZIP) -0 $@ classes.dex + +$(ANDROID_APK_NAME)-unaligned.apk: $(ANDROID_APK_NAME)-unsigned-unaligned.apk + cp $< $@ + $(DEBUG_JARSIGNER) $@ + +$(ANDROID_APK_NAME).apk: $(ANDROID_APK_NAME)-unaligned.apk + $(ZIPALIGN) -f -v 4 $< $@ diff --git a/build/mobile/sutagent/android/ffxcp/R.java b/build/mobile/sutagent/android/ffxcp/R.java new file mode 100644 index 000000000..5366cc5c8 --- /dev/null +++ b/build/mobile/sutagent/android/ffxcp/R.java @@ -0,0 +1,27 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* AUTO-GENERATED FILE. DO NOT MODIFY. + * + * This class was automatically generated by the + * aapt tool from the resource data it found. It + * should not be modified by hand. + */ + +package org.mozilla.ffxcp; + +public final class R { + public static final class attr { + } + public static final class drawable { + public static final int icon=0x7f020000; + } + public static final class layout { + public static final int main=0x7f030000; + } + public static final class string { + public static final int app_name=0x7f040001; + public static final int hello=0x7f040000; + } +} diff --git a/build/mobile/sutagent/android/ffxcp/default.properties b/build/mobile/sutagent/android/ffxcp/default.properties new file mode 100644 index 000000000..06596fbb7 --- /dev/null +++ b/build/mobile/sutagent/android/ffxcp/default.properties @@ -0,0 +1,15 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +# This file is automatically generated by Android Tools. +# Do not modify this file -- YOUR CHANGES WILL BE ERASED! +# +# This file must be checked in Version Control Systems. +# +# To customize properties used by the Ant build system use, +# "build.properties", and override values to adapt the script to your +# project structure. + +# Project target. +target=android-6 diff --git a/build/mobile/sutagent/android/ffxcp/ffxcp.java b/build/mobile/sutagent/android/ffxcp/ffxcp.java new file mode 100644 index 000000000..39d3c580b --- /dev/null +++ b/build/mobile/sutagent/android/ffxcp/ffxcp.java @@ -0,0 +1,16 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +package org.mozilla.ffxcp; + +import android.app.Activity; +import android.os.Bundle; + +public class ffxcp extends Activity { + /** Called when the activity is first created. */ + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.main); + } +}
\ No newline at end of file diff --git a/build/mobile/sutagent/android/ffxcp/moz.build b/build/mobile/sutagent/android/ffxcp/moz.build new file mode 100644 index 000000000..11b4ec2cd --- /dev/null +++ b/build/mobile/sutagent/android/ffxcp/moz.build @@ -0,0 +1,8 @@ +# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +MODULE = 'FfxCP' + diff --git a/build/mobile/sutagent/android/ffxcp/res/drawable-hdpi/icon.png b/build/mobile/sutagent/android/ffxcp/res/drawable-hdpi/icon.png Binary files differnew file mode 100644 index 000000000..8074c4c57 --- /dev/null +++ b/build/mobile/sutagent/android/ffxcp/res/drawable-hdpi/icon.png diff --git a/build/mobile/sutagent/android/ffxcp/res/drawable-ldpi/icon.png b/build/mobile/sutagent/android/ffxcp/res/drawable-ldpi/icon.png Binary files differnew file mode 100644 index 000000000..1095584ec --- /dev/null +++ b/build/mobile/sutagent/android/ffxcp/res/drawable-ldpi/icon.png diff --git a/build/mobile/sutagent/android/ffxcp/res/drawable-mdpi/icon.png b/build/mobile/sutagent/android/ffxcp/res/drawable-mdpi/icon.png Binary files differnew file mode 100644 index 000000000..a07c69fa5 --- /dev/null +++ b/build/mobile/sutagent/android/ffxcp/res/drawable-mdpi/icon.png diff --git a/build/mobile/sutagent/android/ffxcp/res/layout/main.xml b/build/mobile/sutagent/android/ffxcp/res/layout/main.xml new file mode 100644 index 000000000..1256b1a7c --- /dev/null +++ b/build/mobile/sutagent/android/ffxcp/res/layout/main.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- This Source Code Form is subject to the terms of the Mozilla Public + - License, v. 2.0. If a copy of the MPL was not distributed with this + - file, You can obtain one at http://mozilla.org/MPL/2.0/. --> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="vertical" + android:layout_width="fill_parent" + android:layout_height="fill_parent" + > +<TextView + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:text="@string/hello" + /> +</LinearLayout> diff --git a/build/mobile/sutagent/android/ffxcp/res/values/strings.xml b/build/mobile/sutagent/android/ffxcp/res/values/strings.xml new file mode 100644 index 000000000..e7e8d7b2d --- /dev/null +++ b/build/mobile/sutagent/android/ffxcp/res/values/strings.xml @@ -0,0 +1,9 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- This Source Code Form is subject to the terms of the Mozilla Public + - License, v. 2.0. If a copy of the MPL was not distributed with this + - file, You can obtain one at http://mozilla.org/MPL/2.0/. --> + +<resources> + <string name="hello">Hello World, firefoxcp!</string> + <string name="app_name">FirefoxCP</string> +</resources> diff --git a/build/mobile/sutagent/android/moz.build b/build/mobile/sutagent/android/moz.build new file mode 100644 index 000000000..58a08666c --- /dev/null +++ b/build/mobile/sutagent/android/moz.build @@ -0,0 +1,8 @@ +# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +MODULE = 'sutAgentAndroid' + diff --git a/build/mobile/sutagent/android/network-libs/commons-net-2.0-javadoc.jar b/build/mobile/sutagent/android/network-libs/commons-net-2.0-javadoc.jar Binary files differnew file mode 100644 index 000000000..c68405dcd --- /dev/null +++ b/build/mobile/sutagent/android/network-libs/commons-net-2.0-javadoc.jar diff --git a/build/mobile/sutagent/android/network-libs/commons-net-2.0-sources.jar b/build/mobile/sutagent/android/network-libs/commons-net-2.0-sources.jar Binary files differnew file mode 100644 index 000000000..50cc21098 --- /dev/null +++ b/build/mobile/sutagent/android/network-libs/commons-net-2.0-sources.jar diff --git a/build/mobile/sutagent/android/network-libs/commons-net-2.0.jar b/build/mobile/sutagent/android/network-libs/commons-net-2.0.jar Binary files differnew file mode 100644 index 000000000..dd7a52a69 --- /dev/null +++ b/build/mobile/sutagent/android/network-libs/commons-net-2.0.jar diff --git a/build/mobile/sutagent/android/network-libs/commons-net-ftp-2.0.jar b/build/mobile/sutagent/android/network-libs/commons-net-ftp-2.0.jar Binary files differnew file mode 100644 index 000000000..f697534cf --- /dev/null +++ b/build/mobile/sutagent/android/network-libs/commons-net-ftp-2.0.jar diff --git a/build/mobile/sutagent/android/network-libs/jmdns.jar b/build/mobile/sutagent/android/network-libs/jmdns.jar Binary files differnew file mode 100644 index 000000000..ec1c26354 --- /dev/null +++ b/build/mobile/sutagent/android/network-libs/jmdns.jar diff --git a/build/mobile/sutagent/android/res/drawable/ateamlogo.png b/build/mobile/sutagent/android/res/drawable/ateamlogo.png Binary files differnew file mode 100644 index 000000000..6f65bd6e9 --- /dev/null +++ b/build/mobile/sutagent/android/res/drawable/ateamlogo.png diff --git a/build/mobile/sutagent/android/res/drawable/ic_stat_first.png b/build/mobile/sutagent/android/res/drawable/ic_stat_first.png Binary files differnew file mode 100644 index 000000000..95b090433 --- /dev/null +++ b/build/mobile/sutagent/android/res/drawable/ic_stat_first.png diff --git a/build/mobile/sutagent/android/res/drawable/ic_stat_neterror.png b/build/mobile/sutagent/android/res/drawable/ic_stat_neterror.png Binary files differnew file mode 100644 index 000000000..90c0bf850 --- /dev/null +++ b/build/mobile/sutagent/android/res/drawable/ic_stat_neterror.png diff --git a/build/mobile/sutagent/android/res/drawable/ic_stat_second.png b/build/mobile/sutagent/android/res/drawable/ic_stat_second.png Binary files differnew file mode 100644 index 000000000..e3304e50f --- /dev/null +++ b/build/mobile/sutagent/android/res/drawable/ic_stat_second.png diff --git a/build/mobile/sutagent/android/res/drawable/ic_stat_warning.png b/build/mobile/sutagent/android/res/drawable/ic_stat_warning.png Binary files differnew file mode 100644 index 000000000..be00f470a --- /dev/null +++ b/build/mobile/sutagent/android/res/drawable/ic_stat_warning.png diff --git a/build/mobile/sutagent/android/res/drawable/icon.png b/build/mobile/sutagent/android/res/drawable/icon.png Binary files differnew file mode 100644 index 000000000..a07c69fa5 --- /dev/null +++ b/build/mobile/sutagent/android/res/drawable/icon.png diff --git a/build/mobile/sutagent/android/res/layout/main.xml b/build/mobile/sutagent/android/res/layout/main.xml new file mode 100644 index 000000000..07ae83b6a --- /dev/null +++ b/build/mobile/sutagent/android/res/layout/main.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- This Source Code Form is subject to the terms of the Mozilla Public + - License, v. 2.0. If a copy of the MPL was not distributed with this + - file, You can obtain one at http://mozilla.org/MPL/2.0/. --> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="vertical" + android:layout_width="fill_parent" + android:layout_height="fill_parent" + > +<TextView + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:id="@+id/Textview01"/> + + + + +<Button android:id="@+id/Button01" android:layout_width="wrap_content" android:layout_height="wrap_content" android:clickable="true" android:text="Exit"></Button> + + + +</LinearLayout> diff --git a/build/mobile/sutagent/android/res/values/strings.xml b/build/mobile/sutagent/android/res/values/strings.xml new file mode 100644 index 000000000..518e77aa7 --- /dev/null +++ b/build/mobile/sutagent/android/res/values/strings.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- This Source Code Form is subject to the terms of the Mozilla Public + - License, v. 2.0. If a copy of the MPL was not distributed with this + - file, You can obtain one at http://mozilla.org/MPL/2.0/. --> + +<resources> + + <string name="hello">Hello World, SUTAgentAndroid!</string> + <string name="app_name">SUTAgentAndroid</string> + + +<string name="foreground_service_started">Foreground Service Started (ASMozStub)</string> + +</resources> diff --git a/build/mobile/sutagent/android/watcher/AndroidManifest.xml b/build/mobile/sutagent/android/watcher/AndroidManifest.xml new file mode 100644 index 000000000..b78dce5d7 --- /dev/null +++ b/build/mobile/sutagent/android/watcher/AndroidManifest.xml @@ -0,0 +1,44 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- This Source Code Form is subject to the terms of the Mozilla Public + - License, v. 2.0. If a copy of the MPL was not distributed with this + - file, You can obtain one at http://mozilla.org/MPL/2.0/. --> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.mozilla.watcher" + android:versionCode="1" + android:versionName="1.0"> + <uses-permission android:name="android.permission.WRITE_SETTINGS"></uses-permission> + <application android:icon="@drawable/icon" android:label="@string/app_name"> + <activity android:name=".WatcherMain" + android:label="@string/app_name"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> + </activity> + <receiver android:name=".WatcherReceiver"> + <intent-filter> + <action android:value="android.intent.action.BOOT_COMPLETED" android:name="android.intent.action.BOOT_COMPLETED"/> + <category android:value="android.intent.category.HOME" android:name="android.intent.category.HOME"/> + </intent-filter> + </receiver> + <service android:name="WatcherService"> + <intent-filter> + <action android:name="com.mozilla.watcher.LISTENER_SERVICE" /> + </intent-filter> + </service> + + </application> + <uses-sdk android:minSdkVersion="5" /> + <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" /> + + +<uses-permission android:name="android.permission.INTERNET"></uses-permission> + +<uses-permission android:name="android.permission.DISABLE_KEYGUARD"></uses-permission> + +<uses-permission android:name="android.permission.WAKE_LOCK"></uses-permission> + +<uses-permission android:name="android.permission.VIBRATE"></uses-permission> + +</manifest>
\ No newline at end of file diff --git a/build/mobile/sutagent/android/watcher/IWatcherService.java b/build/mobile/sutagent/android/watcher/IWatcherService.java new file mode 100644 index 000000000..9883c8a5b --- /dev/null +++ b/build/mobile/sutagent/android/watcher/IWatcherService.java @@ -0,0 +1,108 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + * This file is auto-generated. DO NOT MODIFY. + * Original file: C:\\Users\\Bob\\workspace\\Watcher\\src\\com\\mozilla\\watcher\\IWatcherService.aidl + */ +package com.mozilla.watcher; +public interface IWatcherService extends android.os.IInterface +{ +/** Local-side IPC implementation stub class. */ +public static abstract class Stub extends android.os.Binder implements com.mozilla.watcher.IWatcherService +{ +private static final java.lang.String DESCRIPTOR = "com.mozilla.watcher.IWatcherService"; +/** Construct the stub at attach it to the interface. */ +public Stub() +{ +this.attachInterface(this, DESCRIPTOR); +} +/** + * Cast an IBinder object into an com.mozilla.watcher.IWatcherService interface, + * generating a proxy if needed. + */ +public static com.mozilla.watcher.IWatcherService asInterface(android.os.IBinder obj) +{ +if ((obj==null)) { +return null; +} +android.os.IInterface iin = (android.os.IInterface)obj.queryLocalInterface(DESCRIPTOR); +if (((iin!=null)&&(iin instanceof com.mozilla.watcher.IWatcherService))) { +return ((com.mozilla.watcher.IWatcherService)iin); +} +return new com.mozilla.watcher.IWatcherService.Stub.Proxy(obj); +} +public android.os.IBinder asBinder() +{ +return this; +} +@Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException +{ +switch (code) +{ +case INTERFACE_TRANSACTION: +{ +reply.writeString(DESCRIPTOR); +return true; +} +case TRANSACTION_UpdateApplication: +{ +data.enforceInterface(DESCRIPTOR); +java.lang.String _arg0; +_arg0 = data.readString(); +java.lang.String _arg1; +_arg1 = data.readString(); +java.lang.String _arg2; +_arg2 = data.readString(); +int _arg3; +_arg3 = data.readInt(); +int _result = this.UpdateApplication(_arg0, _arg1, _arg2, _arg3); +reply.writeNoException(); +reply.writeInt(_result); +return true; +} +} +return super.onTransact(code, data, reply, flags); +} +private static class Proxy implements com.mozilla.watcher.IWatcherService +{ +private android.os.IBinder mRemote; +Proxy(android.os.IBinder remote) +{ +mRemote = remote; +} +public android.os.IBinder asBinder() +{ +return mRemote; +} +public java.lang.String getInterfaceDescriptor() +{ +return DESCRIPTOR; +} +public int UpdateApplication(java.lang.String sPkgName, java.lang.String sPkgFileName, java.lang.String sOutFile, int bReboot) throws android.os.RemoteException +{ +android.os.Parcel _data = android.os.Parcel.obtain(); +android.os.Parcel _reply = android.os.Parcel.obtain(); +int _result; +try { +_data.writeInterfaceToken(DESCRIPTOR); +_data.writeString(sPkgName); +_data.writeString(sPkgFileName); +_data.writeString(sOutFile); +_data.writeInt(bReboot); +mRemote.transact(Stub.TRANSACTION_UpdateApplication, _data, _reply, 0); +_reply.readException(); +_result = _reply.readInt(); +} +finally { +_reply.recycle(); +_data.recycle(); +} +return _result; +} +} +static final int TRANSACTION_UpdateApplication = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0); +} +public int UpdateApplication(java.lang.String sPkgName, java.lang.String sPkgFileName, java.lang.String sOutFile, int bReboot) throws android.os.RemoteException; +} diff --git a/build/mobile/sutagent/android/watcher/Makefile.in b/build/mobile/sutagent/android/watcher/Makefile.in new file mode 100644 index 000000000..c711e75ce --- /dev/null +++ b/build/mobile/sutagent/android/watcher/Makefile.in @@ -0,0 +1,71 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +DEPTH = @DEPTH@ +topsrcdir = @top_srcdir@ +srcdir = @srcdir@ +VPATH = @srcdir@ + +include $(DEPTH)/config/autoconf.mk + +ANDROID_APK_NAME := Watcher + +JAVAFILES = \ + IWatcherService.java \ + RedirOutputThread.java \ + R.java \ + WatcherMain.java \ + WatcherReceiver.java \ + WatcherService.java \ + $(NULL) + +RES_FILES = \ + res/drawable-hdpi/icon.png \ + res/drawable-hdpi/ateamlogo.png \ + res/drawable-ldpi/icon.png \ + res/drawable-ldpi/ateamlogo.png \ + res/drawable-mdpi/icon.png \ + res/drawable-mdpi/ateamlogo.png \ + res/layout/main.xml \ + res/values/strings.xml \ + $(NULL) + +GARBAGE += \ + AndroidManifest.xml \ + classes.dex \ + $(ANDROID_APK_NAME).ap_ \ + $(ANDROID_APK_NAME)-unsigned-unaligned.apk \ + $(ANDROID_APK_NAME)-unaligned.apk \ + $(ANDROID_APK_NAME).apk \ + $(NULL) + +GARBAGE_DIRS += res classes network-libs + +JAVA_CLASSPATH = $(ANDROID_SDK)/android.jar + +include $(topsrcdir)/config/rules.mk + +# include Android specific java flags - using these instead of what's in rules.mk +include $(topsrcdir)/config/android-common.mk + +tools:: $(ANDROID_APK_NAME).apk + +classes.dex: $(JAVAFILES) + $(NSINSTALL) -D classes + $(JAVAC) $(JAVAC_FLAGS) -d classes $(addprefix $(srcdir)/,$(JAVAFILES)) + $(DX) --dex --output=$@ classes + +$(ANDROID_APK_NAME).ap_: AndroidManifest.xml + $(AAPT) package -f -M $< -I $(ANDROID_SDK)/android.jar -S res -F $@ + +$(ANDROID_APK_NAME)-unsigned-unaligned.apk: $(ANDROID_APK_NAME).ap_ classes.dex + cp $< $@ + $(ZIP) -0 $@ classes.dex + +$(ANDROID_APK_NAME)-unaligned.apk: $(ANDROID_APK_NAME)-unsigned-unaligned.apk + cp $< $@ + $(DEBUG_JARSIGNER) $@ + +$(ANDROID_APK_NAME).apk: $(ANDROID_APK_NAME)-unaligned.apk + $(ZIPALIGN) -f -v 4 $< $@ diff --git a/build/mobile/sutagent/android/watcher/R.java b/build/mobile/sutagent/android/watcher/R.java new file mode 100644 index 000000000..b1917c0bd --- /dev/null +++ b/build/mobile/sutagent/android/watcher/R.java @@ -0,0 +1,29 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* AUTO-GENERATED FILE. DO NOT MODIFY. + * + * This class was automatically generated by the + * aapt tool from the resource data it found. It + * should not be modified by hand. + */ + +package com.mozilla.watcher; + +public final class R { + public static final class attr { + } + public static final class drawable { + public static final int ateamlogo=0x7f020000; + public static final int icon=0x7f020001; + } + public static final class layout { + public static final int main=0x7f030000; + } + public static final class string { + public static final int app_name=0x7f040001; + public static final int foreground_service_started=0x7f040002; + public static final int hello=0x7f040000; + } +} diff --git a/build/mobile/sutagent/android/watcher/RedirOutputThread.java b/build/mobile/sutagent/android/watcher/RedirOutputThread.java new file mode 100644 index 000000000..440e726a0 --- /dev/null +++ b/build/mobile/sutagent/android/watcher/RedirOutputThread.java @@ -0,0 +1,131 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package com.mozilla.watcher; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.PrintWriter; + +public class RedirOutputThread extends Thread + { + OutputStream out; + InputStream sutErr; + InputStream sutOut; + Process pProc; + String strOutput; + + public RedirOutputThread(Process pProc, OutputStream out) + { + if (pProc != null) + { + this.pProc = pProc; + sutErr = pProc.getErrorStream(); // Stderr + sutOut = pProc.getInputStream(); // Stdout + } + if (out != null) + this.out = out; + + strOutput = ""; + } + + public void run() + { + boolean bStillRunning = true; + int nBytesOut = 0; + int nBytesErr = 0; + int nBytesRead = 0; + PrintWriter pOut = null; + byte[] buffer = new byte[1024]; + + if (out != null) + pOut = new PrintWriter(out); + else + bStillRunning = true; + + while (bStillRunning) + { + try + { + if ((nBytesOut = sutOut.available()) > 0) + { + if (nBytesOut > buffer.length) + { + buffer = null; + System.gc(); + buffer = new byte[nBytesOut]; + } + nBytesRead = sutOut.read(buffer, 0, nBytesOut); + if (nBytesRead == -1) + bStillRunning = false; + else + { + String sRep = new String(buffer,0,nBytesRead).replace("\n", "\r\n"); + if (pOut != null) + { + pOut.print(sRep); + pOut.flush(); + } + else + strOutput += sRep; + } + } + + if ((nBytesErr = sutErr.available()) > 0) + { + if (nBytesErr > buffer.length) + { + buffer = null; + System.gc(); + buffer = new byte[nBytesErr]; + } + nBytesRead = sutErr.read(buffer, 0, nBytesErr); + if (nBytesRead == -1) + bStillRunning = false; + else + { + String sRep = new String(buffer,0,nBytesRead).replace("\n", "\r\n"); + if (pOut != null) + { + pOut.print(sRep); + pOut.flush(); + } + else + strOutput += sRep; + } + } + + bStillRunning = (IsProcRunning(pProc) || (sutOut.available() > 0) || (sutErr.available() > 0)); + } + catch (IOException e) + { +// Toast.makeText(SUTAgentAndroid.me.getApplicationContext(), e.getMessage(), Toast.LENGTH_LONG).show(); + e.printStackTrace(); + } + } + + pProc.destroy(); + buffer = null; + System.gc(); + } + + private boolean IsProcRunning(Process pProc) + { + boolean bRet = false; + @SuppressWarnings("unused") + int nExitCode = 0; + + try + { + nExitCode = pProc.exitValue(); + } + catch (IllegalThreadStateException z) + { + bRet = true; + } + + return(bRet); + } + } diff --git a/build/mobile/sutagent/android/watcher/WatcherMain.java b/build/mobile/sutagent/android/watcher/WatcherMain.java new file mode 100644 index 000000000..d0c5ee6d5 --- /dev/null +++ b/build/mobile/sutagent/android/watcher/WatcherMain.java @@ -0,0 +1,16 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +package com.mozilla.watcher; + +import android.app.Activity; +import android.os.Bundle; + +public class WatcherMain extends Activity { + /** Called when the activity is first created. */ + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.main); + } +}
\ No newline at end of file diff --git a/build/mobile/sutagent/android/watcher/WatcherReceiver.java b/build/mobile/sutagent/android/watcher/WatcherReceiver.java new file mode 100644 index 000000000..f47b42452 --- /dev/null +++ b/build/mobile/sutagent/android/watcher/WatcherReceiver.java @@ -0,0 +1,22 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +package com.mozilla.watcher; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +// import android.os.Debug; + +public class WatcherReceiver extends BroadcastReceiver { + + @Override + public void onReceive(Context context, Intent intent) { +// Debug.waitForDebugger(); + Intent serviceIntent = new Intent(); + serviceIntent.putExtra("command", "start"); + serviceIntent.setAction("com.mozilla.watcher.LISTENER_SERVICE"); + context.startService(serviceIntent); + } + +} diff --git a/build/mobile/sutagent/android/watcher/WatcherService.java b/build/mobile/sutagent/android/watcher/WatcherService.java new file mode 100644 index 000000000..4b5554256 --- /dev/null +++ b/build/mobile/sutagent/android/watcher/WatcherService.java @@ -0,0 +1,1044 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +package com.mozilla.watcher; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.PrintWriter; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.Date; +import java.util.List; +import java.util.Timer; +import java.util.TimerTask; + +import android.app.Activity; +import android.app.ActivityManager; +import android.app.KeyguardManager; +import android.app.Notification; +import android.app.NotificationManager; +import android.app.PendingIntent; +import android.app.Service; +import android.content.ActivityNotFoundException; +import android.content.ContentResolver; +import android.content.Context; +import android.content.ContextWrapper; +import android.content.Intent; +import android.content.pm.ActivityInfo; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; +import android.os.BatteryManager; +import android.os.Debug; +import android.os.IBinder; +import android.os.PowerManager; +import android.os.RemoteException; +import android.provider.Settings; +import android.util.Log; +import android.view.Gravity; +import android.widget.Toast; +import android.os.Environment; + +public class WatcherService extends Service +{ + private final String prgVersion = "Watcher Version 1.16"; + private final String LOGTAG = "Watcher"; + + String sErrorPrefix = "##Installer Error## "; + String currentDir = "/"; + String sPingTarget = ""; + long lDelay = 60000; + long lPeriod = 300000; + int nMaxStrikes = 0; // maximum number of tries before we consider network unreachable (0 means don't check) + boolean bStartSUTAgent = true; + boolean bStartedTimer = false; + + Process pProc; + Context myContext = null; + Timer myTimer = null; + private PowerManager.WakeLock pwl = null; + public static final int NOTIFICATION_ID = 1964; + boolean bInstalling = false; + + @SuppressWarnings("unchecked") + private static final Class<?>[] mSetForegroundSignature = new Class[] { + boolean.class}; + @SuppressWarnings("unchecked") + private static final Class<?>[] mStartForegroundSignature = new Class[] { + int.class, Notification.class}; + @SuppressWarnings("unchecked") + private static final Class<?>[] mStopForegroundSignature = new Class[] { + boolean.class}; + + private NotificationManager mNM; + private Method mSetForeground; + private Method mStartForeground; + private Method mStopForeground; + private Object[] mSetForegroundArgs = new Object[1]; + private Object[] mStartForegroundArgs = new Object[2]; + private Object[] mStopForegroundArgs = new Object[1]; + + + private IWatcherService.Stub stub = new IWatcherService.Stub() { + public int UpdateApplication(String sAppName, String sFileName, String sOutFile, int bReboot) throws RemoteException + { + return UpdtApp(sAppName, sFileName, sOutFile, bReboot); + } + }; + + @Override + public IBinder onBind(Intent arg0) { + return stub; + } + + @Override + public void onCreate() + { + super.onCreate(); + + Log.i(LOGTAG, prgVersion); + + myContext = this; + + getKeyGuardAndWakeLock(); + + File dir = getFilesDir(); + File iniFile = new File(dir, "watcher.ini"); + String sIniFile = iniFile.getAbsolutePath(); + String sHold = ""; + + Log.i(LOGTAG, String.format("Loading settings from %s", sIniFile)); + this.sPingTarget = GetIniData("watcher", "PingTarget", sIniFile, "www.mozilla.org"); + sHold = GetIniData("watcher", "delay", sIniFile, "60000"); + this.lDelay = Long.parseLong(sHold.trim()); + sHold = GetIniData("watcher", "period", sIniFile,"300000"); + this.lPeriod = Long.parseLong(sHold.trim()); + sHold = GetIniData("watcher", "strikes", sIniFile,"0"); + this.nMaxStrikes = Integer.parseInt(sHold.trim()); + Log.i(LOGTAG, String.format("Pinging %s after a delay of %s sec, period of %s sec, max number of failed attempts is %s (if max # of failed attempts is 0, then no checking)", + this.sPingTarget, this.lDelay / 1000.0, this.lPeriod / 1000.0, nMaxStrikes)); + + sHold = GetIniData("watcher", "StartSUTAgent", sIniFile, "true"); + this.bStartSUTAgent = Boolean.parseBoolean(sHold.trim()); + + sHold = GetIniData("watcher", "stayon", sIniFile,"0"); + int nStayOn = Integer.parseInt(sHold.trim()); + + try { + if (nStayOn != 0) { + if (!Settings.System.putInt(getContentResolver(), Settings.System.STAY_ON_WHILE_PLUGGED_IN, BatteryManager.BATTERY_PLUGGED_AC | BatteryManager.BATTERY_PLUGGED_USB)) { + doToast("Screen couldn't be set to Always On [stay on while plugged in]"); + } + } + } catch (Exception e) { + e.printStackTrace(); + String sExcept = e.getMessage(); + doToast("Screen couldn't be set to Always On [exception " + sExcept + "]"); + } + + doToast("WatcherService created"); + } + + public String GetIniData(String sSection, String sKey, String sFile, String sDefault) + { + String sRet = sDefault; + String sComp = ""; + String sLine = ""; + boolean bFound = false; + BufferedReader in = null; + String sTmpFileName = fixFileName(sFile); + + try { + in = new BufferedReader(new FileReader(sTmpFileName)); + sComp = "[" + sSection + "]"; + while ((sLine = in.readLine()) != null) + { + if (sLine.equalsIgnoreCase(sComp)) + { + bFound = true; + break; + } + } + + if (bFound) + { + sComp = (sKey + " =").toLowerCase(); + while ((sLine = in.readLine()) != null) + { + if (sLine.toLowerCase().contains(sComp)) + { + String [] temp = null; + temp = sLine.split("="); + if (temp != null) + { + if (temp.length > 1) + sRet = temp[1].trim(); + } + break; + } + } + } + in.close(); + } + catch (FileNotFoundException e) + { + sComp = e.toString(); + } + catch (IOException e) + { + sComp = e.toString(); + } + return (sRet); + } + + private void handleCommand(Intent intent) + { + // Note: intent can be null "if the service is being restarted after its process + // has gone away". In this case, we will consider that to be equivalent to a start + // http://developer.android.com/reference/android/app/Service.html#onStartCommand%28android.content.Intent,%20int,%20int%29 + + String sCmd = "start"; + if (intent != null) + { + sCmd = intent.getStringExtra("command"); + } + + if (sCmd != null) + { + if (sCmd.equalsIgnoreCase("updt")) + { + String sPkgName = intent.getStringExtra("pkgName"); + String sPkgFile = intent.getStringExtra("pkgFile"); + String sOutFile = intent.getStringExtra("outFile"); + boolean bReboot = intent.getBooleanExtra("reboot", true); + int nReboot = bReboot ? 1 : 0; + Log.i(LOGTAG, "WatcherService updating " + sPkgName + " using file " + sPkgFile); + + UpdateApplication worker = new UpdateApplication(sPkgName, sPkgFile, sOutFile, nReboot); + } + else if (sCmd.equalsIgnoreCase("start")) + { + if (!this.bStartedTimer) + { + doToast("WatcherService started"); + myTimer = new Timer(); + Date startSchedule = new Date(System.currentTimeMillis() + lDelay); + myTimer.schedule(new MyTime(), startSchedule, lPeriod); + this.bStartedTimer = true; + } + } + else + { + doToast("WatcherService unknown command"); + } + } + else + doToast("WatcherService created"); + } + + public void writeVersion() { + PrintWriter pw = null; + String appPath = getApplicationContext().getFilesDir().getAbsolutePath(); + String versionPath = appPath + "/version.txt"; + Log.i(LOGTAG, "writing version string to: " + versionPath); + try { + pw = new PrintWriter(new FileWriter(versionPath, true)); + pw.println(this.prgVersion); + } catch (IOException ioe) { + Log.e(LOGTAG, "Exception writing version: " + this.prgVersion + " to file: " + versionPath); + } finally { + if (pw != null) { + pw.close(); + } + } + } + + @Override + public void onStart(Intent intent, int startId) { + Log.i(LOGTAG, "onStart"); + writeVersion(); + handleCommand(intent); + return; + } + + @Override + public int onStartCommand(Intent intent, int flags, int startId) { + Log.i(LOGTAG, "onStartCommand"); + writeVersion(); + handleCommand(intent); + return START_STICKY; + } + + @Override + public void onDestroy() { + super.onDestroy(); + doToast("WatcherService destroyed"); + if (pwl != null) + pwl.release(); + stopForegroundCompat(R.string.foreground_service_started); + } + + @Override + public void onLowMemory() { + Log.e(LOGTAG, "onLowMemory"); + System.gc(); + } + + @Override + public void onTrimMemory(int level) { + Log.e(LOGTAG, "onTrimMemory: "+level); + System.gc(); + } + + protected void getKeyGuardAndWakeLock() + { + // Fire off a thread to do some work that we shouldn't do directly in the UI thread + Thread t = new Thread() { + public void run() { + Log.i(LOGTAG, "worker thread started"); + // Keep phone from locking or remove lock on screen + KeyguardManager km = (KeyguardManager)getSystemService(Context.KEYGUARD_SERVICE); + if (km != null) + { + KeyguardManager.KeyguardLock kl = km.newKeyguardLock("watcher"); + if (kl != null) + { + kl.disableKeyguard(); + Log.i(LOGTAG, "keyguard disabled"); + } + } + + // No sleeping on the job + PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE); + if (pm != null) + { + pwl = pm.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK, "watcher"); + if (pwl != null) + { + pwl.acquire(); + Log.i(LOGTAG, "wake lock acquired"); + } + } + + Class<?> serviceClass = null; + try { + serviceClass = Class.forName("com.mozilla.watcher.WatcherService"); + } + catch (Exception e) + { + Log.e(LOGTAG, "unable to find service class: "+e.toString()); + return; + } + mNM = (NotificationManager)getSystemService(NOTIFICATION_SERVICE); + try { + mStartForeground = serviceClass.getMethod("startForeground", mStartForegroundSignature); + mStopForeground = serviceClass.getMethod("stopForeground", mStopForegroundSignature); + } + catch (NoSuchMethodException e) + { + // Might be running on an older platform. + mStartForeground = mStopForeground = null; + Log.w(LOGTAG, "unable to find start/stopForeground method(s) -- older platform?"); + } + try { + mSetForeground = serviceClass.getMethod("setForeground", mSetForegroundSignature); + } + catch (NoSuchMethodException e) + { + mSetForeground = null; + Log.e(LOGTAG, "unable to find setForeground method!"); + } + Notification notification = new Notification(); + startForegroundCompat(R.string.foreground_service_started, notification); + } + }; + t.start(); + } + + /** + * This is a wrapper around the new startForeground method, using the older + * APIs if it is not available. + */ + void startForegroundCompat(int id, Notification notification) { + // If we have the new startForeground API, then use it. + if (mStartForeground != null) { + mStartForegroundArgs[0] = Integer.valueOf(id); + mStartForegroundArgs[1] = notification; + try { + mStartForeground.invoke(this, mStartForegroundArgs); + Log.i(LOGTAG, "startForeground invoked"); + } catch (InvocationTargetException e) { + // Should not happen. + Log.e(LOGTAG, "Unable to invoke startForeground", e); + } catch (IllegalAccessException e) { + // Should not happen. + Log.e(LOGTAG, "Unable to invoke startForeground", e); + } + return; + } + + // Fall back on the old API. + if (mSetForeground != null) { + try { + mSetForegroundArgs[0] = Boolean.TRUE; + mSetForeground.invoke(this, mSetForegroundArgs); + Log.i(LOGTAG, "setForeground(TRUE) invoked"); + } catch (IllegalArgumentException e) { + Log.e(LOGTAG, "Unable to invoke setForeground", e); + e.printStackTrace(); + } catch (IllegalAccessException e) { + Log.e(LOGTAG, "Unable to invoke setForeground", e); + e.printStackTrace(); + } catch (InvocationTargetException e) { + Log.e(LOGTAG, "Unable to invoke setForeground", e); + e.printStackTrace(); + } + } + mNM.notify(id, notification); + } + + /** + * This is a wrapper around the new stopForeground method, using the older + * APIs if it is not available. + */ + void stopForegroundCompat(int id) { + // If we have the new stopForeground API, then use it. + if (mStopForeground != null) { + mStopForegroundArgs[0] = Boolean.TRUE; + try { + mStopForeground.invoke(this, mStopForegroundArgs); + Log.i(LOGTAG, "stopForeground invoked"); + } catch (InvocationTargetException e) { + // Should not happen. + Log.e(LOGTAG, "Unable to invoke stopForeground", e); + } catch (IllegalAccessException e) { + // Should not happen. + Log.e(LOGTAG, "Unable to invoke stopForeground", e); + } + return; + } + + // Fall back on the old API. Note to cancel BEFORE changing the + // foreground state, since we could be killed at that point. + mNM.cancel(id); + if (mSetForeground != null) { + try { + mSetForegroundArgs[0] = Boolean.FALSE; + mSetForeground.invoke(this, mSetForegroundArgs); + Log.i(LOGTAG, "setForeground(FALSE) invoked"); + } catch (IllegalArgumentException e) { + Log.e(LOGTAG, "Unable to invoke setForeground", e); + e.printStackTrace(); + } catch (IllegalAccessException e) { + Log.e(LOGTAG, "Unable to invoke setForeground", e); + e.printStackTrace(); + } catch (InvocationTargetException e) { + Log.e(LOGTAG, "Unable to invoke setForeground", e); + e.printStackTrace(); + } + } + } + + public void doToast(String sMsg) + { + Log.i(LOGTAG, sMsg); + Toast toast = Toast.makeText(this, sMsg, Toast.LENGTH_LONG); + toast.setGravity(Gravity.TOP|Gravity.CENTER_HORIZONTAL, 0, 100); + toast.show(); + } + + public void CheckMem() + { + System.gc(); + long lFreeMemory = Runtime.getRuntime().freeMemory(); + long lTotMemory = Runtime.getRuntime().totalMemory(); + long lMaxMemory = Runtime.getRuntime().maxMemory(); + + Log.i(LOGTAG, "Free: " + lFreeMemory + "Total: " + lTotMemory + "Max: " + lMaxMemory); + } + + public int UpdtApp(String sPkgName, String sPkgFileName, String sOutFile, int bReboot) + { + int nRet = 1; + int lcv = 0; + String sRet = ""; + + FileOutputStream f = null; + + try { + Log.i(LOGTAG, "Step 1: Kill " + sPkgName + " if running"); + while (!IsProcessDead(sPkgName) && (lcv < 5)) { + if (KillProcess(sPkgName, null).startsWith("Successfully")) + break; + else + lcv++; + Thread.sleep(2000); + } + + CheckMem(); + + if ((sOutFile != null) && (sOutFile.length() > 0)) { + File outFile = new File(sOutFile); + if (outFile.exists() && outFile.canWrite()) { + f = new FileOutputStream(outFile, true); + } else { + Log.e(LOGTAG, "File not found or cannot write to " + sOutFile); + } + } + } catch (InterruptedException e) { + e.printStackTrace(); + } catch (FileNotFoundException e) { + Log.e(LOGTAG, "Couldn't open " + sOutFile + " " + e.getLocalizedMessage()); + e.printStackTrace(); + } catch (SecurityException e) { + Log.e(LOGTAG, "Exception message " + e.getLocalizedMessage()); + e.printStackTrace(); + } + + if ((sPkgName != null) && (sPkgName.length() > 0)) + { + Log.i(LOGTAG, "Step 2: Uninstall " + sPkgName); + sRet = UnInstallApp(sPkgName, null); + CheckMem(); + if ((sRet.length() > 0) && (f != null)) + { + try { + f.write(sRet.getBytes()); + f.flush(); + } + catch (IOException e) + { + e.printStackTrace(); + } + } + } + + if ((sPkgFileName != null) && (sPkgFileName.length() > 0)) + { + Log.i(LOGTAG, "Step 3: Install " + sPkgFileName); + sRet = InstallApp(sPkgFileName, null); + Log.i(LOGTAG, "" + sRet); + CheckMem(); + if ((sRet.length() > 0) && (f != null)) + { + try { + f.write(sRet.getBytes()); + f.flush(); + f.close(); + } + catch (IOException e) + { + e.printStackTrace(); + } + } + } + + if (bReboot > 0) + RunReboot(null); + + return(nRet); + } + + public boolean GetProcessInfo(String sProcName) + { + boolean bRet = false; + ActivityManager aMgr = (ActivityManager) getApplicationContext().getSystemService(Activity.ACTIVITY_SERVICE); + List <ActivityManager.RunningAppProcessInfo> lProcesses = aMgr.getRunningAppProcesses(); + int nProcs = 0; + int lcv = 0; + String strProcName = ""; + + if (lProcesses != null) + nProcs = lProcesses.size(); + + for (lcv = 0; lcv < nProcs; lcv++) + { + strProcName = lProcesses.get(lcv).processName; + if (strProcName.contains(sProcName)) + { + bRet = true; + break; + } + } + return (bRet); + } + + public String RunReboot(OutputStream out) + { + String sRet = ""; + String [] theArgs = new String [3]; + + theArgs[0] = "su"; + theArgs[1] = "-c"; + theArgs[2] = "reboot"; + Log.i(LOGTAG, "Running reboot!"); + + try + { + pProc = Runtime.getRuntime().exec(theArgs); + RedirOutputThread outThrd = new RedirOutputThread(pProc, out); + outThrd.start(); + outThrd.join(10000); + } + catch (IOException e) + { + sRet = e.getMessage(); + e.printStackTrace(); + } + catch (InterruptedException e) + { + e.printStackTrace(); + } + + return (sRet); + } + + public String KillProcess(String sProcName, OutputStream out) + { + String [] theArgs = new String [3]; + + theArgs[0] = "su"; + theArgs[1] = "-c"; + theArgs[2] = "kill"; + + String sRet = sErrorPrefix + "Unable to kill " + sProcName + "\n"; + ActivityManager aMgr = (ActivityManager) getSystemService(Activity.ACTIVITY_SERVICE); + List <ActivityManager.RunningAppProcessInfo> lProcesses = aMgr.getRunningAppProcesses(); + int lcv = 0; + String strProcName = ""; + int nPID = 0; + int nProcs = 0; + + if (lProcesses != null) + nProcs = lProcesses.size(); + + for (lcv = 0; lcv < nProcs; lcv++) + { + if (lProcesses.get(lcv).processName.contains(sProcName)) + { + strProcName = lProcesses.get(lcv).processName; + nPID = lProcesses.get(lcv).pid; + sRet = sErrorPrefix + "Failed to kill " + nPID + " " + strProcName + "\n"; + + theArgs[2] += " " + nPID; + + try + { + pProc = Runtime.getRuntime().exec(theArgs); + RedirOutputThread outThrd = new RedirOutputThread(pProc, out); + outThrd.start(); + outThrd.join(5000); + } + catch (IOException e) + { + sRet = e.getMessage(); + e.printStackTrace(); + } + catch (InterruptedException e) + { + e.printStackTrace(); + } + + // Give the messages a chance to be processed + try { + Thread.sleep(2000); + } + catch (InterruptedException e) + { + e.printStackTrace(); + } + break; + } + } + + if (nPID > 0) + { + sRet = "Successfully killed " + nPID + " " + strProcName + "\n"; + lProcesses = aMgr.getRunningAppProcesses(); + nProcs = 0; + if (lProcesses != null) + nProcs = lProcesses.size(); + for (lcv = 0; lcv < nProcs; lcv++) + { + if (lProcesses.get(lcv).processName.contains(sProcName)) + { + sRet = sErrorPrefix + "Unable to kill " + nPID + " " + strProcName + "\n"; + break; + } + } + } + + return (sRet); + } + + public String GetAppRoot(String AppName) + { + String sRet = sErrorPrefix + " internal error [no context]"; + Context ctx = getApplicationContext(); + + if (ctx != null) + { + try { + Context appCtx = ctx.createPackageContext(AppName, 0); + ContextWrapper appCtxW = new ContextWrapper(appCtx); + sRet = appCtxW.getPackageResourcePath(); + appCtxW = null; + appCtx = null; + ctx = null; + System.gc(); + } + catch (NameNotFoundException e) + { + e.printStackTrace(); + } + } + return(sRet); + } + + public boolean IsProcessDead(String sProcName) + { + boolean bRet = true; + ActivityManager aMgr = (ActivityManager) getSystemService(Activity.ACTIVITY_SERVICE); + List <ActivityManager.RunningAppProcessInfo> lProcesses = aMgr.getRunningAppProcesses(); // .getProcessesInErrorState(); + int lcv = 0; + + if (lProcesses != null) + { + for (lcv = 0; lcv < lProcesses.size(); lcv++) + { + if (lProcesses.get(lcv).processName.contentEquals(sProcName)) + { + bRet = false; + break; + } + } + } + + return (bRet); + } + + public String fixFileName(String fileName) + { + String sRet = ""; + String sTmpFileName = ""; + + sRet = fileName.replace('\\', '/'); + + if (sRet.startsWith("/")) + sTmpFileName = sRet; + else + sTmpFileName = currentDir + "/" + sRet; + + sRet = sTmpFileName.replace('\\', '/'); + sTmpFileName = sRet; + sRet = sTmpFileName.replace("//", "/"); + + return(sRet); + } + + public String UnInstallApp(String sApp, OutputStream out) + { + String sRet = ""; + try + { + pProc = Runtime.getRuntime().exec(this.getSuArgs("pm uninstall " + sApp + ";exit")); + + RedirOutputThread outThrd = new RedirOutputThread(pProc, out); + outThrd.start(); + outThrd.join(60000); + int nRet = pProc.exitValue(); + sRet = "\nuninst complete [" + nRet + "]"; + } + catch (IOException e) + { + sRet = e.getMessage(); + e.printStackTrace(); + } + catch (InterruptedException e) + { + e.printStackTrace(); + } + + return (sRet); + } + + private String [] getSuArgs(String cmdString) + { + String [] theArgs = new String [3]; + theArgs[0] = "su"; + theArgs[1] = "-c"; + // as a security measure, ICS and later resets LD_LIBRARY_PATH. reset + // it here when executing the command + theArgs[2] = "LD_LIBRARY_PATH=/vendor/lib:/system/lib " + cmdString; + return theArgs; + } + + public String InstallApp(String sApp, OutputStream out) + { + String sRet = ""; + String sHold = ""; + try + { + pProc = Runtime.getRuntime().exec(this.getSuArgs("pm install -r " + sApp + " Cleanup;exit")); + + RedirOutputThread outThrd = new RedirOutputThread(pProc, out); + outThrd.start(); + outThrd.join(180000); + int nRet = pProc.exitValue(); + sRet += "\ninstall complete [" + nRet + "]"; + sHold = outThrd.strOutput; + sRet += "\nSuccess"; + } + catch (IOException e) + { + sRet = e.getMessage(); + e.printStackTrace(); + } + catch (InterruptedException e) + { + e.printStackTrace(); + } + + return (sRet); + } + + private String SendPing(String sIPAddr) + { + Process pProc; + String sRet = ""; + String [] theArgs = new String [4]; + boolean bStillRunning = true; + int nBytesOut = 0; + int nBytesErr = 0; + int nBytesRead = 0; + byte[] buffer = new byte[1024]; + + theArgs[0] = "ping"; + theArgs[1] = "-c"; + theArgs[2] = "3"; + theArgs[3] = sIPAddr; + Log.i(LOGTAG, "Pinging " + sIPAddr); + + try + { + pProc = Runtime.getRuntime().exec(theArgs); + InputStream sutOut = pProc.getInputStream(); + InputStream sutErr = pProc.getErrorStream(); + + while (bStillRunning) + { + try + { + if ((nBytesOut = sutOut.available()) > 0) + { + if (nBytesOut > buffer.length) + { + buffer = null; + System.gc(); + buffer = new byte[nBytesOut]; + } + nBytesRead = sutOut.read(buffer, 0, nBytesOut); + if (nBytesRead == -1) + bStillRunning = false; + else + { + String sRep = new String(buffer,0,nBytesRead).replace("\n", "\r\n"); + sRet += sRep; + sRep = null; + } + } + + if ((nBytesErr = sutErr.available()) > 0) + { + if (nBytesErr > buffer.length) + { + buffer = null; + System.gc(); + buffer = new byte[nBytesErr]; + } + nBytesRead = sutErr.read(buffer, 0, nBytesErr); + if (nBytesRead == -1) + bStillRunning = false; + else + { + String sRep = new String(buffer,0,nBytesRead).replace("\n", "\r\n"); + sRet += sRep; + sRep = null; + } + } + + bStillRunning = (IsProcRunning(pProc) || (sutOut.available() > 0) || (sutErr.available() > 0)); + } + catch (IOException e) + { + e.printStackTrace(); + } + + if ((bStillRunning == true) && (nBytesErr == 0) && (nBytesOut == 0)) + { + try { + Thread.sleep(2000); + } + catch (InterruptedException e) { + e.printStackTrace(); + } + } + } + + pProc.destroy(); + pProc = null; + } + catch (IOException e) + { + sRet = e.getMessage(); + e.printStackTrace(); + } + + Log.i(LOGTAG, String.format("Ping result was: '%s'", sRet.trim())); + return (sRet); + } + + private boolean IsProcRunning(Process pProc) + { + boolean bRet = false; + @SuppressWarnings("unused") + int nExitCode = 0; + + try + { + nExitCode = pProc.exitValue(); + } + catch (IllegalThreadStateException z) + { + bRet = true; + } + catch (Exception e) + { + e.printStackTrace(); + } + + return(bRet); + } + + private class UpdateApplication implements Runnable { + Thread runner; + String msPkgName = ""; + String msPkgFileName = ""; + String msOutFile = ""; + int mbReboot = 0; + + public UpdateApplication(String sPkgName, String sPkgFileName, String sOutFile, int bReboot) { + runner = new Thread(this); + msPkgName = sPkgName; + msPkgFileName = sPkgFileName; + msOutFile = sOutFile; + mbReboot = bReboot; + runner.start(); + } + + public void run() { + bInstalling = true; + UpdtApp(msPkgName, msPkgFileName, msOutFile, mbReboot); + bInstalling = false; + } + } + + private class MyTime extends TimerTask + { + int nStrikes = 0; + final int PERIODS_TO_WAIT_FOR_SDCARD = 3; + int nPeriodsWaited = 0; + + public MyTime() + { + } + + @Override + public void run() + { + if (bInstalling) + return; + + // See if the network is up, if not reboot after a configurable + // number of tries + if (nMaxStrikes > 0) + { + String sRet = SendPing(sPingTarget); + if (!sRet.contains("3 received")) + { + Log.i(LOGTAG, String.format("Failed ping attempt (remaining: %s)!", + nMaxStrikes - nStrikes)); + if (++nStrikes >= nMaxStrikes) + { + Log.e(LOGTAG, String.format("Number of failed ping attempts to %s (%s) exceeded maximum (%s), running reboot!", sPingTarget, nStrikes, nMaxStrikes)); + RunReboot(null); + } + } + else + { + nStrikes = 0; + } + } + + String sProgramName = "com.mozilla.SUTAgentAndroid"; + + // Ensure the sdcard is mounted before we even attempt to start the agent + // We will wait for the sdcard to mount for PERIODS_TO_WAIT_FOR_SDCARD + // after which time we go ahead and attempt to start the agent. + if (nPeriodsWaited++ < PERIODS_TO_WAIT_FOR_SDCARD) { + String state = Environment.getExternalStorageState(); + if (Environment.MEDIA_MOUNTED.compareTo(state) != 0) { + Log.i(LOGTAG, "SDcard not mounted, waiting another turn"); + return; + } else if (Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) { + Log.e(LOGTAG, "SDcard mounted read only not starting agent now, try again in 60s"); + return; + } + } + + boolean isProc = GetProcessInfo(sProgramName); + if (bStartSUTAgent && !isProc) + { + Log.i(LOGTAG, "Starting SUTAgent from watcher code"); + Intent agentIntent = new Intent(); + agentIntent.setPackage(sProgramName); + agentIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + agentIntent.setAction(Intent.ACTION_MAIN); + try { + PackageManager pm = myContext.getPackageManager(); + PackageInfo pi = pm.getPackageInfo(sProgramName, PackageManager.GET_ACTIVITIES | PackageManager.GET_INTENT_FILTERS); + ActivityInfo [] ai = pi.activities; + for (int i = 0; i < ai.length; i++) + { + ActivityInfo a = ai[i]; + if (a.name.length() > 0) + { + agentIntent.setClassName(a.packageName, a.name); + break; + } + } + } + catch (NameNotFoundException e) + { + e.printStackTrace(); + } + try + { + myContext.startActivity(agentIntent); + } + catch(ActivityNotFoundException anf) + { + anf.printStackTrace(); + } + } + } + } +} diff --git a/build/mobile/sutagent/android/watcher/default.properties b/build/mobile/sutagent/android/watcher/default.properties new file mode 100644 index 000000000..07bdb5414 --- /dev/null +++ b/build/mobile/sutagent/android/watcher/default.properties @@ -0,0 +1,15 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +# This file is automatically generated by Android Tools. +# Do not modify this file -- YOUR CHANGES WILL BE ERASED! +# +# This file must be checked in Version Control Systems. +# +# To customize properties used by the Ant build system use, +# "build.properties", and override values to adapt the script to your +# project structure. + +# Project target. +target=android-5 diff --git a/build/mobile/sutagent/android/watcher/moz.build b/build/mobile/sutagent/android/watcher/moz.build new file mode 100644 index 000000000..d45d52c91 --- /dev/null +++ b/build/mobile/sutagent/android/watcher/moz.build @@ -0,0 +1,8 @@ +# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +MODULE = 'Watcher' + diff --git a/build/mobile/sutagent/android/watcher/res/drawable-hdpi/ateamlogo.png b/build/mobile/sutagent/android/watcher/res/drawable-hdpi/ateamlogo.png Binary files differnew file mode 100644 index 000000000..6f65bd6e9 --- /dev/null +++ b/build/mobile/sutagent/android/watcher/res/drawable-hdpi/ateamlogo.png diff --git a/build/mobile/sutagent/android/watcher/res/drawable-hdpi/icon.png b/build/mobile/sutagent/android/watcher/res/drawable-hdpi/icon.png Binary files differnew file mode 100644 index 000000000..8074c4c57 --- /dev/null +++ b/build/mobile/sutagent/android/watcher/res/drawable-hdpi/icon.png diff --git a/build/mobile/sutagent/android/watcher/res/drawable-ldpi/ateamlogo.png b/build/mobile/sutagent/android/watcher/res/drawable-ldpi/ateamlogo.png Binary files differnew file mode 100644 index 000000000..6f65bd6e9 --- /dev/null +++ b/build/mobile/sutagent/android/watcher/res/drawable-ldpi/ateamlogo.png diff --git a/build/mobile/sutagent/android/watcher/res/drawable-ldpi/icon.png b/build/mobile/sutagent/android/watcher/res/drawable-ldpi/icon.png Binary files differnew file mode 100644 index 000000000..1095584ec --- /dev/null +++ b/build/mobile/sutagent/android/watcher/res/drawable-ldpi/icon.png diff --git a/build/mobile/sutagent/android/watcher/res/drawable-mdpi/ateamlogo.png b/build/mobile/sutagent/android/watcher/res/drawable-mdpi/ateamlogo.png Binary files differnew file mode 100644 index 000000000..6f65bd6e9 --- /dev/null +++ b/build/mobile/sutagent/android/watcher/res/drawable-mdpi/ateamlogo.png diff --git a/build/mobile/sutagent/android/watcher/res/drawable-mdpi/icon.png b/build/mobile/sutagent/android/watcher/res/drawable-mdpi/icon.png Binary files differnew file mode 100644 index 000000000..a07c69fa5 --- /dev/null +++ b/build/mobile/sutagent/android/watcher/res/drawable-mdpi/icon.png diff --git a/build/mobile/sutagent/android/watcher/res/layout/main.xml b/build/mobile/sutagent/android/watcher/res/layout/main.xml new file mode 100644 index 000000000..1256b1a7c --- /dev/null +++ b/build/mobile/sutagent/android/watcher/res/layout/main.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- This Source Code Form is subject to the terms of the Mozilla Public + - License, v. 2.0. If a copy of the MPL was not distributed with this + - file, You can obtain one at http://mozilla.org/MPL/2.0/. --> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="vertical" + android:layout_width="fill_parent" + android:layout_height="fill_parent" + > +<TextView + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:text="@string/hello" + /> +</LinearLayout> diff --git a/build/mobile/sutagent/android/watcher/res/values/strings.xml b/build/mobile/sutagent/android/watcher/res/values/strings.xml new file mode 100644 index 000000000..a2190f7c0 --- /dev/null +++ b/build/mobile/sutagent/android/watcher/res/values/strings.xml @@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- This Source Code Form is subject to the terms of the Mozilla Public + - License, v. 2.0. If a copy of the MPL was not distributed with this + - file, You can obtain one at http://mozilla.org/MPL/2.0/. --> + +<resources> + <string name="hello">Hello World, WatcherMain!</string> + <string name="app_name">watcher</string> +<string name="foreground_service_started">Foreground Service Started</string> + +</resources> diff --git a/build/moz.build b/build/moz.build new file mode 100644 index 000000000..f873ef09b --- /dev/null +++ b/build/moz.build @@ -0,0 +1,25 @@ +# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +if CONFIG['OS_ARCH'] not in ('WINNT', 'OS2'): + DIRS += ['unix'] +elif CONFIG['OS_ARCH'] == 'WINNT': + DIRS += ['win32'] + +if CONFIG['STLPORT_SOURCES']: + DIRS += ['stlport'] + +if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'android': + TEST_DIRS += [ + 'mobile/sutagent/android', + 'mobile/sutagent/android/watcher', + 'mobile/sutagent/android/ffxcp', + 'mobile/sutagent/android/fencp', + 'mobile/robocop', + ] + +MODULE = 'build' + diff --git a/build/mozconfig.common b/build/mozconfig.common new file mode 100644 index 000000000..e329737d3 --- /dev/null +++ b/build/mozconfig.common @@ -0,0 +1,13 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +# Common mozconfig for all users +# +# Add options to this file that will be inherited by all in-tree mozconfigs. +# This is useful for eg try builds with nondefault options that apply to all +# architectures, though note that if you want to override options set in +# another mozconfig file, you'll need to use mozconfig.common.override instead +# of this file. + +mk_add_options AUTOCLOBBER=1 diff --git a/build/mozconfig.common.override b/build/mozconfig.common.override new file mode 100644 index 000000000..7285aa9dd --- /dev/null +++ b/build/mozconfig.common.override @@ -0,0 +1,11 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +# Common mozconfig for all users +# +# Add options to this file that will be inherited by all in-tree mozconfigs. +# This file is included at the *end* of the mozconfigs, and so may be used +# to override anything done previously. +# +# The common expected usage is for try builds with nondefault options. diff --git a/build/mozconfig.vs2010-common b/build/mozconfig.vs2010-common new file mode 100644 index 000000000..9806ae242 --- /dev/null +++ b/build/mozconfig.vs2010-common @@ -0,0 +1,8 @@ +# Pymake needs Windows-style paths. Use cmd.exe to hack around this.
+mk_export_correct_style() {
+ if test -n "${_PYMAKE}"; then
+ mk_add_options "export $1=$(cmd.exe //c echo %$1%)"
+ else
+ mk_add_options "export $1=$(eval echo \$$1)"
+ fi
+}
diff --git a/build/msys-perl-wrapper b/build/msys-perl-wrapper new file mode 100644 index 000000000..8ba78ed62 --- /dev/null +++ b/build/msys-perl-wrapper @@ -0,0 +1,20 @@ +#!/bin/sh +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + + +args="" + +for i in "${@}" +do + case "$i" in + -I?:/*) + i="$(echo "${i}" | sed -e 's|^-I\(.\):/|-I/\1/|')" + ;; + esac + + args="${args} '${i}'" +done + +eval "exec perl $args" diff --git a/build/os2/test_os2.cmd b/build/os2/test_os2.cmd new file mode 100644 index 000000000..389dea091 --- /dev/null +++ b/build/os2/test_os2.cmd @@ -0,0 +1,21 @@ +REM This Source Code Form is subject to the terms of the Mozilla Public +REM License, v. 2.0. If a copy of the MPL was not distributed with this +REM file, You can obtain one at http://mozilla.org/MPL/2.0/. + +/* Invoke unit tests on OS/2 */ +PARSE ARG dist prog parm +dist=forwardtoback(dist); +prog=forwardtoback(prog); +'set BEGINLIBPATH='dist'\bin;%BEGINLIBPATH%' +'set LIBPATHSTRICT=T' +prog parm +exit + +forwardtoback: procedure + arg pathname + parse var pathname pathname'/'rest + do while (rest <> "") + pathname = pathname'\'rest + parse var pathname pathname'/'rest + end + return pathname diff --git a/build/package/Makefile.in b/build/package/Makefile.in new file mode 100644 index 000000000..163fab806 --- /dev/null +++ b/build/package/Makefile.in @@ -0,0 +1,77 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +DEPTH = @DEPTH@ +topsrcdir = @top_srcdir@ +srcdir = @srcdir@ +VPATH = @srcdir@ + +include $(DEPTH)/config/autoconf.mk + +# This makefile doesn't do anything by default. Use one of the special targets +# below to stage and build packages. + +include $(topsrcdir)/config/rules.mk + +ifdef MOZ_DEBUG +APP_NAME = MozillaDebug +else +APP_NAME = Mozilla +endif + +# override these variables in special situations +STAGE_DIR = $(DEPTH)/stage$(if $*,/$*) +STAGE_MACAPP_DIR = $(DEPTH)/stage$(if $*,/$*)/$(APP_NAME).app +XPI_FILE = $(DIST)/xpi/$*.xpi +ZIP_FILE = $(DIST)/$*.zip +TGZ_FILE = $(DIST)/$*.tar.gz +BZ2_FILE = $(DIST)/$*.tar.bz2 +DMG_FILE = $(DIST)/$*.dmg + +PACKAGE_LIST = $(srcdir)/packages.list +PACKAGE_FLAGS = +HANDLERS = xptmerge touch optional preprocess exec +MAPPINGS = dist/bin=bin xpiroot/= + +MACAPP_MAPPINGS = dist/bin=Contents/MacOS approot=Contents + +dump-packages: + @$(PERL) $(srcdir)/dump-packages.pl -o $(DEPTH) -l $(PACKAGE_LIST) -u $(PACKAGE_FLAGS) + +# This rule will stage any one package. If you need to stage multiple packages or +# do ^packages (subtraction packages) use the stage-packages rule and set PACKAGES +# on the command line +stage-%: + $(PERL) $(srcdir)/stage-packages.pl -o $(DEPTH) -l $(PACKAGE_LIST) -s $(STAGE_DIR) $(addprefix -m ,$(MAPPINGS)) $(addprefix -c ,$(HANDLERS)) -d $(PACKAGE_FLAGS) $* + +stagemacapp-%: + $(PERL) $(srcdir)/stage-packages.pl -o $(DEPTH) -l $(PACKAGE_LIST) -s $(STAGE_MACAPP_DIR) $(addprefix -m ,$(MACAPP_MAPPINGS)) $(addprefix -c ,$(HANDLERS)) -d $(PACKAGE_FLAGS) $* + +makexpi-%: + $(PERL) $(srcdir)/stage-packages.pl -o $(DEPTH) -l $(PACKAGE_LIST) -s $(STAGE_DIR) $(addprefix -m ,$(MAPPINGS)) $(addprefix -c ,$(HANDLERS)) --make-package xpi=$(XPI_FILE) -d $(PACKAGE_FLAGS) $* + +makezip-%: + $(PERL) $(srcdir)/stage-packages.pl -o $(DEPTH) -l $(PACKAGE_LIST) -s $(STAGE_DIR) $(addprefix -m ,$(MAPPINGS)) $(addprefix -c ,$(HANDLERS)) --make-package zip=$(ZIP_FILE) -d $(PACKAGE_FLAGS) $* + +maketgz-%: + $(PERL) $(srcdir)/stage-packages.pl -o $(DEPTH) -l $(PACKAGE_LIST) -s $(STAGE_DIR) $(addprefix -m ,$(MAPPINGS)) $(addprefix -c ,$(HANDLERS)) --make-package tgz=$(TGZ_FILE) -d $(PACKAGE_FLAGS) $* + +makebz2-%: + $(PERL) $(srcdir)/stage-packages.pl -o $(DEPTH) -l $(PACKAGE_LIST) -s $(STAGE_DIR) $(addprefix -m ,$(MAPPINGS)) $(addprefix -c ,$(HANDLERS)) --make-package bz2=$(BZ2_FILE) -d $(PACKAGE_FLAGS) $* + +makedmg-%: + $(PERL) $(srcdir)/stage-packages.pl -o $(DEPTH) -l $(PACKAGE_LIST) -s $(STAGE_DIR) $(addprefix -m ,$(MACAPP_MAPPINGS)) $(addprefix -c ,$(HANDLERS)) --make-package dmg=$(DMG_FILE) -d $(PACKAGE_FLAGS) $* + +ifndef PACKAGES +stage-packages makexpi-packages: + @echo "Please set PACKAGES on the makefile command line or environment." + +else #PACKAGES +stage-packages: + $(PERL) $(srcdir)/stage-packages.pl -o $(DEPTH) -l $(PACKAGE_LIST) -s $(STAGE_DIR) $(addprefix -m ,$(MAPPINGS)) $(apprefix -c ,$(HANDLERS)) $(PACKAGE_FLAGS) $(PACKAGES) + +makexpi-packages: + $(PERL) $(srcdir)/stage-packages.pl -o $(DEPTH) -l $(PACKAGE_LIST) -s $(STAGE_DIR) $(addprefix -m ,$(MAPPINGS)) $(apprefix -c ,$(HANDLERS)) --make-package xpi=$(XPI_FILE) -d $(PACKAGE_FLAGS) $(PACKAGES) + +endif diff --git a/build/package/mac_osx/make-diskimage b/build/package/mac_osx/make-diskimage new file mode 100644 index 000000000..c214ceb59 --- /dev/null +++ b/build/package/mac_osx/make-diskimage @@ -0,0 +1,47 @@ +#!/bin/sh +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +# Create a read-only disk image of the contents of a folder +# +# Usage: make-diskimage <image_file> +# <src_folder> +# <volume_name> +# <eula_resource_file> +# <.dsstore_file> +# <background_image_file> +# +# tip: use '-null-' for <eula-resource-file> if you only want to +# provide <.dsstore_file> and <background_image_file> + +DMG_PATH=$1 +SRC_FOLDER=$2 +VOLUME_NAME=$3 + +# optional arguments +EULA_RSRC=$4 +DMG_DSSTORE=$5 +DMG_BKGND_IMG=$6 + +EXTRA_ARGS= + +if test -n "$EULA_RSRC" && test "$EULA_RSRC" != "-null-" ; then + EXTRA_ARGS="--resource $EULA_RSRC" +fi + +if test -n "$DMG_DSSTORE" ; then + EXTRA_ARGS="$EXTRA_ARGS --copy $DMG_DSSTORE:/.DS_Store" +fi + +if test -n "$DMG_BKGND_IMG" ; then + EXTRA_ARGS="$EXTRA_ARGS --mkdir /.background --copy $DMG_BKGND_IMG:/.background" +fi + +echo `dirname $0`/pkg-dmg --target "$DMG_PATH" --source "$SRC_FOLDER" \ + --volname "$VOLUME_NAME" $EXTRA_ARGS + +`dirname $0`/pkg-dmg --target "$DMG_PATH" --source "$SRC_FOLDER" \ + --volname "$VOLUME_NAME" $EXTRA_ARGS + +exit $? diff --git a/build/package/mac_osx/mozilla-background.jpg b/build/package/mac_osx/mozilla-background.jpg Binary files differnew file mode 100644 index 000000000..adb4df036 --- /dev/null +++ b/build/package/mac_osx/mozilla-background.jpg diff --git a/build/package/mac_osx/mozilla.dsstore b/build/package/mac_osx/mozilla.dsstore Binary files differnew file mode 100644 index 000000000..520eb08d6 --- /dev/null +++ b/build/package/mac_osx/mozilla.dsstore diff --git a/build/package/mac_osx/pkg-dmg b/build/package/mac_osx/pkg-dmg new file mode 100644 index 000000000..9e8728a1a --- /dev/null +++ b/build/package/mac_osx/pkg-dmg @@ -0,0 +1,1488 @@ +#!/usr/bin/perl +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +use strict; +use warnings; + +=pod + +=head1 NAME + +B<pkg-dmg> - Mac OS X disk image (.dmg) packager + +=head1 SYNOPSIS + +B<pkg-dmg> +B<--source> I<source-folder> +B<--target> I<target-image> +[B<--format> I<format>] +[B<--volname> I<volume-name>] +[B<--tempdir> I<temp-dir>] +[B<--mkdir> I<directory>] +[B<--copy> I<source>[:I<dest>]] +[B<--symlink> I<source>[:I<dest>]] +[B<--license> I<file>] +[B<--resource> I<file>] +[B<--icon> I<icns-file>] +[B<--attribute> I<a>:I<file>[:I<file>...] +[B<--idme>] +[B<--sourcefile>] +[B<--verbosity> I<level>] +[B<--dry-run>] + +=head1 DESCRIPTION + +I<pkg-dmg> takes a directory identified by I<source-folder> and transforms +it into a disk image stored as I<target-image>. The disk image will +occupy the least space possible for its format, or the least space that the +authors have been able to figure out how to achieve. + +=head1 OPTIONS + +=over 5 + +==item B<--source> I<source-folder> + +Identifies the directory that will be packaged up. This directory is not +touched, a copy will be made in a temporary directory for staging purposes. +See B<--tempdir>. + +==item B<--target> I<target-image> + +The disk image to create. If it exists and is not in use, it will be +overwritten. If I<target-image> already contains a suitable extension, +it will be used unmodified. If no extension is present, or the extension +is incorrect for the selected format, the proper extension will be added. +See B<--format>. + +==item B<--format> I<format> + +The format to create the disk image in. Valid values for I<format> are: + - UDZO - zlib-compressed, read-only; extension I<.dmg> + - UDBZ - bzip2-compressed, read-only; extension I<.dmg>; + create and use on 10.4 ("Tiger") and later only + - UDRW - read-write; extension I<.dmg> + - UDSP - read-write, sparse; extension I<.sparseimage> + +UDBZ is the default format. + +See L<hdiutil(1)> for a description of these formats. + +=item B<--volname> I<volume-name> + +The name of the volume in the disk image. If not specified, I<volume-name> +defaults to the name of the source directory from B<--source>. + +=item B<--tempdir> I<temp-dir> + +A temporary directory to stage intermediate files in. I<temp-dir> must +have enough space available to accommodate twice the size of the files +being packaged. If not specified, defaults to the same directory that +the I<target-image> is to be placed in. B<pkg-dmg> will remove any +temporary files it places in I<temp-dir>. + +=item B<--mkdir> I<directory> + +Specifies a directory that should be created in the disk image. +I<directory> and any ancestor directories will be created. This is +useful in conjunction with B<--copy>, when copying files to directories +that may not exist in I<source-folder>. B<--mkdir> may appear multiple +times. + +=item B<--copy> I<source>[:I<dest>] + +Additional files to copy into the disk image. If I<dest> is +specified, I<source> is copied to the location I<dest> identifies, +otherwise, I<source> is copied to the root of the new volume. B<--copy> +provides a way to package up a I<source-folder> by adding files to it +without modifying the original I<source-folder>. B<--copy> may appear +multiple times. + +This option is useful for adding .DS_Store files and window backgrounds +to disk images. + +=item B<--symlink> I<source>[:I<dest>] + +Like B<--copy>, but allows symlinks to point out of the volume. Empty symlink +destinations are interpreted as "like the source path, but inside the dmg" + +This option is useful for adding symlinks to external resources, +e.g. to /Applications. + +=item B<--license> I<file> + +A plain text file containing a license agreement to be displayed before +the disk image is mounted. English is the only supported language. To +include license agreements in other languages, in multiple languages, +or to use formatted text, prepare a resource and use L<--resource>. + +=item B<--resource> I<file> + +A resource file to merge into I<target-image>. If I<format> is UDZO or +UDBZ, the disk image will be flattened to a single-fork file that contains +the resource but may be freely transferred without any special encodings. +I<file> must be in a format suitable for L<Rez(1)>. See L<Rez(1)> for a +description of the format, and L<hdiutil(1)> for a discussion on flattened +disk images. B<--resource> may appear multiple times. + +This option is useful for adding license agreements and other messages +to disk images. + +=item B<--icon> I<icns-file> + +Specifies an I<icns> file that will be used as the icon for the root of +the volume. This file will be copied to the new volume and the custom +icon attribute will be set on the root folder. + +=item B<--attribute> I<a>:I<file>[:I<file>...] + +Sets the attributes of I<file> to the attribute list in I<a>. See +L<SetFile(1)> + +=item B<--idme> + +Enable IDME to make the disk image "Internet-enabled." The first time +the image is mounted, if IDME processing is enabled on the system, the +contents of the image will be copied out of the image and the image will +be placed in the trash with IDME disabled. + +=item B<--sourcefile> + +If this option is present, I<source-folder> is treated as a file, and is +placed as a file within the volume's root folder. Without this option, +I<source-folder> is treated as the volume root itself. + +=item B<--verbosity> I<level> + +Adjusts the level of loudness of B<pkg-dmg>. The possible values for +I<level> are: + 0 - Only error messages are displayed. + 1 - Print error messages and command invocations. + 2 - Print everything, including command output. + +The default I<level> is 2. + +=item B<--dry-run> + +When specified, the commands that would be executed are printed, without +actually executing them. When commands depend on the output of previous +commands, dummy values are displayed. + +=back + +=head1 NON-OPTIONS + +=over 5 + +=item + +Resource forks aren't copied. + +=item + +The root folder of the created volume is designated as the folder +to open when the volume is mounted. See L<bless(8)>. + +=item + +All files in the volume are set to be world-readable, only writable +by the owner, and world-executable when appropriate. All other +permissions bits are cleared. + +=item + +When possible, disk images are created without any partition tables. This +is what L<hdiutil(1)> refers to as I<-layout NONE>, and saves a handful of +kilobytes. The alternative, I<SPUD>, contains a partition table that +is not terribly handy on disk images that are not intended to represent any +physical disk. + +=item + +Read-write images are created with journaling off. Any read-write image +created by this tool is expected to be transient, and the goal of this tool +is to create images which consume a minimum of space. + +=back + +=head1 EXAMPLE + +pkg-dmg --source /Applications/DeerPark.app --target ~/DeerPark.dmg + --sourcefile --volname DeerPark --icon ~/DeerPark.icns + --mkdir /.background + --copy DeerParkBackground.png:/.background/background.png + --copy DeerParkDSStore:/.DS_Store + --symlink /Applications:"/Drag to here" + +=head1 REQUIREMENTS + +I<pkg-dmg> has been tested with Mac OS X releases 10.2 ("Jaguar") +through 10.4 ("Tiger"). Certain adjustments to behavior are made +depending on the host system's release. Mac OS X 10.3 ("Panther") or +later are recommended. + +=head1 LICENSE + +MPL 1.1/GPL 2.0/LGPL 2.1. Your choice. + +=head1 AUTHOR + +Mark Mentovai + +=head1 SEE ALSO + +L<bless(8)>, L<diskutil(8)>, L<hdid(8)>, L<hdiutil(1)>, L<Rez(1)>, +L<rsync(1)>, L<SetFile(1)> + +=cut + +use Fcntl; +use POSIX; +use Getopt::Long; + +sub argumentEscape(@); +sub cleanupDie($); +sub command(@); +sub commandInternal($@); +sub commandInternalVerbosity($$@); +sub commandOutput(@); +sub commandOutputVerbosity($@); +sub commandVerbosity($@); +sub copyFiles($@); +sub diskImageMaker($$$$$$$$); +sub giveExtension($$); +sub hdidMountImage($@); +sub isFormatCompressed($); +sub licenseMaker($$); +sub pathSplit($); +sub setAttributes($@); +sub trapSignal($); +sub usage(); + +# Variables used as globals +my(@gCleanup, %gConfig, $gDarwinMajor, $gDryRun, $gVerbosity); + +# Use the commands by name if they're expected to be in the user's +# $PATH (/bin:/sbin:/usr/bin:/usr/sbin). Otherwise, go by absolute +# path. These may be overridden with --config. +%gConfig = ('cmd_bless' => 'bless', + 'cmd_chmod' => 'chmod', + 'cmd_diskutil' => 'diskutil', + 'cmd_du' => 'du', + 'cmd_hdid' => 'hdid', + 'cmd_hdiutil' => 'hdiutil', + 'cmd_mkdir' => 'mkdir', + 'cmd_mktemp' => 'mktemp', + 'cmd_Rez' => 'Rez', + 'cmd_rm' => 'rm', + 'cmd_rsync' => 'rsync', + 'cmd_SetFile' => 'SetFile', + + # create_directly indicates whether hdiutil create supports + # -srcfolder and -srcdevice. It does on >= 10.3 (Panther). + # This is fixed up for earlier systems below. If false, + # hdiutil create is used to create empty disk images that + # are manually filled. + 'create_directly' => 1, + + # If hdiutil attach -mountpoint exists, use it to avoid + # mounting disk images in the default /Volumes. This reduces + # the likelihood that someone will notice a mounted image and + # interfere with it. Only available on >= 10.3 (Panther), + # fixed up for earlier systems below. + # + # This is presently turned off for all systems, because there + # is an infrequent synchronization problem during ejection. + # diskutil eject might return before the image is actually + # unmounted. If pkg-dmg then attempts to clean up its + # temporary directory, it could remove items from a read-write + # disk image or attempt to remove items from a read-only disk + # image (or a read-only item from a read-write image) and fail, + # causing pkg-dmg to abort. This problem is experienced + # under Tiger, which appears to eject asynchronously where + # previous systems treated it as a synchronous operation. + # Using hdiutil attach -mountpoint didn't always keep images + # from showing up on the desktop anyway. + 'hdiutil_mountpoint' => 0, + + # hdiutil makehybrid results in optimized disk images that + # consume less space and mount more quickly. Use it when + # it's available, but that's only on >= 10.3 (Panther). + # If false, hdiutil create is used instead. Fixed up for + # earlier systems below. + 'makehybrid' => 1, + + # hdiutil create doesn't allow specifying a folder to open + # at volume mount time, so those images are mounted and + # their root folders made holy with bless -openfolder. But + # only on >= 10.3 (Panther). Earlier systems are out of luck. + # Even on Panther, bless refuses to run unless root. + # Fixed up below. + 'openfolder_bless' => 1, + + # It's possible to save a few more kilobytes by including the + # partition only without any partition table in the image. + # This is a good idea on any system, so turn this option off. + # + # Except it's buggy. "-layout NONE" seems to be creating + # disk images with more data than just the partition table + # stripped out. You might wind up losing the end of the + # filesystem - the last file (or several) might be incomplete. + 'partition_table' => 1, + + # To create a partition table-less image from something + # created by makehybrid, the hybrid image needs to be + # mounted and a new image made from the device associated + # with the relevant partition. This requires >= 10.4 + # (Tiger), presumably because earlier systems have + # problems creating images from devices themselves attached + # to images. If this is false, makehybrid images will + # have partition tables, regardless of the partition_table + # setting. Fixed up for earlier systems below. + 'recursive_access' => 1); + +# --verbosity +$gVerbosity = 2; + +# --dry-run +$gDryRun = 0; + +# %gConfig fix-ups based on features and bugs present in certain releases. +my($ignore, $uname_r, $uname_s); +($uname_s, $ignore, $uname_r, $ignore, $ignore) = POSIX::uname(); +if($uname_s eq 'Darwin') { + ($gDarwinMajor, $ignore) = split(/\./, $uname_r, 2); + + # $major is the Darwin major release, which for our purposes, is 4 higher + # than the interesting digit in a Mac OS X release. + if($gDarwinMajor <= 6) { + # <= 10.2 (Jaguar) + # hdiutil create does not support -srcfolder or -srcdevice + $gConfig{'create_directly'} = 0; + # hdiutil attach does not support -mountpoint + $gConfig{'hdiutil_mountpoint'} = 0; + # hdiutil mkhybrid does not exist + $gConfig{'makehybrid'} = 0; + } + if($gDarwinMajor <= 7) { + # <= 10.3 (Panther) + # Can't mount a disk image and then make a disk image from the device + $gConfig{'recursive_access'} = 0; + # bless does not support -openfolder on 10.2 (Jaguar) and must run + # as root under 10.3 (Panther) + $gConfig{'openfolder_bless'} = 0; + } +} +else { + # If it's not Mac OS X, just assume all of those good features are + # available. They're not, but things will fail long before they + # have a chance to make a difference. + # + # Now, if someone wanted to document some of these private formats... + print STDERR ($0.": warning, not running on Mac OS X, ". + "this could be interesting.\n"); +} + +# Non-global variables used in Getopt +my(@attributes, @copyFiles, @createSymlinks, $iconFile, $idme, $licenseFile, + @makeDirs, $outputFormat, @resourceFiles, $sourceFile, $sourceFolder, + $targetImage, $tempDir, $volumeName); + +# --format +$outputFormat = 'UDBZ'; + +# --idme +$idme = 0; + +# --sourcefile +$sourceFile = 0; + +# Leaving this might screw up the Apple tools. +delete $ENV{'NEXT_ROOT'}; + +# This script can get pretty messy, so trap a few signals. +$SIG{'INT'} = \&trapSignal; +$SIG{'HUP'} = \&trapSignal; +$SIG{'TERM'} = \&trapSignal; + +Getopt::Long::Configure('pass_through'); +GetOptions('source=s' => \$sourceFolder, + 'target=s' => \$targetImage, + 'volname=s' => \$volumeName, + 'format=s' => \$outputFormat, + 'tempdir=s' => \$tempDir, + 'mkdir=s' => \@makeDirs, + 'copy=s' => \@copyFiles, + 'symlink=s' => \@createSymlinks, + 'license=s' => \$licenseFile, + 'resource=s' => \@resourceFiles, + 'icon=s' => \$iconFile, + 'attribute=s' => \@attributes, + 'idme' => \$idme, + 'sourcefile' => \$sourceFile, + 'verbosity=i' => \$gVerbosity, + 'dry-run' => \$gDryRun, + 'config=s' => \%gConfig); # "hidden" option not in usage() + +if(@ARGV) { + # All arguments are parsed by Getopt + usage(); + exit(1); +} + +if($gVerbosity<0 || $gVerbosity>2) { + usage(); + exit(1); +} + +if(!defined($sourceFolder) || $sourceFolder eq '' || + !defined($targetImage) || $targetImage eq '') { + # --source and --target are required arguments + usage(); + exit(1); +} + +# Make sure $sourceFolder doesn't contain trailing slashes. It messes with +# rsync. +while(substr($sourceFolder, -1) eq '/') { + chop($sourceFolder); +} + +if(!defined($volumeName)) { + # Default volumeName is the name of the source directory. + my(@components); + @components = pathSplit($sourceFolder); + $volumeName = pop(@components); +} + +my(@tempDirComponents, $targetImageFilename); +@tempDirComponents = pathSplit($targetImage); +$targetImageFilename = pop(@tempDirComponents); + +if(defined($tempDir)) { + @tempDirComponents = pathSplit($tempDir); +} +else { + # Default tempDir is the same directory as what is specified for + # targetImage + $tempDir = join('/', @tempDirComponents); +} + +# Ensure that the path of the target image has a suitable extension. If +# it didn't, hdiutil would add one, and we wouldn't be able to find the +# file. +# +# Note that $targetImageFilename is not being reset. This is because it's +# used to build other names below, and we don't need to be adding all sorts +# of extra unnecessary extensions to the name. +my($originalTargetImage, $requiredExtension); +$originalTargetImage = $targetImage; +if($outputFormat eq 'UDSP') { + $requiredExtension = '.sparseimage'; +} +else { + $requiredExtension = '.dmg'; +} +$targetImage = giveExtension($originalTargetImage, $requiredExtension); + +if($targetImage ne $originalTargetImage) { + print STDERR ($0.": warning: target image extension is being added\n"); + print STDERR (' The new filename is '. + giveExtension($targetImageFilename,$requiredExtension)."\n"); +} + +# Make a temporary directory in $tempDir for our own nefarious purposes. +my(@output, $tempSubdir, $tempSubdirTemplate); +$tempSubdirTemplate=join('/', @tempDirComponents, + 'pkg-dmg.'.$$.'.XXXXXXXX'); +if(!(@output = commandOutput($gConfig{'cmd_mktemp'}, '-d', + $tempSubdirTemplate)) || $#output != 0) { + cleanupDie('mktemp failed'); +} + +if($gDryRun) { + (@output)=($tempSubdirTemplate); +} + +($tempSubdir) = @output; + +push(@gCleanup, + sub {commandVerbosity(0, $gConfig{'cmd_rm'}, '-rf', $tempSubdir);}); + +my($tempMount, $tempRoot, @tempsToMake); +$tempRoot = $tempSubdir.'/stage'; +$tempMount = $tempSubdir.'/mount'; +push(@tempsToMake, $tempRoot); +if($gConfig{'hdiutil_mountpoint'}) { + push(@tempsToMake, $tempMount); +} + +if(command($gConfig{'cmd_mkdir'}, @tempsToMake) != 0) { + cleanupDie('mkdir tempRoot/tempMount failed'); +} + +# This cleanup object is not strictly necessary, because $tempRoot is inside +# of $tempSubdir, but the rest of the script relies on this object being +# on the cleanup stack and expects to remove it. +push(@gCleanup, + sub {commandVerbosity(0, $gConfig{'cmd_rm'}, '-rf', $tempRoot);}); + +# If $sourceFile is true, it means that $sourceFolder is to be treated as +# a file and placed as a file within the volume root, as opposed to being +# treated as the volume root itself. rsync will do this by default, if no +# trailing '/' is present. With a trailing '/', $sourceFolder becomes +# $tempRoot, instead of becoming an entry in $tempRoot. +if(command($gConfig{'cmd_rsync'}, '-a', '--copy-unsafe-links', + $sourceFolder.($sourceFile?'':'/'),$tempRoot) != 0) { + cleanupDie('rsync failed'); +} + +if(@makeDirs) { + my($makeDir, @tempDirsToMake); + foreach $makeDir (@makeDirs) { + if($makeDir =~ /^\//) { + push(@tempDirsToMake, $tempRoot.$makeDir); + } + else { + push(@tempDirsToMake, $tempRoot.'/'.$makeDir); + } + } + if(command($gConfig{'cmd_mkdir'}, '-p', @tempDirsToMake) != 0) { + cleanupDie('mkdir failed'); + } +} + +# copy files and/or create symlinks +copyFiles($tempRoot, 'copy', @copyFiles); +copyFiles($tempRoot, 'symlink', @createSymlinks); + +if($gConfig{'create_directly'}) { + # If create_directly is false, the contents will be rsynced into a + # disk image and they would lose their attributes. + setAttributes($tempRoot, @attributes); +} + +if(defined($iconFile)) { + if(command($gConfig{'cmd_rsync'}, '-a', '--copy-unsafe-links', $iconFile, + $tempRoot.'/.VolumeIcon.icns') != 0) { + cleanupDie('rsync failed for volume icon'); + } + + # It's pointless to set the attributes of the root when diskutil create + # -srcfolder is being used. In that case, the attributes will be set + # later, after the image is already created. + if(isFormatCompressed($outputFormat) && + (command($gConfig{'cmd_SetFile'}, '-a', 'C', $tempRoot) != 0)) { + cleanupDie('SetFile failed'); + } +} + +if(command($gConfig{'cmd_chmod'}, '-R', 'a+rX,a-st,u+w,go-w', + $tempRoot) != 0) { + cleanupDie('chmod failed'); +} + +my($unflattenable); +if(isFormatCompressed($outputFormat)) { + $unflattenable = 1; +} +else { + $unflattenable = 0; +} + +diskImageMaker($tempRoot, $targetImage, $outputFormat, $volumeName, + $tempSubdir, $tempMount, $targetImageFilename, defined($iconFile)); + +if(defined($licenseFile) && $licenseFile ne '') { + my($licenseResource); + $licenseResource = $tempSubdir.'/license.r'; + if(!licenseMaker($licenseFile, $licenseResource)) { + cleanupDie('licenseMaker failed'); + } + push(@resourceFiles, $licenseResource); + # Don't add a cleanup object because licenseResource is in tempSubdir. +} + +if(@resourceFiles) { + # Add resources, such as a license agreement. + + # Only unflatten read-only and compressed images. It's not supported + # on other image times. + if($unflattenable && + (command($gConfig{'cmd_hdiutil'}, 'unflatten', $targetImage)) != 0) { + cleanupDie('hdiutil unflatten failed'); + } + # Don't push flatten onto the cleanup stack. If we fail now, we'll be + # removing $targetImage anyway. + + # Type definitions come from Carbon.r. + if(command($gConfig{'cmd_Rez'}, 'Carbon.r', @resourceFiles, '-a', '-o', + $targetImage) != 0) { + cleanupDie('Rez failed'); + } + + # Flatten. This merges the resource fork into the data fork, so no + # special encoding is needed to transfer the file. + if($unflattenable && + (command($gConfig{'cmd_hdiutil'}, 'flatten', $targetImage)) != 0) { + cleanupDie('hdiutil flatten failed'); + } +} + +# $tempSubdir is no longer needed. It's buried on the stack below the +# rm of the fresh image file. Splice in this fashion is equivalent to +# pop-save, pop, push-save. +splice(@gCleanup, -2, 1); +# No need to remove licenseResource separately, it's in tempSubdir. +if(command($gConfig{'cmd_rm'}, '-rf', $tempSubdir) != 0) { + cleanupDie('rm -rf tempSubdir failed'); +} + +if($idme) { + if(command($gConfig{'cmd_hdiutil'}, 'internet-enable', '-yes', + $targetImage) != 0) { + cleanupDie('hdiutil internet-enable failed'); + } +} + +# Done. + +exit(0); + +# argumentEscape(@arguments) +# +# Takes a list of @arguments and makes them shell-safe. +sub argumentEscape(@) { + my(@arguments); + @arguments = @_; + my($argument, @argumentsOut); + foreach $argument (@arguments) { + $argument =~ s%([^A-Za-z0-9_\-/.=+,])%\\$1%g; + push(@argumentsOut, $argument); + } + return @argumentsOut; +} + +# cleanupDie($message) +# +# Displays $message as an error message, and then runs through the +# @gCleanup stack, performing any cleanup operations needed before +# exiting. Does not return, exits with exit status 1. +sub cleanupDie($) { + my($message); + ($message) = @_; + print STDERR ($0.': '.$message.(@gCleanup?' (cleaning up)':'')."\n"); + while(@gCleanup) { + my($subroutine); + $subroutine = pop(@gCleanup); + &$subroutine; + } + exit(1); +} + +# command(@arguments) +# +# Runs the specified command at the verbosity level defined by $gVerbosity. +# Returns nonzero on failure, returning the exit status if appropriate. +# Discards command output. +sub command(@) { + my(@arguments); + @arguments = @_; + return commandVerbosity($gVerbosity,@arguments); +} + +# commandInternal($command, @arguments) +# +# Runs the specified internal command at the verbosity level defined by +# $gVerbosity. +# Returns zero(!) on failure, because commandInternal is supposed to be a +# direct replacement for the Perl system call wrappers, which, unlike shell +# commands and C equivalent system calls, return true (instead of 0) to +# indicate success. +sub commandInternal($@) { + my(@arguments, $command); + ($command, @arguments) = @_; + return commandInternalVerbosity($gVerbosity, $command, @arguments); +} + +# commandInternalVerbosity($verbosity, $command, @arguments) +# +# Run an internal command, printing a bogus command invocation message if +# $verbosity is true. +# +# If $command is unlink: +# Removes the files specified by @arguments. Wraps unlink. +# +# If $command is symlink: +# Creates the symlink specified by @arguments. Wraps symlink. +sub commandInternalVerbosity($$@) { + my(@arguments, $command, $verbosity); + ($verbosity, $command, @arguments) = @_; + if($command eq 'unlink') { + if($verbosity || $gDryRun) { + print(join(' ', 'rm', '-f', argumentEscape(@arguments))."\n"); + } + if($gDryRun) { + return $#arguments+1; + } + return unlink(@arguments); + } + elsif($command eq 'symlink') { + if($verbosity || $gDryRun) { + print(join(' ', 'ln', '-s', argumentEscape(@arguments))."\n"); + } + if($gDryRun) { + return 1; + } + my($source, $target); + ($source, $target) = @arguments; + return symlink($source, $target); + } +} + +# commandOutput(@arguments) +# +# Runs the specified command at the verbosity level defined by $gVerbosity. +# Output is returned in an array of lines. undef is returned on failure. +# The exit status is available in $?. +sub commandOutput(@) { + my(@arguments); + @arguments = @_; + return commandOutputVerbosity($gVerbosity, @arguments); +} + +# commandOutputVerbosity($verbosity, @arguments) +# +# Runs the specified command at the verbosity level defined by the +# $verbosity argument. Output is returned in an array of lines. undef is +# returned on failure. The exit status is available in $?. +# +# If an error occurs in fork or exec, an error message is printed to +# stderr and undef is returned. +# +# If $verbosity is 0, the command invocation is not printed, and its +# stdout is not echoed back to stdout. +# +# If $verbosity is 1, the command invocation is printed. +# +# If $verbosity is 2, the command invocation is printed and the output +# from stdout is echoed back to stdout. +# +# Regardless of $verbosity, stderr is left connected. +sub commandOutputVerbosity($@) { + my(@arguments, $verbosity); + ($verbosity, @arguments) = @_; + my($pid); + if($verbosity || $gDryRun) { + print(join(' ', argumentEscape(@arguments))."\n"); + } + if($gDryRun) { + return(1); + } + if (!defined($pid = open(*COMMAND, '-|'))) { + printf STDERR ($0.': fork: '.$!."\n"); + return undef; + } + elsif ($pid) { + # parent + my(@lines); + while(!eof(*COMMAND)) { + my($line); + chop($line = <COMMAND>); + if($verbosity > 1) { + print($line."\n"); + } + push(@lines, $line); + } + close(*COMMAND); + if ($? == -1) { + printf STDERR ($0.': fork: '.$!."\n"); + return undef; + } + elsif ($? & 127) { + printf STDERR ($0.': exited on signal '.($? & 127). + ($? & 128 ? ', core dumped' : '')."\n"); + return undef; + } + return @lines; + } + else { + # child; this form of exec is immune to shell games + if(!exec {$arguments[0]} (@arguments)) { + printf STDERR ($0.': exec: '.$!."\n"); + exit(-1); + } + } +} + +# commandVerbosity($verbosity, @arguments) +# +# Runs the specified command at the verbosity level defined by the +# $verbosity argument. Returns nonzero on failure, returning the exit +# status if appropriate. Discards command output. +sub commandVerbosity($@) { + my(@arguments, $verbosity); + ($verbosity, @arguments) = @_; + if(!defined(commandOutputVerbosity($verbosity, @arguments))) { + return -1; + } + return $?; +} + +# copyFiles($tempRoot, $method, @arguments) +# +# Copies files or create symlinks in the disk image. +# See --copy and --symlink descriptions for details. +# If $method is 'copy', @arguments are interpreted as source:target, if $method +# is 'symlink', @arguments are interpreted as symlink:target. +sub copyFiles($@) { + my(@fileList, $method, $tempRoot); + ($tempRoot, $method, @fileList) = @_; + my($file, $isSymlink); + $isSymlink = ($method eq 'symlink'); + foreach $file (@fileList) { + my($source, $target); + ($source, $target) = split(/:/, $file); + if(!defined($target) and $isSymlink) { + # empty symlink targets would result in an invalid target and fail, + # but they shall be interpreted as "like source path, but inside dmg" + $target = $source; + } + if(!defined($target)) { + $target = $tempRoot; + } + elsif($target =~ /^\//) { + $target = $tempRoot.$target; + } + else { + $target = $tempRoot.'/'.$target; + } + + my($success); + if($isSymlink) { + $success = commandInternal('symlink', $source, $target); + } + else { + $success = !command($gConfig{'cmd_rsync'}, '-a', '--copy-unsafe-links', + $source, $target); + } + if(!$success) { + cleanupDie('copyFiles failed for method '.$method); + } + } +} + +# diskImageMaker($source, $destination, $format, $name, $tempDir, $tempMount, +# $baseName, $setRootIcon) +# +# Creates a disk image in $destination of format $format corresponding to the +# source directory $source. $name is the volume name. $tempDir is a good +# place to write temporary files, which should be empty (aside from the other +# things that this script might create there, like stage and mount). +# $tempMount is a mount point for temporary disk images. $baseName is the +# name of the disk image, and is presently unused. $setRootIcon is true if +# a volume icon was added to the staged $source and indicates that the +# custom volume icon bit on the volume root needs to be set. +sub diskImageMaker($$$$$$$$) { + my($baseName, $destination, $format, $name, $setRootIcon, $source, + $tempDir, $tempMount); + ($source, $destination, $format, $name, $tempDir, $tempMount, + $baseName, $setRootIcon) = @_; + if(isFormatCompressed($format)) { + my($uncompressedImage); + + if($gConfig{'makehybrid'}) { + my($hybridImage); + $hybridImage = giveExtension($tempDir.'/hybrid', '.dmg'); + + if(command($gConfig{'cmd_hdiutil'}, 'makehybrid', '-hfs', + '-hfs-volume-name', $name, '-hfs-openfolder', $source, '-ov', + $source, '-o', $hybridImage) != 0) { + cleanupDie('hdiutil makehybrid failed'); + } + + $uncompressedImage = $hybridImage; + + # $source is no longer needed and will be removed before anything + # else can fail. splice in this form is the same as pop/push. + splice(@gCleanup, -1, 1, + sub {commandInternalVerbosity(0, 'unlink', $hybridImage);}); + + if(command($gConfig{'cmd_rm'}, '-rf', $source) != 0) { + cleanupDie('rm -rf failed'); + } + + if(!$gConfig{'partition_table'} && $gConfig{'recursive_access'}) { + # Even if we do want to create disk images without partition tables, + # it's impossible unless recursive_access is set. + my($rootDevice, $partitionDevice, $partitionMountPoint); + + if(!(($rootDevice, $partitionDevice, $partitionMountPoint) = + hdidMountImage($tempMount, '-readonly', $hybridImage))) { + cleanupDie('hdid mount failed'); + } + + push(@gCleanup, sub {commandVerbosity(0, + $gConfig{'cmd_diskutil'}, 'eject', $rootDevice);}); + + my($udrwImage); + $udrwImage = giveExtension($tempDir.'/udrw', '.dmg'); + + if(command($gConfig{'cmd_hdiutil'}, 'create', '-format', 'UDRW', + '-ov', '-srcdevice', $partitionDevice, $udrwImage) != 0) { + cleanupDie('hdiutil create failed'); + } + + $uncompressedImage = $udrwImage; + + # Going to eject before anything else can fail. Get the eject off + # the stack. + pop(@gCleanup); + + # $hybridImage will be removed soon, but until then, it needs to + # stay on the cleanup stack. It needs to wait until after + # ejection. $udrwImage is staying around. Make it appear as + # though it's been done before $hybridImage. + # + # splice in this form is the same as popping one element to + # @tempCleanup and pushing the subroutine. + my(@tempCleanup); + @tempCleanup = splice(@gCleanup, -1, 1, + sub {commandInternalVerbosity(0, 'unlink', $udrwImage);}); + push(@gCleanup, @tempCleanup); + + if(command($gConfig{'cmd_diskutil'}, 'eject', $rootDevice) != 0) { + cleanupDie('diskutil eject failed'); + } + + # Pop unlink of $uncompressedImage + pop(@gCleanup); + + if(commandInternal('unlink', $hybridImage) != 1) { + cleanupDie('unlink hybridImage failed: '.$!); + } + } + } + else { + # makehybrid is not available, fall back to making a UDRW and + # converting to a compressed image. It ought to be possible to + # create a compressed image directly, but those come out far too + # large (journaling?) and need to be read-write to fix up the + # volume icon anyway. Luckily, we can take advantage of a single + # call back into this function. + my($udrwImage); + $udrwImage = giveExtension($tempDir.'/udrw', '.dmg'); + + diskImageMaker($source, $udrwImage, 'UDRW', $name, $tempDir, + $tempMount, $baseName, $setRootIcon); + + # The call back into diskImageMaker already removed $source. + + $uncompressedImage = $udrwImage; + } + + # The uncompressed disk image is now in its final form. Compress it. + # Jaguar doesn't support hdiutil convert -ov, but it always allows + # overwriting. + # bzip2-compressed UDBZ images can only be created and mounted on 10.4 + # and later. The bzip2-level imagekey is only effective when creating + # images in 10.5. In 10.4, bzip2-level is harmlessly ignored, and the + # default value of 1 is always used. + if(command($gConfig{'cmd_hdiutil'}, 'convert', '-format', $format, + '-imagekey', ($format eq 'UDBZ' ? 'bzip2-level=9' : 'zlib-level=9'), + (defined($gDarwinMajor) && $gDarwinMajor <= 6 ? () : ('-ov')), + $uncompressedImage, '-o', $destination) != 0) { + cleanupDie('hdiutil convert failed'); + } + + # $uncompressedImage is going to be unlinked before anything else can + # fail. splice in this form is the same as pop/push. + splice(@gCleanup, -1, 1, + sub {commandInternalVerbosity(0, 'unlink', $destination);}); + + if(commandInternal('unlink', $uncompressedImage) != 1) { + cleanupDie('unlink uncompressedImage failed: '.$!); + } + + # At this point, the only thing that the compressed block has added to + # the cleanup stack is the removal of $destination. $source has already + # been removed, and its cleanup entry has been removed as well. + } + elsif($format eq 'UDRW' || $format eq 'UDSP') { + my(@extraArguments); + if(!$gConfig{'partition_table'}) { + @extraArguments = ('-layout', 'NONE'); + } + + if($gConfig{'create_directly'}) { + # Use -fs HFS+ to suppress the journal. + if(command($gConfig{'cmd_hdiutil'}, 'create', '-format', $format, + @extraArguments, '-fs', 'HFS+', '-volname', $name, + '-ov', '-srcfolder', $source, $destination) != 0) { + cleanupDie('hdiutil create failed'); + } + + # $source is no longer needed and will be removed before anything + # else can fail. splice in this form is the same as pop/push. + splice(@gCleanup, -1, 1, + sub {commandInternalVerbosity(0, 'unlink', $destination);}); + + if(command($gConfig{'cmd_rm'}, '-rf', $source) != 0) { + cleanupDie('rm -rf failed'); + } + } + else { + # hdiutil create does not support -srcfolder or -srcdevice, it only + # knows how to create blank images. Figure out how large an image + # is needed, create it, and fill it. This is needed for Jaguar. + + # Use native block size for hdiutil create -sectors. + delete $ENV{'BLOCKSIZE'}; + + my(@duOutput, $ignore, $sizeBlocks, $sizeOverhead, $sizeTotal, $type); + if(!(@output = commandOutput($gConfig{'cmd_du'}, '-s', $tempRoot)) || + $? != 0) { + cleanupDie('du failed'); + } + ($sizeBlocks, $ignore) = split(' ', $output[0], 2); + + # The filesystem itself takes up 152 blocks of its own blocks for the + # filesystem up to 8192 blocks, plus 64 blocks for every additional + # 4096 blocks or portion thereof. + $sizeOverhead = 152 + 64 * POSIX::ceil( + (($sizeBlocks - 8192) > 0) ? (($sizeBlocks - 8192) / (4096 - 64)) : 0); + + # The number of blocks must be divisible by 8. + my($mod); + if($mod = ($sizeOverhead % 8)) { + $sizeOverhead += 8 - $mod; + } + + # sectors is taken as the size of a disk, not a filesystem, so the + # partition table eats into it. + if($gConfig{'partition_table'}) { + $sizeOverhead += 80; + } + + # That was hard. Leave some breathing room anyway. Use 1024 sectors + # (512kB). These read-write images wouldn't be useful if they didn't + # have at least a little free space. + $sizeTotal = $sizeBlocks + $sizeOverhead + 1024; + + # Minimum sizes - these numbers are larger on Jaguar than on later + # systems. Just use the Jaguar numbers, since it's unlikely to wind + # up here on any other release. + if($gConfig{'partition_table'} && $sizeTotal < 8272) { + $sizeTotal = 8272; + } + if(!$gConfig{'partition_table'} && $sizeTotal < 8192) { + $sizeTotal = 8192; + } + + # hdiutil create without -srcfolder or -srcdevice will not accept + # -format. It uses -type. Fortunately, the two supported formats + # here map directly to the only two supported types. + if ($format eq 'UDSP') { + $type = 'SPARSE'; + } + else { + $type = 'UDIF'; + } + + if(command($gConfig{'cmd_hdiutil'}, 'create', '-type', $type, + @extraArguments, '-fs', 'HFS+', '-volname', $name, + '-ov', '-sectors', $sizeTotal, $destination) != 0) { + cleanupDie('hdiutil create failed'); + } + + push(@gCleanup, + sub {commandInternalVerbosity(0, 'unlink', $destination);}); + + # The rsync will occur shortly. + } + + my($mounted, $rootDevice, $partitionDevice, $partitionMountPoint); + + $mounted=0; + if(!$gConfig{'create_directly'} || $gConfig{'openfolder_bless'} || + $setRootIcon) { + # The disk image only needs to be mounted if: + # create_directly is false, because the content needs to be copied + # openfolder_bless is true, because bless -openfolder needs to run + # setRootIcon is true, because the root needs its attributes set. + if(!(($rootDevice, $partitionDevice, $partitionMountPoint) = + hdidMountImage($tempMount, $destination))) { + cleanupDie('hdid mount failed'); + } + + $mounted=1; + + push(@gCleanup, sub {commandVerbosity(0, + $gConfig{'cmd_diskutil'}, 'eject', $rootDevice);}); + } + + if(!$gConfig{'create_directly'}) { + # Couldn't create and copy directly in one fell swoop. Now that + # the volume is mounted, copy the files. --copy-unsafe-links is + # unnecessary since it was used to copy everything to the staging + # area. There can be no more unsafe links. + if(command($gConfig{'cmd_rsync'}, '-a', + $source.'/',$partitionMountPoint) != 0) { + cleanupDie('rsync to new volume failed'); + } + + # We need to get the rm -rf of $source off the stack, because it's + # being cleaned up here. There are two items now on top of it: + # removing the target image and, above that, ejecting it. Splice it + # out. + my(@tempCleanup); + @tempCleanup = splice(@gCleanup, -2); + # The next splice is the same as popping once and pushing @tempCleanup. + splice(@gCleanup, -1, 1, @tempCleanup); + + if(command($gConfig{'cmd_rm'}, '-rf', $source) != 0) { + cleanupDie('rm -rf failed'); + } + } + + if($gConfig{'openfolder_bless'}) { + # On Tiger, the bless docs say to use --openfolder, but only + # --openfolder is accepted on Panther. Tiger takes it with a single + # dash too. Jaguar is out of luck. + if(command($gConfig{'cmd_bless'}, '-openfolder', + $partitionMountPoint) != 0) { + cleanupDie('bless failed'); + } + } + + setAttributes($partitionMountPoint, @attributes); + + if($setRootIcon) { + # When "hdiutil create -srcfolder" is used, the root folder's + # attributes are not copied to the new volume. Fix up. + + if(command($gConfig{'cmd_SetFile'}, '-a', 'C', + $partitionMountPoint) != 0) { + cleanupDie('SetFile failed'); + } + } + + if($mounted) { + # Pop diskutil eject + pop(@gCleanup); + + if(command($gConfig{'cmd_diskutil'}, 'eject', $rootDevice) != 0) { + cleanupDie('diskutil eject failed'); + } + } + + # End of UDRW/UDSP section. At this point, $source has been removed + # and its cleanup entry has been removed from the stack. + } + else { + cleanupDie('unrecognized format'); + print STDERR ($0.": unrecognized format\n"); + exit(1); + } +} + +# giveExtension($file, $extension) +# +# If $file does not end in $extension, $extension is added. The new +# filename is returned. +sub giveExtension($$) { + my($extension, $file); + ($file, $extension) = @_; + if(substr($file, -length($extension)) ne $extension) { + return $file.$extension; + } + return $file; +} + +# hdidMountImage($mountPoint, @arguments) +# +# Runs the hdid command with arguments specified by @arguments. +# @arguments may be a single-element array containing the name of the +# disk image to mount. Returns a three-element array, with elements +# corresponding to: +# - The root device of the mounted image, suitable for ejection +# - The device corresponding to the mounted partition +# - The mounted partition's mount point +# +# If running on a system that supports easy mounting at points outside +# of the default /Volumes with hdiutil attach, it is used instead of hdid, +# and $mountPoint is used as the mount point. +# +# The root device will differ from the partition device when the disk +# image contains a partition table, otherwise, they will be identical. +# +# If hdid fails, undef is returned. +sub hdidMountImage($@) { + my(@arguments, @command, $mountPoint); + ($mountPoint, @arguments) = @_; + my(@output); + + if($gConfig{'hdiutil_mountpoint'}) { + @command=($gConfig{'cmd_hdiutil'}, 'attach', @arguments, + '-mountpoint', $mountPoint); + } + else { + @command=($gConfig{'cmd_hdid'}, @arguments); + } + + if(!(@output = commandOutput(@command)) || + $? != 0) { + return undef; + } + + if($gDryRun) { + return('/dev/diskX','/dev/diskXsY','/Volumes/'.$volumeName); + } + + my($line, $restOfLine, $rootDevice); + + foreach $line (@output) { + my($device, $mountpoint); + if($line !~ /^\/dev\//) { + # Consider only lines that correspond to /dev entries + next; + } + ($device, $restOfLine) = split(' ', $line, 2); + + if(!defined($rootDevice) || $rootDevice eq '') { + # If this is the first device seen, it's the root device to be + # used for ejection. Keep it. + $rootDevice = $device; + } + + if($restOfLine =~ /(\/.*)/) { + # The first partition with a mount point is the interesting one. It's + # usually Apple_HFS and usually the last one in the list, but beware of + # the possibility of other filesystem types and the Apple_Free partition. + # If the disk image contains no partition table, the partition will not + # have a type, so look for the mount point by looking for a slash. + $mountpoint = $1; + return($rootDevice, $device, $mountpoint); + } + } + + # No mount point? This is bad. If there's a root device, eject it. + if(defined($rootDevice) && $rootDevice ne '') { + # Failing anyway, so don't care about failure + commandVerbosity(0, $gConfig{'cmd_diskutil'}, 'eject', $rootDevice); + } + + return undef; +} + +# isFormatCompressed($format) +# +# Returns true if $format corresponds to a compressed disk image format. +# Returns false otherwise. +sub isFormatCompressed($) { + my($format); + ($format) = @_; + return $format eq 'UDZO' || $format eq 'UDBZ'; +} + +# licenseMaker($text, $resource) +# +# Takes a plain text file at path $text and creates a license agreement +# resource containing the text at path $license. English-only, and +# no special formatting. This is the bare-bones stuff. For more +# intricate license agreements, create your own resource. +# +# ftp://ftp.apple.com/developer/Development_Kits/SLAs_for_UDIFs_1.0.dmg +sub licenseMaker($$) { + my($resource, $text); + ($text, $resource) = @_; + if(!sysopen(*TEXT, $text, O_RDONLY)) { + print STDERR ($0.': licenseMaker: sysopen text: '.$!."\n"); + return 0; + } + if(!sysopen(*RESOURCE, $resource, O_WRONLY|O_CREAT|O_EXCL)) { + print STDERR ($0.': licenseMaker: sysopen resource: '.$!."\n"); + return 0; + } + print RESOURCE << '__EOT__'; +// See /System/Library/Frameworks/CoreServices.framework/Frameworks/CarbonCore.framework/Headers/Script.h for language IDs. +data 'LPic' (5000) { + // Default language ID, 0 = English + $"0000" + // Number of entries in list + $"0001" + + // Entry 1 + // Language ID, 0 = English + $"0000" + // Resource ID, 0 = STR#/TEXT/styl 5000 + $"0000" + // Multibyte language, 0 = no + $"0000" +}; + +resource 'STR#' (5000, "English") { + { + // Language (unused?) = English + "English", + // Agree + "Agree", + // Disagree + "Disagree", +__EOT__ + # This stuff needs double-quotes for interpolations to work. + print RESOURCE (" // Print, ellipsis is 0xC9\n"); + print RESOURCE (" \"Print\xc9\",\n"); + print RESOURCE (" // Save As, ellipsis is 0xC9\n"); + print RESOURCE (" \"Save As\xc9\",\n"); + print RESOURCE (' // Descriptive text, curly quotes are 0xD2 and 0xD3'. + "\n"); + print RESOURCE (' "If you agree to the terms of this license '. + "agreement, click \xd2Agree\xd3 to access the software. If you ". + "do not agree, press \xd2Disagree.\xd3\"\n"); +print RESOURCE << '__EOT__'; + }; +}; + +// Beware of 1024(?) byte (character?) line length limitation. Split up long +// lines. +// If straight quotes are used ("), remember to escape them (\"). +// Newline is \n, to leave a blank line, use two of them. +// 0xD2 and 0xD3 are curly double-quotes ("), 0xD4 and 0xD5 are curly +// single quotes ('), 0xD5 is also the apostrophe. +data 'TEXT' (5000, "English") { +__EOT__ + + while(!eof(*TEXT)) { + my($line); + chop($line = <TEXT>); + + while(defined($line)) { + my($chunk); + + # Rez doesn't care for lines longer than (1024?) characters. Split + # at less than half of that limit, in case everything needs to be + # backwhacked. + if(length($line)>500) { + $chunk = substr($line, 0, 500); + $line = substr($line, 500); + } + else { + $chunk = $line; + $line = undef; + } + + if(length($chunk) > 0) { + # Unsafe characters are the double-quote (") and backslash (\), escape + # them with backslashes. + $chunk =~ s/(["\\])/\\$1/g; + + print RESOURCE ' "'.$chunk.'"'."\n"; + } + } + print RESOURCE ' "\n"'."\n"; + } + close(*TEXT); + + print RESOURCE << '__EOT__'; +}; + +data 'styl' (5000, "English") { + // Number of styles following = 1 + $"0001" + + // Style 1. This is used to display the first two lines in bold text. + // Start character = 0 + $"0000 0000" + // Height = 16 + $"0010" + // Ascent = 12 + $"000C" + // Font family = 1024 (Lucida Grande) + $"0400" + // Style bitfield, 0x1=bold 0x2=italic 0x4=underline 0x8=outline + // 0x10=shadow 0x20=condensed 0x40=extended + $"00" + // Style, unused? + $"02" + // Size = 12 point + $"000C" + // Color, RGB + $"0000 0000 0000" +}; +__EOT__ + close(*RESOURCE); + + return 1; +} + +# pathSplit($pathname) +# +# Splits $pathname into an array of path components. +sub pathSplit($) { + my($pathname); + ($pathname) = @_; + return split(/\//, $pathname); +} + +# setAttributes($root, @attributeList) +# +# @attributeList is an array, each element of which must be in the form +# <a>:<file>. <a> is a list of attributes, per SetFile. <file> is a file +# which is taken as relative to $root (even if it appears as an absolute +# path.) SetFile is called to set the attributes on each file in +# @attributeList. +sub setAttributes($@) { + my(@attributes, $root); + ($root, @attributes) = @_; + my($attribute); + foreach $attribute (@attributes) { + my($attrList, $file, @fileList, @fixedFileList); + ($attrList, @fileList) = split(/:/, $attribute); + if(!defined($attrList) || !@fileList) { + cleanupDie('--attribute requires <attributes>:<file>'); + } + @fixedFileList=(); + foreach $file (@fileList) { + if($file =~ /^\//) { + push(@fixedFileList, $root.$file); + } + else { + push(@fixedFileList, $root.'/'.$file); + } + } + if(command($gConfig{'cmd_SetFile'}, '-a', $attrList, @fixedFileList)) { + cleanupDie('SetFile failed to set attributes'); + } + } + return; +} + +sub trapSignal($) { + my($signalName); + ($signalName) = @_; + cleanupDie('exiting on SIG'.$signalName); +} + +sub usage() { + print STDERR ( +"usage: pkg-dmg --source <source-folder>\n". +" --target <target-image>\n". +" [--format <format>] (default: UDZO)\n". +" [--volname <volume-name>] (default: same name as source)\n". +" [--tempdir <temp-dir>] (default: same dir as target)\n". +" [--mkdir <directory>] (make directory in image)\n". +" [--copy <source>[:<dest>]] (extra files to add)\n". +" [--symlink <source>[:<dest>]] (extra symlinks to add)\n". +" [--license <file>] (plain text license agreement)\n". +" [--resource <file>] (flat .r files to merge)\n". +" [--icon <icns-file>] (volume icon)\n". +" [--attribute <a>:<file>] (set file attributes)\n". +" [--idme] (make Internet-enabled image)\n". +" [--sourcefile] (treat --source as a file)\n". +" [--verbosity <level>] (0, 1, 2; default=2)\n". +" [--dry-run] (print what would be done)\n"); + return; +} diff --git a/build/package/mac_osx/unpack-diskimage b/build/package/mac_osx/unpack-diskimage new file mode 100644 index 000000000..3ba977805 --- /dev/null +++ b/build/package/mac_osx/unpack-diskimage @@ -0,0 +1,54 @@ +#!/bin/bash +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +# Unpack a disk image to a specified target folder +# +# Usage: unpack-diskimage <image_file> +# <mountpoint> +# <target_path> + +DMG_PATH=$1 +MOUNTPOINT=$2 +TARGETPATH=$3 + +# How long to wait before giving up waiting for the mount to finish (seconds) +TIMEOUT=90 + +# If mnt already exists, then the previous run may not have cleaned up +# properly. We should try to umount and remove the mnt directory. +if [ -d $MOUNTPOINT ]; then + echo "mnt already exists, trying to clean up" + hdiutil detach $MOUNTPOINT -force + rm -rdfv $MOUNTPOINT +fi + +# Install an on-exit handler that will unmount and remove the '$MOUNTPOINT' directory +trap "{ if [ -d $MOUNTPOINT ]; then hdiutil detach $MOUNTPOINT -force; rm -rdfv $MOUNTPOINT; fi; }" EXIT + +mkdir -p $MOUNTPOINT + +hdiutil attach -verbose -noautoopen -mountpoint $MOUNTPOINT "$DMG_PATH" +# Wait for files to show up +# hdiutil uses a helper process, diskimages-helper, which isn't always done its +# work by the time hdiutil exits. So we wait until something shows up in the +# mnt directory. Due to the async nature of diskimages-helper, the best thing +# we can do is to make sure the glob() rsync is making can find files. +i=0 +while [ "$(echo $MOUNTPOINT/*)" == "$MOUNTPOINT/*" ]; do + if [ $i -gt $TIMEOUT ]; then + echo "No files found, exiting" + exit 1 + fi + sleep 1 + i=$(expr $i + 1) +done +# Now we can copy everything out of the $MOUNTPOINT directory into the target directory +rsync -av $MOUNTPOINT/* $MOUNTPOINT/.DS_Store $MOUNTPOINT/.background $MOUNTPOINT/.VolumeIcon.icns $TARGETPATH/. +hdiutil detach $MOUNTPOINT +rm -rdf $MOUNTPOINT +# diskimage-helper prints messages to stdout asynchronously as well, sleep +# for a bit to ensure they don't disturb following commands in a script that +# might parse stdout messages +sleep 5 diff --git a/build/package/moz.build b/build/package/moz.build new file mode 100644 index 000000000..895d11993 --- /dev/null +++ b/build/package/moz.build @@ -0,0 +1,6 @@ +# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + diff --git a/build/pgo/blueprint/LICENSE b/build/pgo/blueprint/LICENSE new file mode 100644 index 000000000..d7474100a --- /dev/null +++ b/build/pgo/blueprint/LICENSE @@ -0,0 +1,314 @@ +Blueprint CSS Framework License +---------------------------------------------------------------- + +Copyright (c) 2007-2008 Olav Bjorkoy (olav at bjorkoy.com) + +The Blueprint CSS Framework is available for use in all personal or +commercial projects, under both the (modified) MIT and the GPL license. You +may choose the one that fits your project. + + +The (modified) MIT License +---------------------------------------------------------------- + +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, sub-license, 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 every other copyright notice found in this +software, and all the attributions in every file, 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 NON-INFRINGEMENT. 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. + + +The GPL License +---------------------------------------------------------------- + + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES.
\ No newline at end of file diff --git a/build/pgo/blueprint/elements.html b/build/pgo/blueprint/elements.html new file mode 100644 index 000000000..51d79fae4 --- /dev/null +++ b/build/pgo/blueprint/elements.html @@ -0,0 +1,250 @@ +<!-- This Source Code Form is subject to the terms of the Mozilla Public + - License, v. 2.0. If a copy of the MPL was not distributed with this + - file, You can obtain one at http://mozilla.org/MPL/2.0/. --> + +<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" + "http://www.w3.org/TR/html4/strict.dtd"> + +<html lang="en"> +<head> + <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> + <title>Blueprint HTML Elements Tests</title> + + <!-- Framework CSS --> + <link rel="stylesheet" href="screen.css" type="text/css" media="screen, projection"> + <link rel="stylesheet" href="print.css" type="text/css" media="print"> + <!--[if IE]><link rel="stylesheet" href="ie.css" type="text/css" media="screen, projection"><![endif]--> + +</head> +<body> + + <div class="container showgrid"> + <h2>Tests for common HTML elements</h2> + <hr> + + <h5>PARAGRAPHS <span class="alt">&</span> BOXES</h5> + + <div class="span-8"> + <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p> + <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor <sub>sub text</sub> ut labore et <sup>sup text</sup> magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p> + </div> + + <div class="span-8"> + <p class="small">Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.</p> + <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p> + <p class="large">Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p> + </div> + + <div class="span-8 last"> + + <div class="box"> + <p class="last">Aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p> + </div> + <blockquote> + <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.</p> + </blockquote> + + </div> + <hr> + + <h5>LISTS</h5> + + <div class="span-8"> + <ul> + <li>Unordered list test</li> + <li>Another list element. Lorem ipsum dolor sit amet, consectetur adipisicing elit.</li> + <li>Yet another element in the list</li> + <li>Some long text. Lorem ipsum dolor sit amet, consectetur adipisicing elit. Lorem ipsum dolor sit amet, consectetur adipisicing elit.</li> + </ul> + <ol> + <li>Ordered list test</li> + <li>Another list element</li> + <li>Yet another element in the list</li> + </ol> + </div> + + <div class="span-8"> + <ol> + <li>Ordered list</li> + <li>Here's a nested unordered list + <ul> + <li>Nested Unordered list</li> + <li>Nested ordered list + <ol> + <li>The first</li> + <li>And the second</li> + </ol> + </li> + </ul> + </li> + <li>Ordered List item</li> + <li>Nested Ordered list + <ol> + <li>Some point</li> + <li>Nested Unordered list + <ul> + <li>The first</li> + <li>And the second</li> + </ul> + </li> + </ol> + </li> + </ol> + </div> + + <div class="span-8 last"> + <dl> + <dt>definition list dt</dt> + <dd>definition list dd</dd> + <dt>definition list dt</dt> + <dd>definition list dd</dd> + <dt>Lorem ipsum dolor sit amet, consectetur adipisicing elit adipisicing elit adipisicing elit</dt> + <dd>Lorem ipsum dolor sit amet, consectetur adipisicing elit adipisicing elit adipisicing elit</dd> + <dt>Lorem ipsum dolor sit amet, consectetur adipisicing elit adipisicing elit adipisicing elit</dt> + <dd>Lorem ipsum dolor sit amet, consectetur adipisicing elit adipisicing elit adipisicing elit</dd> + </dl> + </div> + <hr> + + <h5>HEADINGS</h5> + + <div class="span-8"> + <h1>H1: Lorem ipsum dolor sit amet</h1> + <h2>H2: Lorem ipsum dolor sit amet, consectetur elit</h2> + <h3>H3: Lorem ipsum dolor sit amet, consectetur adipisicing elit</h3> + <h4>H4: Lorem ipsum dolor sit amet, consectetur adipisicing elit adipis</h4> + <h5>H5: Lorem ipsum dolor sit amet, consectetur adipisicing elit adipisicing elit adipisicing elit</h5> + <h6>H6: Lorem ipsum dolor sit amet, consectetur adipisicing elit adipisicing elit adipisicing elit</h6> + </div> + + <div class="span-8"> + <h1>Heading 1</h1><hr> + <h2>Heading 2</h2><hr> + <h3>Heading 3</h3><hr> + <h4>Heading 4</h4><hr> + <h5>Heading 5</h5><hr> + <h6>Heading 6</h6> + </div> + + <div class="span-8 last"> + <h1>Heading 1</h1> + <h2>Heading 2</h2> + <h3>Heading 3</h3> + <h4>Heading 4</h4> + <h5>Heading 5</h5> + <h6>Heading 6</h6> + </div> + <hr> + + <h5>MISC ELEMENTS</h5> + + <div class="span-8"> + <p> + <strong><strong></strong><br> + <del><del> deleted</del><br> + <dfn><dfn> dfn</dfn><br> + <em><em> emphasis</em> + </p> + <p> + <a><a> anchor</a><br> + <a href="http://www.google.com"><a> a + href</a> + </p> + <p> + <abbr title="extended abbr text should show when mouse over"><abbr> abbr - extended text when mouseover.</abbr><br> + <acronym title="extended acronym text should show when mouse over"><acronym> acronym - extended text when mouseover.</acronym> + </p> + <address> + <address><br> + Donald Duck<br> + Box 555<br> + Disneyland + </address> + <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore dolore.</p> + </div> + + <div class="span-8"> + <table summary="This is the summary text for this table." border="0" cellspacing="0" cellpadding="0"> + <caption><em>A standard test table with a caption, tr, td elements</em></caption> + <tr> + <th class="span-4">Table Header One</th> + <th class="span-4 last">Table Header Two</th> + </tr> + <tr> + <td>TD One</td> + <td>TD Two</td> + </tr> + <tr> + <td colspan="2">TD colspan 2</td> + </tr> + </table> + + <table summary="This is the summary text for this table." border="0" cellspacing="0" cellpadding="0"> + <caption><em>A test table with a thead, tfoot, and tbody elements</em></caption> + <thead> + <tr> + <th class="span-4">Table Header One</th> + <th class="span-4 last">Table Header Two</th> + </tr> + </thead> + <tfoot> + <tr> + <td colspan="2">tfoot footer</td> + </tr> + </tfoot> + <tbody> + <tr> + <td>TD One</td> + <td>TD Two</td> + </tr> + <tr> + <td>TD One</td> + <td>TD Two</td> + </tr> + </tbody> + <tbody> + <tr> + <td>TD One</td> + <td>TD Two</td> + </tr> + <tr> + <td>TD One</td> + <td>TD Two</td> + </tr> + </tbody> + </table> + </div> + + <div class="span-8 last"> + +<pre><pre> +pre space1 +pre space1 +pre space2 +pre space2 +pre tab +pre tab</pre> + +<code><code> +Not indented + indent1 + indent1 + indent2 + indent3</code> + + <tt><tt> + This tt text should be monospaced + and + wrap as if + one line of text + even though the code has newlines, spaces, and tabs. + It should be the same size as <p> text. + </tt> + </div> + <hr> + + <p><a href="http://validator.w3.org/check?uri=referer"> + <img src="valid.png" alt="Valid HTML 4.01 Strict" height="31" width="88" class="top"></a></p> + + </div> +</body> +</html> diff --git a/build/pgo/blueprint/fancytype-screen.css b/build/pgo/blueprint/fancytype-screen.css new file mode 100644 index 000000000..0d3feb77f --- /dev/null +++ b/build/pgo/blueprint/fancytype-screen.css @@ -0,0 +1,75 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* -------------------------------------------------------------- + + fancy-type.css + * Lots of pretty advanced classes for manipulating text. + + See the Readme file in this folder for additional instructions. + +-------------------------------------------------------------- */ + +/* Indentation instead of line shifts for sibling paragraphs. */ + p + p { text-indent:2em; margin-top:-1.5em; } + form p + p { text-indent: 0; } /* Don't want this in forms. */ + + +/* For great looking type, use this code instead of asdf: + <span class="alt">asdf</span> + Best used on prepositions and ampersands. */ + +.alt { + color: #666; + font-family: "Warnock Pro", "Goudy Old Style","Palatino","Book Antiqua", Georgia, serif; + font-style: italic; + font-weight: normal; +} + + +/* For great looking quote marks in titles, replace "asdf" with: + <span class="dquo">“</span>asdf” + (That is, when the title starts with a quote mark). + (You may have to change this value depending on your font size). */ + +.dquo { margin-left: -.5em; } + + +/* Reduced size type with incremental leading + (http://www.markboulton.co.uk/journal/comments/incremental_leading/) + + This could be used for side notes. For smaller type, you don't necessarily want to + follow the 1.5x vertical rhythm -- the line-height is too much. + + Using this class, it reduces your font size and line-height so that for + every four lines of normal sized type, there is five lines of the sidenote. eg: + + New type size in em's: + 10px (wanted side note size) / 12px (existing base size) = 0.8333 (new type size in ems) + + New line-height value: + 12px x 1.5 = 18px (old line-height) + 18px x 4 = 72px + 72px / 5 = 14.4px (new line height) + 14.4px / 10px = 1.44 (new line height in em's) */ + +p.incr, .incr p { + font-size: 10px; + line-height: 1.44em; + margin-bottom: 1.5em; +} + + +/* Surround uppercase words and abbreviations with this class. + Based on work by Jørgen Arnor Gårdsø Lom [http://twistedintellect.com/] */ + +.caps { + font-variant: small-caps; + letter-spacing: 1px; + text-transform: lowercase; + font-size:1.2em; + line-height:1%; + font-weight:bold; + padding:0 2px; +} diff --git a/build/pgo/blueprint/forms.html b/build/pgo/blueprint/forms.html new file mode 100644 index 000000000..8310ba4d4 --- /dev/null +++ b/build/pgo/blueprint/forms.html @@ -0,0 +1,104 @@ +<!-- This Source Code Form is subject to the terms of the Mozilla Public + - License, v. 2.0. If a copy of the MPL was not distributed with this + - file, You can obtain one at http://mozilla.org/MPL/2.0/. --> + +<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" + "http://www.w3.org/TR/html4/strict.dtd"> + +<html lang="en"> +<head> + <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> + <title>Blueprint Forms Tests</title> + + <!-- Framework CSS --> + <link rel="stylesheet" href="screen.css" type="text/css" media="screen, projection"> + <link rel="stylesheet" href="print.css" type="text/css" media="print"> + <!--[if IE]><link rel="stylesheet" href="ie.css" type="text/css" media="screen, projection"><![endif]--> +</head> +<body> + + <div class="container showgrid"> + <h1>Forms</h1> + <hr> + + <div class="span-12"> + + <form id="dummy" action="" method="post"> + + <fieldset> + <legend>Simple sample form</legend> + + <p><label for="dummy0">Text input (title)</label><br> + <input type="text" class="title" name="dummy0" id="dummy0" value="Field with class .title"></p> + + <p><label for="dummy1">Another field</label><br> + <input type="text" class="text" id="dummy1" name="dummy1" value="Field with class .text"></p> + + <p><label for="dummy2">Textarea</label><br> + <textarea name="dummy2" id="dummy2" rows="5" cols="20"></textarea></p> + + <p><input type="submit" value="Submit"> + <input type="reset" value="Reset"></p> + + </fieldset> + </form> + + </div> + <div class="span-12 last"> + + <div class="error"> + This is a <div> with the class <strong>.error</strong>. <a href="#">Link</a>. + </div> + <div class="notice"> + This is a <div> with the class <strong>.notice</strong>. <a href="#">Link</a>. + </div> + <div class="success"> + This is a <div> with the class <strong>.success</strong>. <a href="#">Link</a>. + </div> + + <fieldset> + <legend>Select, checkboxes, lists</legend> + + <p><label for="dummy3">Select field</label><br> + <select id="dummy3" name="dummy3"> + <option value="1">Ottawa</option> + <option value="2">Calgary</option> + <option value="3">Moosejaw</option> + </select></p> + + <p><label for="dummy4">Select with groups</label><br> + <select id="dummy4" name="dummy4"> + <option>Favorite pet</option> + <optgroup label="mammals"> + <option>dog</option> + <option>cat</option> + <option>rabbit</option> + <option>horse</option> + </optgroup> + <optgroup label="reptiles"> + <option>iguana</option> + <option>snake</option> + </optgroup> + </select></p> + + <p><label>Radio buttons</label><br> + <input type="radio" name="example"> Radio one<br> + <input type="radio" name="example"> Radio two<br> + <input type="radio" name="example"> Radio three<br></p> + + <p><label>Checkboxes</label><br> + <input type="checkbox"> Check one<br> + <input type="checkbox"> Check two<br> + <input type="checkbox"> Check three<br></p> + + </fieldset> + + </div> + <hr> + + <p><a href="http://validator.w3.org/check?uri=referer"> + <img src="valid.png" alt="Valid HTML 4.01 Strict" height="31" width="88" class="top"></a></p> + + </div> +</body> +</html> diff --git a/build/pgo/blueprint/grid.html b/build/pgo/blueprint/grid.html new file mode 100644 index 000000000..e851a6313 --- /dev/null +++ b/build/pgo/blueprint/grid.html @@ -0,0 +1,210 @@ +<!-- This Source Code Form is subject to the terms of the Mozilla Public + - License, v. 2.0. If a copy of the MPL was not distributed with this + - file, You can obtain one at http://mozilla.org/MPL/2.0/. --> + +<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" + "http://www.w3.org/TR/html4/strict.dtd"> + +<html lang="en"> +<head> + <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> + <title>Blueprint Grid Tests</title> + + <!-- Framework CSS --> + <link rel="stylesheet" href="screen.css" type="text/css" media="screen, projection"> + <link rel="stylesheet" href="print.css" type="text/css" media="print"> + <!--[if IE]><link rel="stylesheet" href="ie.css" type="text/css" media="screen, projection"><![endif]--> +</head> +<body> + + <div class="container showgrid"> + <h1>Blueprint Tests: grid.css</h1> + + + <div class="span-8"> + <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit.</p> + </div> + <div class="span-8"> + <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit.</p> + </div> + <div class="span-8 last"> + <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit.</p> + </div> + + + <div class="span-6 append-1"> + <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit.</p> + </div> + <div class="span-6 append-2"> + <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit.</p> + </div> + <div class="span-6 append-3 last"> + <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit.</p> + </div> + + + <div class="span-6 prepend-1"> + <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit.</p> + </div> + <div class="span-6 prepend-2"> + <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit.</p> + </div> + <div class="span-6 prepend-3 last"> + <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit.</p> + </div> + <hr> + + <div class="span-12 border"> + <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p> + </div> + <div class="span-12 last"> + <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p> + </div> + <hr> + + <div class="span-1 prepend-1"><p>1</p></div> + <div class="span-1 prepend-2"><p>2</p></div> + <div class="span-1 prepend-3"><p>3</p></div> + <div class="span-1 prepend-4"><p>4</p></div> + <div class="span-1 prepend-5"><p>5</p></div> + <div class="span-1 prepend-3 last"><p>3</p></div> + + <div class="span-1 append-1"><p>1</p></div> + <div class="span-1 append-2"><p>2</p></div> + <div class="span-1 append-3"><p>3</p></div> + <div class="span-1 append-4"><p>4</p></div> + <div class="span-1 append-5"><p>5</p></div> + <div class="span-1 append-3 last"><p>3</p></div> + + <div class="span-1 border"><p>1</p></div> + <div class="span-1 border"><p>2</p></div> + <div class="span-1 border"><p>3</p></div> + <div class="span-1 border"><p>4</p></div> + <div class="span-1 border"><p>5</p></div> + <div class="span-1 border"><p>6</p></div> + <div class="span-1 border"><p>7</p></div> + <div class="span-1 border"><p>8</p></div> + <div class="span-1 border"><p>9</p></div> + <div class="span-1 border"><p>10</p></div> + <div class="span-1 border"><p>11</p></div> + <div class="span-1 border"><p>12</p></div> + <div class="span-1 border"><p>13</p></div> + <div class="span-1 border"><p>14</p></div> + <div class="span-1 border"><p>15</p></div> + <div class="span-1 border"><p>16</p></div> + <div class="span-1 border"><p>17</p></div> + <div class="span-1 border"><p>18</p></div> + <div class="span-1 border"><p>19</p></div> + <div class="span-1 border"><p>20</p></div> + <div class="span-1 border"><p>21</p></div> + <div class="span-1 border"><p>22</p></div> + <div class="span-1 border"><p>23</p></div> + <div class="span-1 last"><p>24</p></div> + + + <div class="span-4"><p>1</p></div> + <div class="span-4"><p>2</p></div> + <div class="span-4"><p>3</p></div> + <div class="span-4"><p>4</p></div> + <div class="span-4"><p>5</p></div> + <div class="span-4 last"><p>6</p></div> + + + <div class="prepend-23 span-1 last"><p>24</p></div> + + + <div class="prepend-1 span-1"><p>2</p></div> + <div class="prepend-20 span-1 append-1 last"><p>23</p></div> + <hr> + + <div class="span-24"> + <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p> + </div> + + + <div class="span-12"> + <div class="span-6"> + <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod.</p> + </div> + + <div class="span-6 last"> + <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p> + </div> + + <div class="span-12 last"> + <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p> + </div> + </div> + + <div class="span-12 last"> + <div class="span-6"> + <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod.</p> + </div> + + <div class="span-6 last"> + <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p> + </div> + + <div class="span-12 last"> + <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p> + </div> + </div> + + + <div class="span-14 prepend-5 append-5 last"> + <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p> + </div> + <hr> + + <div class="span-12"> + <h5>TESTING .PUSH-1 TO .PUSH-5</h5> + + <div class="span-2"><img src="test-small.jpg" class="push-1"></div> + <div class="span-10 last"><p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p></div> + + <div class="span-2"><img src="test-small.jpg" class="push-2"></div> + <div class="span-10 last"><p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p></div> + + <div class="span-2"><img src="test-small.jpg" class="push-3"></div> + <div class="span-10 last"><p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p></div> + + <div class="span-2"><img src="test-small.jpg" class="push-4"></div> + <div class="span-10 last"><p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p></div> + + <div class="span-2"><img src="test-small.jpg" class="push-5"></div> + <div class="span-10 last"><p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p></div> + + </div> + + <div class="span-12 last"> + <h5>TESTING .PULL-1 TO .PULL-5</h5> + + <div class="span-10"><p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p></div> + <div class="span-2 last"><img src="test-small.jpg" class="top pull-1"></div> + + <div class="span-10"><p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p></div> + <div class="span-2 last"><img src="test-small.jpg" class="top pull-2"></div> + + <div class="span-10"><p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p></div> + <div class="span-2 last"><img src="test-small.jpg" class="top pull-3"></div> + + <div class="span-10"><p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p></div> + <div class="span-2 last"><img src="test-small.jpg" class="top pull-4"></div> + + <div class="span-10"><p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p></div> + <div class="span-2 last"><img src="test-small.jpg" class="top pull-5"></div> + + </div> + + + + + + <div class="span-24"> + <p><a href="http://validator.w3.org/check?uri=referer"> + <img src="valid.png" alt="Valid HTML 4.01 Strict" height="31" width="88" class="bottom"></a></p> + </div> + + </div> +</body> +</html> diff --git a/build/pgo/blueprint/grid.png b/build/pgo/blueprint/grid.png Binary files differnew file mode 100644 index 000000000..129d4a29f --- /dev/null +++ b/build/pgo/blueprint/grid.png diff --git a/build/pgo/blueprint/print.css b/build/pgo/blueprint/print.css new file mode 100644 index 000000000..661861457 --- /dev/null +++ b/build/pgo/blueprint/print.css @@ -0,0 +1,29 @@ +/* ----------------------------------------------------------------------- + + Blueprint CSS Framework 0.7.1 + http://blueprintcss.googlecode.com + + * Copyright (c) 2007-2008. See LICENSE for more info. + * See README for instructions on how to use Blueprint. + * For credits and origins, see AUTHORS. + * This is a compressed file. See the sources in the 'src' directory. + +----------------------------------------------------------------------- */ + +/* print.css */ +body {line-height:1.5;font-family:"Helvetica Neue", Helvetica, Arial, sans-serif;color:#000;background:none;font-size:10pt;} +.container {background:none;} +hr {background:#ccc;color:#ccc;width:100%;height:2px;margin:2em 0;padding:0;border:none;} +hr.space {background:#fff;color:#fff;} +h1, h2, h3, h4, h5, h6 {font-family:"Helvetica Neue", Arial, "Lucida Grande", sans-serif;} +code {font:.9em "Courier New", Monaco, Courier, monospace;} +img {float:left;margin:1.5em 1.5em 1.5em 0;} +a img {border:none;} +p img.top {margin-top:0;} +blockquote {margin:1.5em;padding:1em;font-style:italic;font-size:.9em;} +.small {font-size:.9em;} +.large {font-size:1.1em;} +.quiet {color:#999;} +.hide {display:none;} +a:link, a:visited {background:transparent;font-weight:700;text-decoration:underline;} +a:link:after, a:visited:after {content:" (" attr(href) ") ";font-size:90%;}
\ No newline at end of file diff --git a/build/pgo/blueprint/sample.html b/build/pgo/blueprint/sample.html new file mode 100644 index 000000000..d2c4dfeb8 --- /dev/null +++ b/build/pgo/blueprint/sample.html @@ -0,0 +1,91 @@ +<!-- This Source Code Form is subject to the terms of the Mozilla Public + - License, v. 2.0. If a copy of the MPL was not distributed with this + - file, You can obtain one at http://mozilla.org/MPL/2.0/. --> + +<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" + "http://www.w3.org/TR/html4/strict.dtd"> + +<html lang="en"> +<head> + <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> + <title>Blueprint Sample Page</title> + + <!-- Framework CSS --> + <link rel="stylesheet" href="screen.css" type="text/css" media="screen, projection"> + <link rel="stylesheet" href="print.css" type="text/css" media="print"> + <!--[if IE]><link rel="stylesheet" href="ie.css" type="text/css" media="screen, projection"><![endif]--> + + <!-- Import fancy-type plugin for the sample page. --> + <link rel="stylesheet" href="fancytype-screen.css" type="text/css" media="screen, projection"> +</head> + +<body> + + <div class="container"> + <h1>A simple sample page</h1> + <hr> + <h2 class="alt">This sample page demonstrates a tiny fraction of what you get with Blueprint.</h2> + <hr> + + <div class="span-7 colborder"> + <h6>Here's a box</h6> + <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip.</p> + </div> + + <div class="span-8 colborder"> + <h6>And another box</h6> + <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat laboris nisi ut aliquip.</p> + </div> + + <div class="span-7 last"> + <h6>This box is aligned with the sidebar</h6> + <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip.</p> + </div> + <hr> + <hr class="space"> + + <div class="span-15 prepend-1 colborder"> + <p><img src="test.jpg" class="top pull-1" alt="test">Lorem ipsum dolor sit amet, <em>consectetuer adipiscing elit</em>. Nunc congue ipsum vestibulum libero. Aenean vitae justo. Nam eget tellus. Etiam convallis, est eu lobortis mattis, lectus tellus tempus felis, a ultricies erat ipsum at metus.</p> + <p>Lorem ipsum dolor sit amet, consectetuer adipiscing elit. <a href="#">Morbi et risus</a>. Aliquam nisl. Nulla facilisi. Cras accumsan vestibulum ante. Vestibulum sed tortor. Praesent <span class="caps">SMALL CAPS</span> tempus fringilla elit. Ut elit diam, sagittis in, nonummy in, gravida non, nunc. Ut orci. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos hymenaeos. Nam egestas, orci eu imperdiet malesuada, nisl purus fringilla odio, quis commodo est orci vitae justo. Aliquam placerat odio tincidunt nulla. Cras in libero. Aenean rutrum, magna non tristique posuere, erat odio eleifend nisl, non convallis est tortor blandit ligula. Nulla id augue.</p> + <p>Nullam mattis, odio ut tempus facilisis, metus nisl facilisis metus, auctor consectetuer felis ligula nec mauris. Vestibulum odio erat, fermentum at, commodo vitae, ultrices et, urna. Mauris vulputate, mi pulvinar sagittis condimentum, sem nulla aliquam velit, sed imperdiet mi purus eu magna. Nulla varius metus ut eros. Aenean aliquet magna eget orci. Class aptent taciti sociosqu ad litora.</p> + <p>Vivamus euismod. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Suspendisse vel nibh ut turpis dictum sagittis. Aliquam vel velit a elit auctor sollicitudin. Nam vel dui vel neque lacinia pretium. Quisque nunc erat, venenatis id, volutpat ut, scelerisque sed, diam. Mauris ante. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Donec mattis. Morbi dignissim sollicitudin libero. Nulla lorem.</p> + <blockquote> + <p>Integer cursus ornare mauris. Praesent nisl arcu, imperdiet eu, ornare id, scelerisque ut, nunc. Praesent sagittis erat sed velit tempus imperdiet. Ut tristique, ante in interdum hendrerit, erat enim faucibus felis, quis rutrum mauris lorem quis sem. Vestibulum ligula nisi, mattis nec, posuere et, blandit eu, ligula. Nam suscipit placerat odio. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos hymenaeos. Pellentesque tortor libero, venenatis vitae, rhoncus eu, placerat ut, mi. Nulla nulla.</p> + </blockquote> + <p>Maecenas vel metus quis magna pharetra fermentum. <em>Integer sit amet tortor</em>. Maecenas porttitor, pede sed gravida auctor, nulla augue aliquet elit, at pretium urna orci ut metus. Aliquam in dolor. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Sed aliquam, tellus id ornare posuere, quam nunc accumsan turpis, at convallis tellus orci et nisl. Phasellus congue neque a lorem.</p> + + <hr> + <div class="span-7 colborder"> + <h6>This is a nested column</h6> + <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.</p> + </div> + <div class="span-7 last"> + <h6>This is another nested column</h6> + <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.</p> + </div> + + </div> + <div class="span-7 last"> + + <h3>A <span class="alt">Simple</span> Sidebar</h3> + + <p>Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Cras ornare mattis nunc. Mauris venenatis, pede sed aliquet vehicula, lectus tellus pulvinar neque, non cursus sem nisi vel augue.</p> + <p>Mauris a lectus. Aliquam erat volutpat. Phasellus ultrices mi a sapien. Nunc rutrum egestas lorem. Duis ac sem sagittis elit tincidunt gravida. Mauris a lectus. Aliquam erat volutpat. Phasellus ultrices mi a sapien. Nunc rutrum egestas lorem. Duis ac sem sagittis elit tincidunt gravida.</p> + <p class="quiet">Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Cras ornare mattis nunc. Mauris venenatis, pede sed aliquet vehicula, lectus tellus pulvinar neque, non cursus sem nisi vel augue.</p> + + <h5>Incremental leading</h5> + <p class="incr">Vestibulum ante ipsum primis in faucibus orci luctus vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Cras ornare mattis nunc. Mauris venenatis, pede sed aliquet vehicula, lectus tellus pulvinar neque, non cursus sem nisi vel augue. sed aliquet vehicula, lectus tellus.</p> + <p class="incr">Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Cras ornare mattis nunc. Mauris venenatis, pede sed aliquet vehicula, lectus tellus pulvinar neque, non cursus sem nisi vel augue. sed aliquet vehicula, lectus tellus pulvinar neque, non cursus sem nisi vel augue. ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Cras ornare mattis nunc. Mauris venenatis, pede sed aliquet vehicula, lectus tellus pulvinar neque, non cursus sem nisi vel augue. sed aliquet vehicula, lectus tellus pulvinar neque, non cursus sem nisi vel augue.</p> + + </div> + + <hr> + <h2 class="alt">You may pick and choose amongst these and many more features, so be bold.</h2> + <hr> + + <p><a href="http://validator.w3.org/check?uri=referer"> + <img src="valid.png" alt="Valid HTML 4.01 Strict" height="31" width="88" class="top"></a></p> + </div> + +</body> +</html> diff --git a/build/pgo/blueprint/screen.css b/build/pgo/blueprint/screen.css new file mode 100644 index 000000000..c631ead6a --- /dev/null +++ b/build/pgo/blueprint/screen.css @@ -0,0 +1,226 @@ +/* ----------------------------------------------------------------------- + + Blueprint CSS Framework 0.7.1 + http://blueprintcss.googlecode.com + + * Copyright (c) 2007-2008. See LICENSE for more info. + * See README for instructions on how to use Blueprint. + * For credits and origins, see AUTHORS. + * This is a compressed file. See the sources in the 'src' directory. + +----------------------------------------------------------------------- */ + +/* reset.css */ +html, body, div, span, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, code, del, dfn, em, img, q, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td {margin:0;padding:0;border:0;font-weight:inherit;font-style:inherit;font-size:100%;font-family:inherit;vertical-align:baseline;} +body {line-height:1.5;} +table {border-collapse:separate;border-spacing:0;} +caption, th, td {text-align:left;font-weight:normal;} +table, td, th {vertical-align:middle;} +blockquote:before, blockquote:after, q:before, q:after {content:"";} +blockquote, q {quotes:"" "";} +a img {border:none;} + +/* typography.css */ +body {font-size:75%;color:#222;background:#fff;font-family:"Helvetica Neue", Helvetica, Arial, sans-serif;} +h1, h2, h3, h4, h5, h6 {font-weight:normal;color:#111;} +h1 {font-size:3em;line-height:1;margin-bottom:0.5em;} +h2 {font-size:2em;margin-bottom:0.75em;} +h3 {font-size:1.5em;line-height:1;margin-bottom:1em;} +h4 {font-size:1.2em;line-height:1.25;margin-bottom:1.25em;height:1.25em;} +h5 {font-size:1em;font-weight:bold;margin-bottom:1.5em;} +h6 {font-size:1em;font-weight:bold;} +h1 img, h2 img, h3 img, h4 img, h5 img, h6 img {margin:0;} +p {margin:0 0 1.5em;} +p img {float:left;margin:1.5em 1.5em 1.5em 0;padding:0;} +p img.right {float:right;margin:1.5em 0 1.5em 1.5em;} +a:focus, a:hover {color:#000;} +a {color:#009;text-decoration:underline;} +blockquote {margin:1.5em;color:#666;font-style:italic;} +strong {font-weight:bold;} +em, dfn {font-style:italic;} +dfn {font-weight:bold;} +sup, sub {line-height:0;} +abbr, acronym {border-bottom:1px dotted #666;} +address {margin:0 0 1.5em;font-style:italic;} +del {color:#666;} +pre, code {margin:1.5em 0;white-space:pre;} +pre, code, tt {font:1em 'andale mono', 'lucida console', monospace;line-height:1.5;} +li ul, li ol {margin:0 1.5em;} +ul, ol {margin:0 1.5em 1.5em 1.5em;} +ul {list-style-type:disc;} +ol {list-style-type:decimal;} +dl {margin:0 0 1.5em 0;} +dl dt {font-weight:bold;} +dd {margin-left:1.5em;} +table {margin-bottom:1.4em;width:100%;} +th {font-weight:bold;background:#C3D9FF;} +th, td {padding:4px 10px 4px 5px;} +tr.even td {background:#E5ECF9;} +tfoot {font-style:italic;} +caption {background:#eee;} +.small {font-size:.8em;margin-bottom:1.875em;line-height:1.875em;} +.large {font-size:1.2em;line-height:2.5em;margin-bottom:1.25em;} +.hide {display:none;} +.quiet {color:#666;} +.loud {color:#000;} +.highlight {background:#ff0;} +.added {background:#060;color:#fff;} +.removed {background:#900;color:#fff;} +.first {margin-left:0;padding-left:0;} +.last {margin-right:0;padding-right:0;} +.top {margin-top:0;padding-top:0;} +.bottom {margin-bottom:0;padding-bottom:0;} + +/* grid.css */ +.container {width:950px;margin:0 auto;} +.showgrid {background:url(grid.png);} +body {margin:1.5em 0;} +div.span-1, div.span-2, div.span-3, div.span-4, div.span-5, div.span-6, div.span-7, div.span-8, div.span-9, div.span-10, div.span-11, div.span-12, div.span-13, div.span-14, div.span-15, div.span-16, div.span-17, div.span-18, div.span-19, div.span-20, div.span-21, div.span-22, div.span-23, div.span-24 {float:left;margin-right:10px;} +div.last {margin-right:0;} +.span-1 {width:30px;} +.span-2 {width:70px;} +.span-3 {width:110px;} +.span-4 {width:150px;} +.span-5 {width:190px;} +.span-6 {width:230px;} +.span-7 {width:270px;} +.span-8 {width:310px;} +.span-9 {width:350px;} +.span-10 {width:390px;} +.span-11 {width:430px;} +.span-12 {width:470px;} +.span-13 {width:510px;} +.span-14 {width:550px;} +.span-15 {width:590px;} +.span-16 {width:630px;} +.span-17 {width:670px;} +.span-18 {width:710px;} +.span-19 {width:750px;} +.span-20 {width:790px;} +.span-21 {width:830px;} +.span-22 {width:870px;} +.span-23 {width:910px;} +.span-24, div.span-24 {width:950px;margin:0;} +.append-1 {padding-right:40px;} +.append-2 {padding-right:80px;} +.append-3 {padding-right:120px;} +.append-4 {padding-right:160px;} +.append-5 {padding-right:200px;} +.append-6 {padding-right:240px;} +.append-7 {padding-right:280px;} +.append-8 {padding-right:320px;} +.append-9 {padding-right:360px;} +.append-10 {padding-right:400px;} +.append-11 {padding-right:440px;} +.append-12 {padding-right:480px;} +.append-13 {padding-right:520px;} +.append-14 {padding-right:560px;} +.append-15 {padding-right:600px;} +.append-16 {padding-right:640px;} +.append-17 {padding-right:680px;} +.append-18 {padding-right:720px;} +.append-19 {padding-right:760px;} +.append-20 {padding-right:800px;} +.append-21 {padding-right:840px;} +.append-22 {padding-right:880px;} +.append-23 {padding-right:920px;} +.prepend-1 {padding-left:40px;} +.prepend-2 {padding-left:80px;} +.prepend-3 {padding-left:120px;} +.prepend-4 {padding-left:160px;} +.prepend-5 {padding-left:200px;} +.prepend-6 {padding-left:240px;} +.prepend-7 {padding-left:280px;} +.prepend-8 {padding-left:320px;} +.prepend-9 {padding-left:360px;} +.prepend-10 {padding-left:400px;} +.prepend-11 {padding-left:440px;} +.prepend-12 {padding-left:480px;} +.prepend-13 {padding-left:520px;} +.prepend-14 {padding-left:560px;} +.prepend-15 {padding-left:600px;} +.prepend-16 {padding-left:640px;} +.prepend-17 {padding-left:680px;} +.prepend-18 {padding-left:720px;} +.prepend-19 {padding-left:760px;} +.prepend-20 {padding-left:800px;} +.prepend-21 {padding-left:840px;} +.prepend-22 {padding-left:880px;} +.prepend-23 {padding-left:920px;} +div.border {padding-right:4px;margin-right:5px;border-right:1px solid #eee;} +div.colborder {padding-right:24px;margin-right:25px;border-right:1px solid #eee;} +.pull-1 {margin-left:-40px;} +.pull-2 {margin-left:-80px;} +.pull-3 {margin-left:-120px;} +.pull-4 {margin-left:-160px;} +.pull-5 {margin-left:-200px;} +.pull-6 {margin-left:-240px;} +.pull-7 {margin-left:-280px;} +.pull-8 {margin-left:-320px;} +.pull-9 {margin-left:-360px;} +.pull-10 {margin-left:-400px;} +.pull-11 {margin-left:-440px;} +.pull-12 {margin-left:-480px;} +.pull-13 {margin-left:-520px;} +.pull-14 {margin-left:-560px;} +.pull-15 {margin-left:-600px;} +.pull-16 {margin-left:-640px;} +.pull-17 {margin-left:-680px;} +.pull-18 {margin-left:-720px;} +.pull-19 {margin-left:-760px;} +.pull-20 {margin-left:-800px;} +.pull-21 {margin-left:-840px;} +.pull-22 {margin-left:-880px;} +.pull-23 {margin-left:-920px;} +.pull-24 {margin-left:-960px;} +.pull-1, .pull-2, .pull-3, .pull-4, .pull-5, .pull-6, .pull-7, .pull-8, .pull-9, .pull-10, .pull-11, .pull-12, .pull-13, .pull-14, .pull-15, .pull-16, .pull-17, .pull-18, .pull-19, .pull-20, .pull-21, .pull-22, .pull-23, .pull-24 {float:left;position:relative;} +.push-1 {margin:0 -40px 1.5em 40px;} +.push-2 {margin:0 -80px 1.5em 80px;} +.push-3 {margin:0 -120px 1.5em 120px;} +.push-4 {margin:0 -160px 1.5em 160px;} +.push-5 {margin:0 -200px 1.5em 200px;} +.push-6 {margin:0 -240px 1.5em 240px;} +.push-7 {margin:0 -280px 1.5em 280px;} +.push-8 {margin:0 -320px 1.5em 320px;} +.push-9 {margin:0 -360px 1.5em 360px;} +.push-10 {margin:0 -400px 1.5em 400px;} +.push-11 {margin:0 -440px 1.5em 440px;} +.push-12 {margin:0 -480px 1.5em 480px;} +.push-13 {margin:0 -520px 1.5em 520px;} +.push-14 {margin:0 -560px 1.5em 560px;} +.push-15 {margin:0 -600px 1.5em 600px;} +.push-16 {margin:0 -640px 1.5em 640px;} +.push-17 {margin:0 -680px 1.5em 680px;} +.push-18 {margin:0 -720px 1.5em 720px;} +.push-19 {margin:0 -760px 1.5em 760px;} +.push-20 {margin:0 -800px 1.5em 800px;} +.push-21 {margin:0 -840px 1.5em 840px;} +.push-22 {margin:0 -880px 1.5em 880px;} +.push-23 {margin:0 -920px 1.5em 920px;} +.push-24 {margin:0 -960px 1.5em 960px;} +.push-1, .push-2, .push-3, .push-4, .push-5, .push-6, .push-7, .push-8, .push-9, .push-10, .push-11, .push-12, .push-13, .push-14, .push-15, .push-16, .push-17, .push-18, .push-19, .push-20, .push-21, .push-22, .push-23, .push-24 {float:right;position:relative;} +.box {padding:1.5em;margin-bottom:1.5em;background:#E5ECF9;} +hr {background:#ddd;color:#ddd;clear:both;float:none;width:100%;height:.1em;margin:0 0 1.45em;border:none;} +hr.space {background:#fff;color:#fff;} +.clearfix:after, .container:after {content:".";display:block;height:0;clear:both;visibility:hidden;} +.clearfix, .container {display:inline-block;} +* html .clearfix, * html .container {height:1%;} +.clearfix, .container {display:block;} +.clear {clear:both;} + +/* forms.css */ +label {font-weight:bold;} +fieldset {padding:1.4em;margin:0 0 1.5em 0;border:1px solid #ccc;} +legend {font-weight:bold;font-size:1.2em;} +input.text, input.title, textarea, select {margin:0.5em 0;border:1px solid #bbb;} +input.text:focus, input.title:focus, textarea:focus, select:focus {border:1px solid #666;} +input.text, input.title {width:300px;padding:5px;} +input.title {font-size:1.5em;} +textarea {width:390px;height:250px;padding:5px;} +.error, .notice, .success {padding:.8em;margin-bottom:1em;border:2px solid #ddd;} +.error {background:#FBE3E4;color:#8a1f11;border-color:#FBC2C4;} +.notice {background:#FFF6BF;color:#514721;border-color:#FFD324;} +.success {background:#E6EFC2;color:#264409;border-color:#C6D880;} +.error a {color:#8a1f11;} +.notice a {color:#514721;} +.success a {color:#264409;}
\ No newline at end of file diff --git a/build/pgo/blueprint/test-small.jpg b/build/pgo/blueprint/test-small.jpg Binary files differnew file mode 100644 index 000000000..aa599d99a --- /dev/null +++ b/build/pgo/blueprint/test-small.jpg diff --git a/build/pgo/blueprint/test.jpg b/build/pgo/blueprint/test.jpg Binary files differnew file mode 100644 index 000000000..0107be273 --- /dev/null +++ b/build/pgo/blueprint/test.jpg diff --git a/build/pgo/blueprint/valid.png b/build/pgo/blueprint/valid.png Binary files differnew file mode 100644 index 000000000..dd20e497e --- /dev/null +++ b/build/pgo/blueprint/valid.png diff --git a/build/pgo/certs/README b/build/pgo/certs/README new file mode 100644 index 000000000..ba2b346f9 --- /dev/null +++ b/build/pgo/certs/README @@ -0,0 +1,9 @@ +The certificate authority and server certificates here are generated by $topsrcdir/build/pgo/genpgocert.py. + +You can generate a new CA cert by running: +$objdir/_virtualenv/bin/python $topsrcdir/build/pgo/genpgocert.py --gen-ca + +You can generate new server certificates by running: +$objdir/_virtualenv/bin/python $topsrcdir/build/pgo/genpgocert.py --gen-server + +These will place the new files in this directory where you can commit them. diff --git a/build/pgo/certs/bug483440-attack2b.ca b/build/pgo/certs/bug483440-attack2b.ca new file mode 100644 index 000000000..8adcf2ac1 --- /dev/null +++ b/build/pgo/certs/bug483440-attack2b.ca @@ -0,0 +1,14 @@ +-----BEGIN CERTIFICATE----- +MIICKDCCAZGgAwIBAgIFAIyjFPowDQYJKoZIhvcNAQEFBQAwKDEXMBUGA1UEAwwO
+KgB3d3cubXlDQS5vcmcxDTALBgNVBAMTBG15Q0EwHhcNMDkwMzE0MTg0NzU2WhcN
+MTkwMzE0MTg0NzU2WjBhMRMwEQYDVQQKEwpCYWRndXkgSW5jMRcwFQYDVQQDEw53
+d3cuYmFkZ3V5LmNvbTEZMBcGA1UECxMQSGFja2luZyBEaXZpc2lvbjEWMBQGBFUE
+gAMTDHd3dy5iYW5rLmNvbTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA2YvL
+GgmF0OTLBKz0nYTvR+DlnZai7b2MqAIM9IUEpMfqzJPNYCsXziYXgHtr/do9ppJP
+BhDjeyIGEOSpgBqdkWItxlLopUHnf8VKwnDPPj4KkNyXuTLm60X/ph+/zrjTw+kU
+m+/kVYstgGMuTIoTuu7loxCqqeVlAgc5lzTpUhkCAwEAAaMlMCMwDAYDVR0TAQH/
+BAIwADATBgNVHSUEDDAKBggrBgEFBQcDATANBgkqhkiG9w0BAQUFAAOBgQAKHl1G
+vaXftj5QPK3eIT6Q3fWuGKR39grlg5GL/WocPanYycOlm9zvT1Hx95cY6msIXSKp
+xycndJ02ODX35DDgolV6VHUsM9yoagk+eqs5kCqW2qiv3moIshL0yWVhuCobMA+E
+D3wHFCPqVU+igRdCrEQDxZHoFOR4J/DKHfGANg== +-----END CERTIFICATE----- diff --git a/build/pgo/certs/bug483440-attack7.ca b/build/pgo/certs/bug483440-attack7.ca new file mode 100644 index 000000000..778e7aced --- /dev/null +++ b/build/pgo/certs/bug483440-attack7.ca @@ -0,0 +1,16 @@ +-----BEGIN CERTIFICATE----- +MIICljCCAf+gAwIBAgIBATANBgkqhkiG9w0BAQUFADCBkDERMA8GBPMlBAMTB2F0
+dGFjazcxEDAOBgOABAMTB2F0dGFjazYxEzARBgZVBP///38TB2F0dGFjazUxEjAQ
+BgVVBAOBgRMHYXR0YWNrNDEUMBIGB1UEgICAgIATB2F0dGFjazMxFDASBgdVBJCA
+gIABEwdhdHRhY2syMRQwEgYHVQSIgICAARMHYXR0YWNrMTAeFw0wOTA0MTMxNDAw
+MzdaFw0yOTA0MTMxNDAwMzdaMIGQMREwDwYE8yUEAxMHYXR0YWNrNzEQMA4GA4AE
+AxMHYXR0YWNrNjETMBEGBlUE////fxMHYXR0YWNrNTESMBAGBVUEA4GBEwdhdHRh
+Y2s0MRQwEgYHVQSAgICAgBMHYXR0YWNrMzEUMBIGB1UEkICAgAETB2F0dGFjazIx
+FDASBgdVBIiAgIABEwdhdHRhY2sxMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKB
+gQC77fQ1wrywBnVmr8XO0/78/qFOz+sjnMlpBvLx5UImittgMNSgEqNulRDbO0qG
+K4tlFF2sNsS7aOun6Cq7yl2+a8mIljmjzs+iwCLOEAkQTOM4RsdCosJVy/fjwmH1
+xI0uXt5cPkA0FM7B/IZSzWSC+2gY1+u1AhRJ35bXDhu92wIDAQABMA0GCSqGSIb3
+DQEBBQUAA4GBAFZitQjsQJ1+XsxKchBefilaHsi4oncc05P29IXcRbHI8wK2vNk8
+kkG2c6M4a4Rx1R4C3n99NwXH4vyNUbA9FuMSAdjaS3TW3zm8lKNCuIWGuI2Vvefy
++wNcCfb8B4AuP8pZOqqKsspgiBAE1EPPErnb7nMVLCnf+ts9ARXLBZTi +-----END CERTIFICATE----- diff --git a/build/pgo/certs/bug483440-pk10oflo.ca b/build/pgo/certs/bug483440-pk10oflo.ca new file mode 100644 index 000000000..7daef524b --- /dev/null +++ b/build/pgo/certs/bug483440-pk10oflo.ca @@ -0,0 +1,14 @@ +-----BEGIN CERTIFICATE----- +MIICMTCCAZqgAwIBAgIFAIyjFTAwDQYJKoZIhvcNAQEFBQAwKDEXMBUGA1UEAwwO
+KgB3d3cubXlDQS5vcmcxDTALBgNVBAMTBG15Q0EwHhcNMDkwMzE0MTg0ODI0WhcN
+MTkwMzE0MTg0ODI0WjBqMRMwEQYDVQQKEwpCYWRndXkgSW5jMRcwFQYDVQQDEw53
+d3cuYmFkZ3V5LmNvbTEZMBcGA1UECxMQSGFja2luZyBEaXZpc2lvbjEfMB0GDVUE
+goCAgICAgICAgAMTDHd3dy5iYW5rLmNvbTCBnzANBgkqhkiG9w0BAQEFAAOBjQAw
+gYkCgYEA2YvLGgmF0OTLBKz0nYTvR+DlnZai7b2MqAIM9IUEpMfqzJPNYCsXziYX
+gHtr/do9ppJPBhDjeyIGEOSpgBqdkWItxlLopUHnf8VKwnDPPj4KkNyXuTLm60X/
+ph+/zrjTw+kUm+/kVYstgGMuTIoTuu7loxCqqeVlAgc5lzTpUhkCAwEAAaMlMCMw
+DAYDVR0TAQH/BAIwADATBgNVHSUEDDAKBggrBgEFBQcDATANBgkqhkiG9w0BAQUF
+AAOBgQBr+ekYoADBm6kbHBR1oc/6O9ZciRsTbxIAl3xjA3kNEeiUXXSoe+1dlt3Z
+7D6EaQztqR8usCW728J3vi8p/XxociK3r4aq0Sxu29gp21N1V/Um8y3ssI+Yt9Im
+oHlo5ikUXra5PtGAwi4FymrU5dWlHxYk1PlNP5nsvxdElPZnZA== +-----END CERTIFICATE----- diff --git a/build/pgo/certs/cert8.db b/build/pgo/certs/cert8.db Binary files differnew file mode 100644 index 000000000..aa38b3b0d --- /dev/null +++ b/build/pgo/certs/cert8.db diff --git a/build/pgo/certs/evintermediate.ca b/build/pgo/certs/evintermediate.ca new file mode 100644 index 000000000..17a048ad8 --- /dev/null +++ b/build/pgo/certs/evintermediate.ca @@ -0,0 +1,34 @@ +-----BEGIN CERTIFICATE----- +MIIF9zCCBN+gAwIBAgIBAzANBgkqhkiG9w0BAQUFADCB4TELMAkGA1UEBhMCVVMx
+CzAJBgNVBAgTAkNBMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MSMwIQYDVQQKExpN
+b3ppbGxhIC0gRVYgZGVidWcgdGVzdCBDQTEdMBsGA1UECxMUU2VjdXJpdHkgRW5n
+aW5lZXJpbmcxJjAkBgNVBAMTHUVWIFRlc3RpbmcgKHVudHJ1c3R3b3J0aHkpIENB
+MRMwEQYDVQQpEwpldi10ZXN0LWNhMSwwKgYJKoZIhvcNAQkBFh1jaGFybGF0YW5A
+dGVzdGluZy5leGFtcGxlLmNvbTAeFw0xMzAyMTQxNzU5MDlaFw0yMzAyMTIxNzU5
+MDlaMIHRMQswCQYDVQQGEwJVUzELMAkGA1UECBMCQ0ExFjAUBgNVBAcTDU1vdW50
+YWluIFZpZXcxIzAhBgNVBAoTGk1vemlsbGEgLSBFViBkZWJ1ZyB0ZXN0IENBMR0w
+GwYDVQQLExRTZWN1cml0eSBFbmdpbmVlcmluZzEWMBQGA1UEAxMNaW50ZXJtZWRp
+YXRlMzETMBEGA1UEKRMKZXYtdGVzdC1jYTEsMCoGCSqGSIb3DQEJARYdY2hhcmxh
+dGFuQHRlc3RpbmcuZXhhbXBsZS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw
+ggEKAoIBAQDAfzrlJdawr7v8m7lslODk5FTqCiBO7tPxnWhAOEL5g05knLTZTc5J
+3ywmGoW6ae6RwPlWuqRuFd2Ea+yCawyjkUoLOpFH/xziDzvaS6LXNdJoxQqWk/LX
+8YYQVFfmxh8E11fz74IoCzX++mY1byaNONf3bLU2HU8vnVvENr1gy9Bzpm8wUuKm
+HkBYuG0SVzaeym2H/mo5PJICPVhPa+YxfEVS8EIFCigXGH7xrz/bPXnpfgsSJTnN
+4amBNkORfjf7H9x6IWkJGEkIvkVoYKT4iQ9q6/C4YDjWa9p5lA4F/qxnJefezH/I
+6hcqEODSaDsY+I6vsN8ks8r8MTTnd7BjAgMBAAGjggHGMIIBwjAdBgNVHQ4EFgQU
+fluXMAT0ZS21pV13vv46m8k7nRkwggEYBgNVHSMEggEPMIIBC4AUyJg651hwk+3B
+V0rQvQZv9n2bWPahgeekgeQwgeExCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJDQTEW
+MBQGA1UEBxMNTW91bnRhaW4gVmlldzEjMCEGA1UEChMaTW96aWxsYSAtIEVWIGRl
+YnVnIHRlc3QgQ0ExHTAbBgNVBAsTFFNlY3VyaXR5IEVuZ2luZWVyaW5nMSYwJAYD
+VQQDEx1FViBUZXN0aW5nICh1bnRydXN0d29ydGh5KSBDQTETMBEGA1UEKRMKZXYt
+dGVzdC1jYTEsMCoGCSqGSIb3DQEJARYdY2hhcmxhdGFuQHRlc3RpbmcuZXhhbXBs
+ZS5jb22CCQCvxT0iZiZJMjAMBgNVHRMEBTADAQH/MDYGA1UdHwQvMC0wK6ApoCeG
+JWh0dHA6Ly9leGFtcGxlLmNvbS9yb290LWV2LXRlc3Rlci5jcmwwPwYDVR0gBDgw
+NjA0BgRVHSAAMCwwKgYIKwYBBQUHAgEWHmh0dHA6Ly9teXRlc3Rkb21haW4ubG9j
+YWwvY3BzOzANBgkqhkiG9w0BAQUFAAOCAQEAC4grNTV5K8yqiAJ/0f6oIkTMqyJ4
+lyHXvvKXMHTpRZ7Jdy0aq5KTSHswx64ZRN7V2ds+czzDWgxX3rBuZZAgOW1JYva3
+Ps3XRYUiaTW8eeaWjuVRFAp7ytRmSsOGeOtHbez8jDmTqPRQ1mTMsMzpY4bFD8do
+5y0xsbz4DYIeeNnX9+XGB5u2ml8t5L8Cj65wwMAx9HlsjTrfQTMIwpwbNle6GuZ3
+9FzmE2piAND73yCgU5W66K2lZg8N6vHBq0UhPDCF72y8MlHxQOpTr3/jIGr4X7k9
+uyYq0Pw5Y/LKyGbyW5iMFdLzabm1ua8IWAf7DSFMH6L3WlK8mngCfJ1icQ== +-----END CERTIFICATE----- diff --git a/build/pgo/certs/evroot.ca b/build/pgo/certs/evroot.ca new file mode 100644 index 000000000..df220e446 --- /dev/null +++ b/build/pgo/certs/evroot.ca @@ -0,0 +1,32 @@ +-----BEGIN CERTIFICATE----- +MIIFljCCBH6gAwIBAgIJAK/FPSJmJkkyMA0GCSqGSIb3DQEBBQUAMIHhMQswCQYD
+VQQGEwJVUzELMAkGA1UECBMCQ0ExFjAUBgNVBAcTDU1vdW50YWluIFZpZXcxIzAh
+BgNVBAoTGk1vemlsbGEgLSBFViBkZWJ1ZyB0ZXN0IENBMR0wGwYDVQQLExRTZWN1
+cml0eSBFbmdpbmVlcmluZzEmMCQGA1UEAxMdRVYgVGVzdGluZyAodW50cnVzdHdv
+cnRoeSkgQ0ExEzARBgNVBCkTCmV2LXRlc3QtY2ExLDAqBgkqhkiG9w0BCQEWHWNo
+YXJsYXRhbkB0ZXN0aW5nLmV4YW1wbGUuY29tMB4XDTEzMDIxNDE3NDkwMFoXDTIz
+MDIxMjE3NDkwMFowgeExCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJDQTEWMBQGA1UE
+BxMNTW91bnRhaW4gVmlldzEjMCEGA1UEChMaTW96aWxsYSAtIEVWIGRlYnVnIHRl
+c3QgQ0ExHTAbBgNVBAsTFFNlY3VyaXR5IEVuZ2luZWVyaW5nMSYwJAYDVQQDEx1F
+ViBUZXN0aW5nICh1bnRydXN0d29ydGh5KSBDQTETMBEGA1UEKRMKZXYtdGVzdC1j
+YTEsMCoGCSqGSIb3DQEJARYdY2hhcmxhdGFuQHRlc3RpbmcuZXhhbXBsZS5jb20w
+ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCk+k5mvnrxhVdVnhaxCeDG
+ZC5kcC8951K3xTkh2JMtUpSQL2IoGLOZOWTNY+2wGNyHbdJjKDv1d0bzZfz3yDkB
+AbY6OcxS4WkwccKsyIzkdacpYWhi7kEFevm9p7TI8jdrpKmItrlqfZKfteh+K+DF
+XZF7xp6zpoUis6dykmk5v8RivpCZl7HIlsOW0wSqCocXWH/WWFgAQyozjW8MgGOL
+/eV2aLsx+yg7it9GMMtyidggwvlYM7O8vY0gJqQKXntbHq1zV7jIJ3bXzJceur+G
+Ce4HvsRHAQUSl6jUfm00aKkqS+1t3svZURIKM6qWAuIKMGcspv+L8lyn1KImG8M5
+AgMBAAGjggFNMIIBSTAdBgNVHQ4EFgQUyJg651hwk+3BV0rQvQZv9n2bWPYwggEY
+BgNVHSMEggEPMIIBC4AUyJg651hwk+3BV0rQvQZv9n2bWPahgeekgeQwgeExCzAJ
+BgNVBAYTAlVTMQswCQYDVQQIEwJDQTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEj
+MCEGA1UEChMaTW96aWxsYSAtIEVWIGRlYnVnIHRlc3QgQ0ExHTAbBgNVBAsTFFNl
+Y3VyaXR5IEVuZ2luZWVyaW5nMSYwJAYDVQQDEx1FViBUZXN0aW5nICh1bnRydXN0
+d29ydGh5KSBDQTETMBEGA1UEKRMKZXYtdGVzdC1jYTEsMCoGCSqGSIb3DQEJARYd
+Y2hhcmxhdGFuQHRlc3RpbmcuZXhhbXBsZS5jb22CCQCvxT0iZiZJMjAMBgNVHRME
+BTADAQH/MA0GCSqGSIb3DQEBBQUAA4IBAQAcKVLC9MbdSZjzkVBseCm6t49aIIBm
+xQrsTNV2Gnp5eIXBfUhNAfD0zbBcKHK9AfHmNT8ZK6iABjiOrnn6yQNufW5MMdNx
++/4FtTmdlBPLpyuBY7re+XbIaPxr/jB9jJ1pmh52xH3wMkO7ATDQ2fqFnODFrUKS
+UpXzuydPnsCdu32KPSnewIrkDB10Sah7vw3uwASO2GWqaFtUDFWGpt6rYQTcOF8g
+7a6Zj0johBMQFHE3HDRebWxiOf21ppN/tvv0gtGiA0ZIXBezeLaJ+Hob1xTbi4sw
+sGYDKHPCrLuTZWXmkv0rAIkLLK4VHbsA5xYPQNJJsTpX3u0Z0vZxJd9/ +-----END CERTIFICATE----- diff --git a/build/pgo/certs/jartests-object.ca b/build/pgo/certs/jartests-object.ca new file mode 100644 index 000000000..fc6d0ffb5 --- /dev/null +++ b/build/pgo/certs/jartests-object.ca @@ -0,0 +1,15 @@ +-----BEGIN CERTIFICATE----- +MIICTTCCAbagAwIBAgIBADANBgkqhkiG9w0BAQUFADBaMRMwEQYDVQQLEwpVbml0
+IFRlc3RzMRgwFgYDVQQKEw9Nb3ppbGxhIFRlc3RpbmcxKTAnBgNVBAMTIFNpZ25l
+ZCBKQVIgQ2VydGlmaWNhdGUgQXV0aG9yaXR5MB4XDTA5MDExNTE0MDkwM1oXDTM0
+MDExNTE0MDkwM1owWjETMBEGA1UECxMKVW5pdCBUZXN0czEYMBYGA1UEChMPTW96
+aWxsYSBUZXN0aW5nMSkwJwYDVQQDEyBTaWduZWQgSkFSIENlcnRpZmljYXRlIEF1
+dGhvcml0eTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAsQd8eUw4WSK7YoKl
+hqe+CjEgI5Rs3TirWtDsfmMtMBmTvRhJpdTeMAFTpWvlOPuXJwkKXMMFLxE8ayNX
+fO5ixCgJ7LrpguOVZ3pY4RvEyE6yh3Hv81Ztblbo120IdcrkyN4KMs5EgeauDllU
+ehhbq9lmnmQxIQs3KPcoMteqAXcCAwEAAaMjMCEwEQYJYIZIAYb4QgEBBAQDAgAH
+MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADgYEAAZHhPT133TcavAKnn37X
+0VE9davrX7t20CLb06KYpgkg7yO0BjIjTnYeJBQgaH652pZVEFT7dbi0JTn4BMXz
+EwOQ2JjzjwNUDHpWAopiCKxAnjwy/kGcZfkKUydwQHKr8m1Faywu1Cyrj0gBHClL
+b2b9ywK4pb545mE6V9pi1zg= +-----END CERTIFICATE----- diff --git a/build/pgo/certs/key3.db b/build/pgo/certs/key3.db Binary files differnew file mode 100644 index 000000000..a99595c52 --- /dev/null +++ b/build/pgo/certs/key3.db diff --git a/build/pgo/certs/mochitest.client b/build/pgo/certs/mochitest.client Binary files differnew file mode 100644 index 000000000..5d72cd2b7 --- /dev/null +++ b/build/pgo/certs/mochitest.client diff --git a/build/pgo/certs/pgoca.ca b/build/pgo/certs/pgoca.ca new file mode 100644 index 000000000..65fb8a589 --- /dev/null +++ b/build/pgo/certs/pgoca.ca @@ -0,0 +1,15 @@ +-----BEGIN CERTIFICATE----- +MIICXTCCAcagAwIBAgIBATANBgkqhkiG9w0BAQUFADBqMSQwIgYDVQQLExtQcm9m +aWxlIEd1aWRlZCBPcHRpbWl6YXRpb24xGDAWBgNVBAoTD01vemlsbGEgVGVzdGlu +ZzEoMCYGA1UEAxMfVGVtcG9yYXJ5IENlcnRpZmljYXRlIEF1dGhvcml0eTAeFw0w +ODA1MjIwMDM4MDVaFw0xODA1MjIwMDM4MDVaMGoxJDAiBgNVBAsTG1Byb2ZpbGUg +R3VpZGVkIE9wdGltaXphdGlvbjEYMBYGA1UEChMPTW96aWxsYSBUZXN0aW5nMSgw +JgYDVQQDEx9UZW1wb3JhcnkgQ2VydGlmaWNhdGUgQXV0aG9yaXR5MIGfMA0GCSqG +SIb3DQEBAQUAA4GNADCBiQKBgQDg6iipAXGZYmgTcHfx8M2hcLqmqDalcj7sZ1A7 +a3LiCBb+1uHKKy9hUxRUe61aJF4NgMAF5oc+HpXN0hpvkiNHxqqD7R6hrkP3gAJ3 +eczEFKsFUI6AqaCL0+xpyhaaZmmarcHxU+PL2h5zq6VssxfBAsO0DkzWzk6E8vM+ +jrku7QIDAQABoxMwETAPBgNVHRMECDAGAQH/AgEAMA0GCSqGSIb3DQEBBQUAA4GB +ALPbn3Ztg0m8qDt8Vkf5You6HEqIxZe+ffDTrfq/L7ofHk/OXEpL7OWKRHU33pNG +QS8khBG+sO461C51s6u9giW+eq2PaQv2HGASBpDbvPqc/Hf+zupZsdsXzHv6rt0V +lu5B6nOpMse1nhA494i1ARSuBNzLv5mas38YWG8Rr6jR +-----END CERTIFICATE----- diff --git a/build/pgo/certs/pgoca.p12 b/build/pgo/certs/pgoca.p12 Binary files differnew file mode 100644 index 000000000..4867c286b --- /dev/null +++ b/build/pgo/certs/pgoca.p12 diff --git a/build/pgo/certs/secmod.db b/build/pgo/certs/secmod.db Binary files differnew file mode 100644 index 000000000..a3341f767 --- /dev/null +++ b/build/pgo/certs/secmod.db diff --git a/build/pgo/favicon.ico b/build/pgo/favicon.ico Binary files differnew file mode 100644 index 000000000..d44438903 --- /dev/null +++ b/build/pgo/favicon.ico diff --git a/build/pgo/genpgocert.py b/build/pgo/genpgocert.py new file mode 100644 index 000000000..c58bcd40d --- /dev/null +++ b/build/pgo/genpgocert.py @@ -0,0 +1,192 @@ +#!/usr/bin/env python +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +# This script exists to generate the Certificate Authority and server +# certificates used for SSL testing in Mochitest. The already generated +# certs are located at $topsrcdir/build/pgo/certs/ . + +import mozinfo +import os +import random +import re +import shutil +import subprocess +import sys +import tempfile + +from mozbuild.base import MozbuildObject +from mozfile import NamedTemporaryFile +from mozprofile.permissions import ServerLocations + +dbFiles = [ + re.compile("^cert[0-9]+\.db$"), + re.compile("^key[0-9]+\.db$"), + re.compile("^secmod\.db$") +] + +def unlinkDbFiles(path): + for root, dirs, files in os.walk(path): + for name in files: + for dbFile in dbFiles: + if dbFile.match(name) and os.path.exists(os.path.join(root, name)): + os.unlink(os.path.join(root, name)) + +def dbFilesExist(path): + for root, dirs, files in os.walk(path): + for name in files: + for dbFile in dbFiles: + if dbFile.match(name) and os.path.exists(os.path.join(root, name)): + return True + return False + + +def runUtil(util, args, inputdata = None): + env = os.environ.copy() + if mozinfo.os == "linux": + pathvar = "LD_LIBRARY_PATH" + app_path = os.path.dirname(util) + if pathvar in env: + env[pathvar] = "%s%s%s" % (app_path, os.pathsep, env[pathvar]) + else: + env[pathvar] = app_path + proc = subprocess.Popen([util] + args, env=env, + stdin=subprocess.PIPE if inputdata else None) + proc.communicate(inputdata) + return proc.returncode + + +def createRandomFile(randomFile): + for count in xrange(0, 2048): + randomFile.write(chr(random.randint(0, 255))) + + +def createCertificateAuthority(build, srcDir): + certutil = build.get_binary_path(what="certutil") + pk12util = build.get_binary_path(what="pk12util") + + #TODO: mozfile.TemporaryDirectory + tempDbDir = tempfile.mkdtemp() + with NamedTemporaryFile() as pwfile, NamedTemporaryFile() as rndfile: + pgoCAModulePathSrc = os.path.join(srcDir, "pgoca.p12") + pgoCAPathSrc = os.path.join(srcDir, "pgoca.ca") + + pwfile.write("\n") + + # Create temporary certification database for CA generation + status = runUtil(certutil, ["-N", "-d", tempDbDir, "-f", pwfile.name]) + if status: + return status + + createRandomFile(rndfile) + status = runUtil(certutil, ["-S", "-d", tempDbDir, "-s", "CN=Temporary Certificate Authority, O=Mozilla Testing, OU=Profile Guided Optimization", "-t", "C,,", "-x", "-m", "1", "-v", "120", "-n", "pgo temporary ca", "-2", "-f", pwfile.name, "-z", rndfile.name], "Y\n0\nN\n") + if status: + return status + + status = runUtil(certutil, ["-L", "-d", tempDbDir, "-n", "pgo temporary ca", "-a", "-o", pgoCAPathSrc, "-f", pwfile.name]) + if status: + return status + + status = runUtil(pk12util, ["-o", pgoCAModulePathSrc, "-n", "pgo temporary ca", "-d", tempDbDir, "-w", pwfile.name, "-k", pwfile.name]) + if status: + return status + + shutil.rmtree(tempDbDir) + return 0 + + +def createSSLServerCertificate(build, srcDir): + certutil = build.get_binary_path(what="certutil") + pk12util = build.get_binary_path(what="pk12util") + + with NamedTemporaryFile() as pwfile, NamedTemporaryFile() as rndfile: + pgoCAPath = os.path.join(srcDir, "pgoca.p12") + + pwfile.write("\n") + + if not dbFilesExist(srcDir): + # Make sure all DB files from src are really deleted + unlinkDbFiles(srcDir) + + # Create certification database for ssltunnel + status = runUtil(certutil, ["-N", "-d", srcDir, "-f", pwfile.name]) + if status: + return status + + status = runUtil(pk12util, ["-i", pgoCAPath, "-w", pwfile.name, "-d", srcDir, "-k", pwfile.name]) + if status: + return status + + # Generate automatic certificate + locations = ServerLocations(os.path.join(build.topsrcdir, + "build", "pgo", + "server-locations.txt")) + iterator = iter(locations) + + # Skips the first entry, I don't know why: bug 879740 + iterator.next() + + locationsParam = "" + firstLocation = "" + for loc in iterator: + if loc.scheme == "https" and "nocert" not in loc.options: + customCertOption = False + customCertRE = re.compile("^cert=(?:\w+)") + for option in loc.options: + match = customCertRE.match(option) + if match: + customCertOption = True + break + + if not customCertOption: + if len(locationsParam) > 0: + locationsParam += "," + locationsParam += loc.host + + if firstLocation == "": + firstLocation = loc.host + + if not firstLocation: + print "Nothing to generate, no automatic secure hosts specified" + else: + createRandomFile(rndfile) + + runUtil(certutil, ["-D", "-n", "pgo server certificate", "-d", srcDir, "-z", rndfile.name, "-f", pwfile.name]) + # Ignore the result, the certificate may not be present when new database is being built + + status = runUtil(certutil, ["-S", "-s", "CN=%s" % firstLocation, "-t", "Pu,,", "-c", "pgo temporary ca", "-m", "2", "-8", locationsParam, "-v", "120", "-n", "pgo server certificate", "-d", srcDir, "-z", rndfile.name, "-f", pwfile.name]) + if status: + return status + + return 0 + +if len(sys.argv) == 1: + print "Specify --gen-server or --gen-ca" + sys.exit(1) + +build = MozbuildObject.from_environment() +certdir = os.path.join(build.topsrcdir, "build", "pgo", "certs") +if sys.argv[1] == "--gen-server": + certificateStatus = createSSLServerCertificate(build, certdir) + if certificateStatus: + print "TEST-UNEXPECTED-FAIL | SSL Server Certificate generation" + + sys.exit(certificateStatus) + +if sys.argv[1] == "--gen-ca": + certificateStatus = createCertificateAuthority(build, certdir) + if certificateStatus: + print "TEST-UNEXPECTED-FAIL | Certificate Authority generation" + else: + print "\n\n" + print "===================================================" + print " IMPORTANT:" + print " To use this new certificate authority in tests" + print " run 'make' at testing/mochitest" + print "===================================================" + + sys.exit(certificateStatus) + +print "Invalid option specified" +sys.exit(1) diff --git a/build/pgo/index.html b/build/pgo/index.html new file mode 100644 index 000000000..4801f4815 --- /dev/null +++ b/build/pgo/index.html @@ -0,0 +1,177 @@ +<script> +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + + function quitHook() + { + var xhr = new XMLHttpRequest(); + xhr.open("GET", "http://" + location.host + "/server/shutdown", true); + xhr.onreadystatechange = function (event) + { + if (xhr.readyState == 4) + goQuitApplication(); + }; + xhr.send(null); + } + + function canQuitApplication() + { + var os = Components.classes["@mozilla.org/observer-service;1"] + .getService(Components.interfaces.nsIObserverService); + if (!os) + { + return true; + } + + try + { + var cancelQuit = Components.classes["@mozilla.org/supports-PRBool;1"] + .createInstance(Components.interfaces.nsISupportsPRBool); + os.notifyObservers(cancelQuit, "quit-application-requested", null); + + // Something aborted the quit process. + if (cancelQuit.data) + { + return false; + } + } + catch (ex) + { + } + os.notifyObservers(null, "quit-application-granted", null); + return true; + } + + function goQuitApplication() + { + const privs = 'UniversalXPConnect'; + + try + { + netscape.security.PrivilegeManager.enablePrivilege(privs); + } + catch(ex) + { + throw('goQuitApplication: privilege failure ' + ex); + } + + if (!canQuitApplication()) + { + return false; + } + + const kAppStartup = '@mozilla.org/toolkit/app-startup;1'; + const kAppShell = '@mozilla.org/appshell/appShellService;1'; + var appService; + var forceQuit; + + if (kAppStartup in Components.classes) + { + appService = Components.classes[kAppStartup]. + getService(Components.interfaces.nsIAppStartup); + forceQuit = Components.interfaces.nsIAppStartup.eForceQuit; + + } + else if (kAppShell in Components.classes) + { + appService = Components.classes[kAppShell]. + getService(Components.interfaces.nsIAppShellService); + forceQuit = Components.interfaces.nsIAppShellService.eForceQuit; + } + else + { + throw 'goQuitApplication: no AppStartup/appShell'; + } + + var windowManager = Components.classes['@mozilla.org/appshell/window-mediator;1'].getService(); + + var windowManagerInterface = windowManager. + QueryInterface(Components.interfaces.nsIWindowMediator); + + var enumerator = windowManagerInterface.getEnumerator(null); + + while (enumerator.hasMoreElements()) + { + var domWindow = enumerator.getNext(); + if (("tryToClose" in domWindow) && !domWindow.tryToClose()) + { + return false; + } + domWindow.close(); + } + + try + { + appService.quit(forceQuit); + } + catch(ex) + { + throw('goQuitApplication: ' + ex); + } + + return true; + } + + var list = + [ + "blueprint/sample.html", + "blueprint/forms.html", + "blueprint/grid.html", + "blueprint/elements.html", + "js-input/3d-cube.html", + "js-input/3d-morph.html", + "js-input/3d-raytrace.html", + "js-input/3d-thingy.html", + "js-input/access-binary-trees.html", + "js-input/access-fannkuch.html", + "js-input/access-nbody.html", + "js-input/access-nsieve.html", + "js-input/bitops-3bit-bits-in-byte.html", + "js-input/bitops-bits-in-byte.html", + "js-input/bitops-bitwise-and.html", + "js-input/bitops-nsieve-bits.html", + "js-input/controlflow-recursive.html", + "js-input/crypto-aes.html", + "js-input/crypto-md5.html", + "js-input/crypto-sha1.html", + "js-input/crypto-otp.html", + "js-input/date-format-tofte.html", + "js-input/date-format-xparb.html", + "js-input/math-cordic.html", + "js-input/math-partial-sums.html", + "js-input/math-spectral-norm.html", + "js-input/regexp-dna.html", + "js-input/string-base64.html", + "js-input/string-fasta.html", + "js-input/string-tagcloud.html", + "js-input/string-unpack-code.html", + "js-input/string-validate-input.html", + "http://acid3.acidtests.org/", + "http://www.webvizbench.com/", + "https://www.google.com/", + "http://wolfbeast.deviantart.com" + ]; + var interval = 8000; //3000; // 15000 + var idx = 0; + var w; + + window.onload = function () { + w = window.open("about:blank"); + window.setTimeout(loadURL, interval); + }; + function loadURL () { + w.location.href = list[idx++]; + if (idx < list.length) { + window.setTimeout(loadURL, interval); + } else { + window.setTimeout(goQuitApplication, interval); + } + } + var i; + + for(i=0; i < list.length;i++) { + document.write(list[i]); + document.write("<br>"); + } + </script> diff --git a/build/pgo/js-input/3d-cube.html b/build/pgo/js-input/3d-cube.html new file mode 100644 index 000000000..453167d44 --- /dev/null +++ b/build/pgo/js-input/3d-cube.html @@ -0,0 +1,387 @@ +<!DOCTYPE html> +<head> +<!-- + Copyright (C) 2007 Apple Inc. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--> + +<title>SunSpider 3d-cube</title> + +</head> + +<body> +<h3>3d-cube</h3> +<div id="console"> +</div> + +<script> + +var _sunSpiderStartDate = new Date(); + +// 3D Cube Rotation +// http://www.speich.net/computer/moztesting/3d.htm +// Created by Simon Speich + +var Q = new Array(); +var MTrans = new Array(); // transformation matrix +var MQube = new Array(); // position information of qube +var I = new Array(); // entity matrix +var Origin = new Object(); +var Testing = new Object(); +var LoopTimer; + +var DisplArea = new Object(); +DisplArea.Width = 300; +DisplArea.Height = 300; + +function DrawLine(From, To) { + var x1 = From.V[0]; + var x2 = To.V[0]; + var y1 = From.V[1]; + var y2 = To.V[1]; + var dx = Math.abs(x2 - x1); + var dy = Math.abs(y2 - y1); + var x = x1; + var y = y1; + var IncX1, IncY1; + var IncX2, IncY2; + var Den; + var Num; + var NumAdd; + var NumPix; + + if (x2 >= x1) { IncX1 = 1; IncX2 = 1; } + else { IncX1 = -1; IncX2 = -1; } + if (y2 >= y1) { IncY1 = 1; IncY2 = 1; } + else { IncY1 = -1; IncY2 = -1; } + if (dx >= dy) { + IncX1 = 0; + IncY2 = 0; + Den = dx; + Num = dx / 2; + NumAdd = dy; + NumPix = dx; + } + else { + IncX2 = 0; + IncY1 = 0; + Den = dy; + Num = dy / 2; + NumAdd = dx; + NumPix = dy; + } + + NumPix = Math.round(Q.LastPx + NumPix); + + var i = Q.LastPx; + for (; i < NumPix; i++) { + Num += NumAdd; + if (Num >= Den) { + Num -= Den; + x += IncX1; + y += IncY1; + } + x += IncX2; + y += IncY2; + } + Q.LastPx = NumPix; +} + +function CalcCross(V0, V1) { + var Cross = new Array(); + Cross[0] = V0[1]*V1[2] - V0[2]*V1[1]; + Cross[1] = V0[2]*V1[0] - V0[0]*V1[2]; + Cross[2] = V0[0]*V1[1] - V0[1]*V1[0]; + return Cross; +} + +function CalcNormal(V0, V1, V2) { + var A = new Array(); var B = new Array(); + for (var i = 0; i < 3; i++) { + A[i] = V0[i] - V1[i]; + B[i] = V2[i] - V1[i]; + } + A = CalcCross(A, B); + var Length = Math.sqrt(A[0]*A[0] + A[1]*A[1] + A[2]*A[2]); + for (var i = 0; i < 3; i++) A[i] = A[i] / Length; + A[3] = 1; + return A; +} + +function CreateP(X,Y,Z) { + this.V = [X,Y,Z,1]; +} + +// multiplies two matrices +function MMulti(M1, M2) { + var M = [[],[],[],[]]; + var i = 0; + var j = 0; + for (; i < 4; i++) { + j = 0; + for (; j < 4; j++) M[i][j] = M1[i][0] * M2[0][j] + M1[i][1] * M2[1][j] + M1[i][2] * M2[2][j] + M1[i][3] * M2[3][j]; + } + return M; +} + +//multiplies matrix with vector +function VMulti(M, V) { + var Vect = new Array(); + var i = 0; + for (;i < 4; i++) Vect[i] = M[i][0] * V[0] + M[i][1] * V[1] + M[i][2] * V[2] + M[i][3] * V[3]; + return Vect; +} + +function VMulti2(M, V) { + var Vect = new Array(); + var i = 0; + for (;i < 3; i++) Vect[i] = M[i][0] * V[0] + M[i][1] * V[1] + M[i][2] * V[2]; + return Vect; +} + +// add to matrices +function MAdd(M1, M2) { + var M = [[],[],[],[]]; + var i = 0; + var j = 0; + for (; i < 4; i++) { + j = 0; + for (; j < 4; j++) M[i][j] = M1[i][j] + M2[i][j]; + } + return M; +} + +function Translate(M, Dx, Dy, Dz) { + var T = [ + [1,0,0,Dx], + [0,1,0,Dy], + [0,0,1,Dz], + [0,0,0,1] + ]; + return MMulti(T, M); +} + +function RotateX(M, Phi) { + var a = Phi; + a *= Math.PI / 180; + var Cos = Math.cos(a); + var Sin = Math.sin(a); + var R = [ + [1,0,0,0], + [0,Cos,-Sin,0], + [0,Sin,Cos,0], + [0,0,0,1] + ]; + return MMulti(R, M); +} + +function RotateY(M, Phi) { + var a = Phi; + a *= Math.PI / 180; + var Cos = Math.cos(a); + var Sin = Math.sin(a); + var R = [ + [Cos,0,Sin,0], + [0,1,0,0], + [-Sin,0,Cos,0], + [0,0,0,1] + ]; + return MMulti(R, M); +} + +function RotateZ(M, Phi) { + var a = Phi; + a *= Math.PI / 180; + var Cos = Math.cos(a); + var Sin = Math.sin(a); + var R = [ + [Cos,-Sin,0,0], + [Sin,Cos,0,0], + [0,0,1,0], + [0,0,0,1] + ]; + return MMulti(R, M); +} + +function DrawQube() { + // calc current normals + var CurN = new Array(); + var i = 5; + Q.LastPx = 0; + for (; i > -1; i--) CurN[i] = VMulti2(MQube, Q.Normal[i]); + if (CurN[0][2] < 0) { + if (!Q.Line[0]) { DrawLine(Q[0], Q[1]); Q.Line[0] = true; }; + if (!Q.Line[1]) { DrawLine(Q[1], Q[2]); Q.Line[1] = true; }; + if (!Q.Line[2]) { DrawLine(Q[2], Q[3]); Q.Line[2] = true; }; + if (!Q.Line[3]) { DrawLine(Q[3], Q[0]); Q.Line[3] = true; }; + } + if (CurN[1][2] < 0) { + if (!Q.Line[2]) { DrawLine(Q[3], Q[2]); Q.Line[2] = true; }; + if (!Q.Line[9]) { DrawLine(Q[2], Q[6]); Q.Line[9] = true; }; + if (!Q.Line[6]) { DrawLine(Q[6], Q[7]); Q.Line[6] = true; }; + if (!Q.Line[10]) { DrawLine(Q[7], Q[3]); Q.Line[10] = true; }; + } + if (CurN[2][2] < 0) { + if (!Q.Line[4]) { DrawLine(Q[4], Q[5]); Q.Line[4] = true; }; + if (!Q.Line[5]) { DrawLine(Q[5], Q[6]); Q.Line[5] = true; }; + if (!Q.Line[6]) { DrawLine(Q[6], Q[7]); Q.Line[6] = true; }; + if (!Q.Line[7]) { DrawLine(Q[7], Q[4]); Q.Line[7] = true; }; + } + if (CurN[3][2] < 0) { + if (!Q.Line[4]) { DrawLine(Q[4], Q[5]); Q.Line[4] = true; }; + if (!Q.Line[8]) { DrawLine(Q[5], Q[1]); Q.Line[8] = true; }; + if (!Q.Line[0]) { DrawLine(Q[1], Q[0]); Q.Line[0] = true; }; + if (!Q.Line[11]) { DrawLine(Q[0], Q[4]); Q.Line[11] = true; }; + } + if (CurN[4][2] < 0) { + if (!Q.Line[11]) { DrawLine(Q[4], Q[0]); Q.Line[11] = true; }; + if (!Q.Line[3]) { DrawLine(Q[0], Q[3]); Q.Line[3] = true; }; + if (!Q.Line[10]) { DrawLine(Q[3], Q[7]); Q.Line[10] = true; }; + if (!Q.Line[7]) { DrawLine(Q[7], Q[4]); Q.Line[7] = true; }; + } + if (CurN[5][2] < 0) { + if (!Q.Line[8]) { DrawLine(Q[1], Q[5]); Q.Line[8] = true; }; + if (!Q.Line[5]) { DrawLine(Q[5], Q[6]); Q.Line[5] = true; }; + if (!Q.Line[9]) { DrawLine(Q[6], Q[2]); Q.Line[9] = true; }; + if (!Q.Line[1]) { DrawLine(Q[2], Q[1]); Q.Line[1] = true; }; + } + Q.Line = [false,false,false,false,false,false,false,false,false,false,false,false]; + Q.LastPx = 0; +} + +function Loop() { + if (Testing.LoopCount > Testing.LoopMax) return; + var TestingStr = String(Testing.LoopCount); + while (TestingStr.length < 3) TestingStr = "0" + TestingStr; + MTrans = Translate(I, -Q[8].V[0], -Q[8].V[1], -Q[8].V[2]); + MTrans = RotateX(MTrans, 1); + MTrans = RotateY(MTrans, 3); + MTrans = RotateZ(MTrans, 5); + MTrans = Translate(MTrans, Q[8].V[0], Q[8].V[1], Q[8].V[2]); + MQube = MMulti(MTrans, MQube); + var i = 8; + for (; i > -1; i--) { + Q[i].V = VMulti(MTrans, Q[i].V); + } + DrawQube(); + Testing.LoopCount++; + Loop(); +} + +function Init(CubeSize) { + // init/reset vars + Origin.V = [150,150,20,1]; + Testing.LoopCount = 0; + Testing.LoopMax = 50; + Testing.TimeMax = 0; + Testing.TimeAvg = 0; + Testing.TimeMin = 0; + Testing.TimeTemp = 0; + Testing.TimeTotal = 0; + Testing.Init = false; + + // transformation matrix + MTrans = [ + [1,0,0,0], + [0,1,0,0], + [0,0,1,0], + [0,0,0,1] + ]; + + // position information of qube + MQube = [ + [1,0,0,0], + [0,1,0,0], + [0,0,1,0], + [0,0,0,1] + ]; + + // entity matrix + I = [ + [1,0,0,0], + [0,1,0,0], + [0,0,1,0], + [0,0,0,1] + ]; + + // create qube + Q[0] = new CreateP(-CubeSize,-CubeSize, CubeSize); + Q[1] = new CreateP(-CubeSize, CubeSize, CubeSize); + Q[2] = new CreateP( CubeSize, CubeSize, CubeSize); + Q[3] = new CreateP( CubeSize,-CubeSize, CubeSize); + Q[4] = new CreateP(-CubeSize,-CubeSize,-CubeSize); + Q[5] = new CreateP(-CubeSize, CubeSize,-CubeSize); + Q[6] = new CreateP( CubeSize, CubeSize,-CubeSize); + Q[7] = new CreateP( CubeSize,-CubeSize,-CubeSize); + + // center of gravity + Q[8] = new CreateP(0, 0, 0); + + // anti-clockwise edge check + Q.Edge = [[0,1,2],[3,2,6],[7,6,5],[4,5,1],[4,0,3],[1,5,6]]; + + // calculate squad normals + Q.Normal = new Array(); + for (var i = 0; i < Q.Edge.length; i++) Q.Normal[i] = CalcNormal(Q[Q.Edge[i][0]].V, Q[Q.Edge[i][1]].V, Q[Q.Edge[i][2]].V); + + // line drawn ? + Q.Line = [false,false,false,false,false,false,false,false,false,false,false,false]; + + // create line pixels + Q.NumPx = 9 * 2 * CubeSize; + for (var i = 0; i < Q.NumPx; i++) CreateP(0,0,0); + + MTrans = Translate(MTrans, Origin.V[0], Origin.V[1], Origin.V[2]); + MQube = MMulti(MTrans, MQube); + + var i = 0; + for (; i < 9; i++) { + Q[i].V = VMulti(MTrans, Q[i].V); + } + DrawQube(); + Testing.Init = true; + Loop(); +} + +for ( var i = 20; i <= 160; i *= 2 ) { + Init(i); +} + +Q = null; +MTrans = null; +MQube = null; +I = null; +Origin = null; +Testing = null; +LoopTime = null; +DisplArea = null; + + +var _sunSpiderInterval = new Date() - _sunSpiderStartDate; + +document.getElementById("console").innerHTML = _sunSpiderInterval; +</script> + + +</body> +</html> diff --git a/build/pgo/js-input/3d-morph.html b/build/pgo/js-input/3d-morph.html new file mode 100644 index 000000000..aca991d39 --- /dev/null +++ b/build/pgo/js-input/3d-morph.html @@ -0,0 +1,104 @@ +<!DOCTYPE html> +<head> +<!-- + Copyright (C) 2007 Apple Inc. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--> + +<title>SunSpider 3d-morph</title> + +</head> + +<body> +<h3>3d-morph</h3> +<div id="console"> +</div> + +<script> + +var _sunSpiderStartDate = new Date(); + +/* + * Copyright (C) 2007 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +var loops = 15 +var nx = 120 +var nz = 120 + +function morph(a, f) { + var PI2nx = Math.PI * 8/nx + var sin = Math.sin + var f30 = -(50 * sin(f*Math.PI*2)) + + for (var i = 0; i < nz; ++i) { + for (var j = 0; j < nx; ++j) { + a[3*(i*nx+j)+1] = sin((j-1) * PI2nx ) * -f30 + } + } +} + + +var a = Array() +for (var i=0; i < nx*nz*3; ++i) + a[i] = 0 + +for (var i = 0; i < loops; ++i) { + morph(a, i/loops) +} + +testOutput = 0; +for (var i = 0; i < nx; i++) + testOutput += a[3*(i*nx+i)+1]; +a = null; + + +var _sunSpiderInterval = new Date() - _sunSpiderStartDate; + +document.getElementById("console").innerHTML = _sunSpiderInterval; +</script> + + +</body> +</html> diff --git a/build/pgo/js-input/3d-raytrace.html b/build/pgo/js-input/3d-raytrace.html new file mode 100644 index 000000000..2097d4238 --- /dev/null +++ b/build/pgo/js-input/3d-raytrace.html @@ -0,0 +1,490 @@ +<!DOCTYPE html> +<head> +<!-- + Copyright (C) 2007 Apple Inc. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--> + +<title>SunSpider 3d-raytrace</title> +</head> + +<body> +<h3>3d-raytrace</h3> +<div id="console"> +</div> + +<script> + +var _sunSpiderStartDate = new Date(); + +/* + * Copyright (C) 2007 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +function createVector(x,y,z) { + return new Array(x,y,z); +} + +function sqrLengthVector(self) { + return self[0] * self[0] + self[1] * self[1] + self[2] * self[2]; +} + +function lengthVector(self) { + return Math.sqrt(self[0] * self[0] + self[1] * self[1] + self[2] * self[2]); +} + +function addVector(self, v) { + self[0] += v[0]; + self[1] += v[1]; + self[2] += v[2]; + return self; +} + +function subVector(self, v) { + self[0] -= v[0]; + self[1] -= v[1]; + self[2] -= v[2]; + return self; +} + +function scaleVector(self, scale) { + self[0] *= scale; + self[1] *= scale; + self[2] *= scale; + return self; +} + +function normaliseVector(self) { + var len = Math.sqrt(self[0] * self[0] + self[1] * self[1] + self[2] * self[2]); + self[0] /= len; + self[1] /= len; + self[2] /= len; + return self; +} + +function add(v1, v2) { + return new Array(v1[0] + v2[0], v1[1] + v2[1], v1[2] + v2[2]); +} + +function sub(v1, v2) { + return new Array(v1[0] - v2[0], v1[1] - v2[1], v1[2] - v2[2]); +} + +function scalev(v1, v2) { + return new Array(v1[0] * v2[0], v1[1] * v2[1], v1[2] * v2[2]); +} + +function dot(v1, v2) { + return v1[0] * v2[0] + v1[1] * v2[1] + v1[2] * v2[2]; +} + +function scale(v, scale) { + return [v[0] * scale, v[1] * scale, v[2] * scale]; +} + +function cross(v1, v2) { + return [v1[1] * v2[2] - v1[2] * v2[1], + v1[2] * v2[0] - v1[0] * v2[2], + v1[0] * v2[1] - v1[1] * v2[0]]; + +} + +function normalise(v) { + var len = lengthVector(v); + return [v[0] / len, v[1] / len, v[2] / len]; +} + +function transformMatrix(self, v) { + var vals = self; + var x = vals[0] * v[0] + vals[1] * v[1] + vals[2] * v[2] + vals[3]; + var y = vals[4] * v[0] + vals[5] * v[1] + vals[6] * v[2] + vals[7]; + var z = vals[8] * v[0] + vals[9] * v[1] + vals[10] * v[2] + vals[11]; + return [x, y, z]; +} + +function invertMatrix(self) { + var temp = new Array(16); + var tx = -self[3]; + var ty = -self[7]; + var tz = -self[11]; + for (h = 0; h < 3; h++) + for (v = 0; v < 3; v++) + temp[h + v * 4] = self[v + h * 4]; + for (i = 0; i < 11; i++) + self[i] = temp[i]; + self[3] = tx * self[0] + ty * self[1] + tz * self[2]; + self[7] = tx * self[4] + ty * self[5] + tz * self[6]; + self[11] = tx * self[8] + ty * self[9] + tz * self[10]; + return self; +} + + +// Triangle intersection using barycentric coord method +function Triangle(p1, p2, p3) { + var edge1 = sub(p3, p1); + var edge2 = sub(p2, p1); + var normal = cross(edge1, edge2); + if (Math.abs(normal[0]) > Math.abs(normal[1])) + if (Math.abs(normal[0]) > Math.abs(normal[2])) + this.axis = 0; + else + this.axis = 2; + else + if (Math.abs(normal[1]) > Math.abs(normal[2])) + this.axis = 1; + else + this.axis = 2; + var u = (this.axis + 1) % 3; + var v = (this.axis + 2) % 3; + var u1 = edge1[u]; + var v1 = edge1[v]; + + var u2 = edge2[u]; + var v2 = edge2[v]; + this.normal = normalise(normal); + this.nu = normal[u] / normal[this.axis]; + this.nv = normal[v] / normal[this.axis]; + this.nd = dot(normal, p1) / normal[this.axis]; + var det = u1 * v2 - v1 * u2; + this.eu = p1[u]; + this.ev = p1[v]; + this.nu1 = u1 / det; + this.nv1 = -v1 / det; + this.nu2 = v2 / det; + this.nv2 = -u2 / det; + this.material = [0.7, 0.7, 0.7]; +} + +Triangle.prototype.intersect = function(orig, dir, near, far) { + var u = (this.axis + 1) % 3; + var v = (this.axis + 2) % 3; + var d = dir[this.axis] + this.nu * dir[u] + this.nv * dir[v]; + var t = (this.nd - orig[this.axis] - this.nu * orig[u] - this.nv * orig[v]) / d; + if (t < near || t > far) + return null; + var Pu = orig[u] + t * dir[u] - this.eu; + var Pv = orig[v] + t * dir[v] - this.ev; + var a2 = Pv * this.nu1 + Pu * this.nv1; + if (a2 < 0) + return null; + var a3 = Pu * this.nu2 + Pv * this.nv2; + if (a3 < 0) + return null; + + if ((a2 + a3) > 1) + return null; + return t; +} + +function Scene(a_triangles) { + this.triangles = a_triangles; + this.lights = []; + this.ambient = [0,0,0]; + this.background = [0.8,0.8,1]; +} +var zero = new Array(0,0,0); + +Scene.prototype.intersect = function(origin, dir, near, far) { + var closest = null; + for (i = 0; i < this.triangles.length; i++) { + var triangle = this.triangles[i]; + var d = triangle.intersect(origin, dir, near, far); + if (d == null || d > far || d < near) + continue; + far = d; + closest = triangle; + } + + if (!closest) + return [this.background[0],this.background[1],this.background[2]]; + + var normal = closest.normal; + var hit = add(origin, scale(dir, far)); + if (dot(dir, normal) > 0) + normal = [-normal[0], -normal[1], -normal[2]]; + + var colour = null; + if (closest.shader) { + colour = closest.shader(closest, hit, dir); + } else { + colour = closest.material; + } + + // do reflection + var reflected = null; + if (colour.reflection > 0.001) { + var reflection = addVector(scale(normal, -2*dot(dir, normal)), dir); + reflected = this.intersect(hit, reflection, 0.0001, 1000000); + if (colour.reflection >= 0.999999) + return reflected; + } + + var l = [this.ambient[0], this.ambient[1], this.ambient[2]]; + for (var i = 0; i < this.lights.length; i++) { + var light = this.lights[i]; + var toLight = sub(light, hit); + var distance = lengthVector(toLight); + scaleVector(toLight, 1.0/distance); + distance -= 0.0001; + if (this.blocked(hit, toLight, distance)) + continue; + var nl = dot(normal, toLight); + if (nl > 0) + addVector(l, scale(light.colour, nl)); + } + l = scalev(l, colour); + if (reflected) { + l = addVector(scaleVector(l, 1 - colour.reflection), scaleVector(reflected, colour.reflection)); + } + return l; +} + +Scene.prototype.blocked = function(O, D, far) { + var near = 0.0001; + var closest = null; + for (i = 0; i < this.triangles.length; i++) { + var triangle = this.triangles[i]; + var d = triangle.intersect(O, D, near, far); + if (d == null || d > far || d < near) + continue; + return true; + } + + return false; +} + + +// this camera code is from notes i made ages ago, it is from *somewhere* -- i cannot remember where +// that somewhere is +function Camera(origin, lookat, up) { + var zaxis = normaliseVector(subVector(lookat, origin)); + var xaxis = normaliseVector(cross(up, zaxis)); + var yaxis = normaliseVector(cross(xaxis, subVector([0,0,0], zaxis))); + var m = new Array(16); + m[0] = xaxis[0]; m[1] = xaxis[1]; m[2] = xaxis[2]; + m[4] = yaxis[0]; m[5] = yaxis[1]; m[6] = yaxis[2]; + m[8] = zaxis[0]; m[9] = zaxis[1]; m[10] = zaxis[2]; + invertMatrix(m); + m[3] = 0; m[7] = 0; m[11] = 0; + this.origin = origin; + this.directions = new Array(4); + this.directions[0] = normalise([-0.7, 0.7, 1]); + this.directions[1] = normalise([ 0.7, 0.7, 1]); + this.directions[2] = normalise([ 0.7, -0.7, 1]); + this.directions[3] = normalise([-0.7, -0.7, 1]); + this.directions[0] = transformMatrix(m, this.directions[0]); + this.directions[1] = transformMatrix(m, this.directions[1]); + this.directions[2] = transformMatrix(m, this.directions[2]); + this.directions[3] = transformMatrix(m, this.directions[3]); +} + +Camera.prototype.generateRayPair = function(y) { + rays = new Array(new Object(), new Object()); + rays[0].origin = this.origin; + rays[1].origin = this.origin; + rays[0].dir = addVector(scale(this.directions[0], y), scale(this.directions[3], 1 - y)); + rays[1].dir = addVector(scale(this.directions[1], y), scale(this.directions[2], 1 - y)); + return rays; +} + +function renderRows(camera, scene, pixels, width, height, starty, stopy) { + for (var y = starty; y < stopy; y++) { + var rays = camera.generateRayPair(y / height); + for (var x = 0; x < width; x++) { + var xp = x / width; + var origin = addVector(scale(rays[0].origin, xp), scale(rays[1].origin, 1 - xp)); + var dir = normaliseVector(addVector(scale(rays[0].dir, xp), scale(rays[1].dir, 1 - xp))); + var l = scene.intersect(origin, dir); + pixels[y][x] = l; + } + } +} + +Camera.prototype.render = function(scene, pixels, width, height) { + var cam = this; + var row = 0; + renderRows(cam, scene, pixels, width, height, 0, height); +} + + + +function raytraceScene() +{ + var startDate = new Date().getTime(); + var numTriangles = 2 * 6; + var triangles = new Array();//numTriangles); + var tfl = createVector(-10, 10, -10); + var tfr = createVector( 10, 10, -10); + var tbl = createVector(-10, 10, 10); + var tbr = createVector( 10, 10, 10); + var bfl = createVector(-10, -10, -10); + var bfr = createVector( 10, -10, -10); + var bbl = createVector(-10, -10, 10); + var bbr = createVector( 10, -10, 10); + + // cube!!! + // front + var i = 0; + + triangles[i++] = new Triangle(tfl, tfr, bfr); + triangles[i++] = new Triangle(tfl, bfr, bfl); + // back + triangles[i++] = new Triangle(tbl, tbr, bbr); + triangles[i++] = new Triangle(tbl, bbr, bbl); + // triangles[i-1].material = [0.7,0.2,0.2]; + // triangles[i-1].material.reflection = 0.8; + // left + triangles[i++] = new Triangle(tbl, tfl, bbl); + // triangles[i-1].reflection = 0.6; + triangles[i++] = new Triangle(tfl, bfl, bbl); + // triangles[i-1].reflection = 0.6; + // right + triangles[i++] = new Triangle(tbr, tfr, bbr); + triangles[i++] = new Triangle(tfr, bfr, bbr); + // top + triangles[i++] = new Triangle(tbl, tbr, tfr); + triangles[i++] = new Triangle(tbl, tfr, tfl); + // bottom + triangles[i++] = new Triangle(bbl, bbr, bfr); + triangles[i++] = new Triangle(bbl, bfr, bfl); + + //Floor!!!! + var green = createVector(0.0, 0.4, 0.0); + var grey = createVector(0.4, 0.4, 0.4); + grey.reflection = 1.0; + var floorShader = function(tri, pos, view) { + var x = ((pos[0]/32) % 2 + 2) % 2; + var z = ((pos[2]/32 + 0.3) % 2 + 2) % 2; + if (x < 1 != z < 1) { + //in the real world we use the fresnel term... + // var angle = 1-dot(view, tri.normal); + // angle *= angle; + // angle *= angle; + // angle *= angle; + //grey.reflection = angle; + return grey; + } else + return green; + } + var ffl = createVector(-1000, -30, -1000); + var ffr = createVector( 1000, -30, -1000); + var fbl = createVector(-1000, -30, 1000); + var fbr = createVector( 1000, -30, 1000); + triangles[i++] = new Triangle(fbl, fbr, ffr); + triangles[i-1].shader = floorShader; + triangles[i++] = new Triangle(fbl, ffr, ffl); + triangles[i-1].shader = floorShader; + + var _scene = new Scene(triangles); + _scene.lights[0] = createVector(20, 38, -22); + _scene.lights[0].colour = createVector(0.7, 0.3, 0.3); + _scene.lights[1] = createVector(-23, 40, 17); + _scene.lights[1].colour = createVector(0.7, 0.3, 0.3); + _scene.lights[2] = createVector(23, 20, 17); + _scene.lights[2].colour = createVector(0.7, 0.7, 0.7); + _scene.ambient = createVector(0.1, 0.1, 0.1); + // _scene.background = createVector(0.7, 0.7, 1.0); + + var size = 30; + var pixels = new Array(); + for (var y = 0; y < size; y++) { + pixels[y] = new Array(); + for (var x = 0; x < size; x++) { + pixels[y][x] = 0; + } + } + + var _camera = new Camera(createVector(-40, 40, 40), createVector(0, 0, 0), createVector(0, 1, 0)); + _camera.render(_scene, pixels, size, size); + + return pixels; +} + +function arrayToCanvasCommands(pixels) +{ + var s = '<canvas id="renderCanvas" width="30px" height="30px"></canvas><scr' + 'ipt>\nvar pixels = ['; + var size = 30; + for (var y = 0; y < size; y++) { + s += "["; + for (var x = 0; x < size; x++) { + s += "[" + pixels[y][x] + "],"; + } + s+= "],"; + } + s += '];\n var canvas = document.getElementById("renderCanvas").getContext("2d");\n\ +\n\ +\n\ + var size = 30;\n\ + canvas.fillStyle = "red";\n\ + canvas.fillRect(0, 0, size, size);\n\ + canvas.scale(1, -1);\n\ + canvas.translate(0, -size);\n\ +\n\ + if (!canvas.setFillColor)\n\ + canvas.setFillColor = function(r, g, b, a) {\n\ + this.fillStyle = "rgb("+[Math.floor(r * 255), Math.floor(g * 255), Math.floor(b * 255)]+")";\n\ + }\n\ +\n\ +for (var y = 0; y < size; y++) {\n\ + for (var x = 0; x < size; x++) {\n\ + var l = pixels[y][x];\n\ + canvas.setFillColor(l[0], l[1], l[2], 1);\n\ + canvas.fillRect(x, y, 1, 1);\n\ + }\n\ +}</scr' + 'ipt>'; + + return s; +} + +testOutput = arrayToCanvasCommands(raytraceScene()); + + +var _sunSpiderInterval = new Date() - _sunSpiderStartDate; + +document.getElementById("console").innerHTML = _sunSpiderInterval; +</script> + + +</body> +</html> diff --git a/build/pgo/js-input/3d-thingy.html b/build/pgo/js-input/3d-thingy.html new file mode 100644 index 000000000..9e54299df --- /dev/null +++ b/build/pgo/js-input/3d-thingy.html @@ -0,0 +1,390 @@ +<html> +<head> +<title>3d thingy</title> +<style type="text/css"> +div.z2 { position:absolute; z-index:2; } +div.z1 { position:absolute; z-index:1; } +</style> +<script type="text/javascript"> +/************************************************************************** +JavaScript Graphics Library 0.0.1, Updated Source Code at Scriptersoft.com +Copyright (C) 2005 Kurt L. Whicher +November,13,2005 + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +**************************************************************************/ + +//________________________________________ global variables + +var S_piDoubled=Math.PI*2; +var S_deg2Rad=Math.PI/180; + +//_______________________________________________ functions + +function S_matrix() { + return [1,0,0,0, + 0,1,0,0, + 0,0,1,0, + 0,0,0,1]; +} +function S_vec2D(x,y) { this.x=x; this.y=y; } +function S_vec3D(x,y,z) { this.x=x; this.y=y; this.z=z; } +function S_subVec2D(a,b) { + return new S_vec2D(a.x-b.x, a.y-b.y); +} +function S_subVec3D(a,b) { + return new S_vec3D(a.x-b.x, a.y-b.y, a.z-b.z); +} +function S_dotVec3D(a, b) { return a.x*b.x+a.y*b.y+a.z*b.z; } +function S_cross(a,b) { + return new S_vec3D( a.y*b.z-a.z*b.y, a.z*b.x-a.x*b.z, a.x*b.y-a.y*b.x); +} +function S_lengthSquaredVec3D(v) { return S_dotVec3D(v,v); } +function S_lengthVec3D(v) { return Math.sqrt(S_lengthSquaredVec3D(v)); } +function S_normalizeVec3D(v) { + var l=S_lengthVec3D(v), nv=new S_vec3D(0,0,0); + if(l!=0) { nv.x=v.x/l; nv.y=v.y/l; nv.z=v.z/l; } + return nv; +} +function S_rotate(m,ax,a) { // transformation matrix, axis, angle + var i,j,ij=new Array(),v=new Array(),c=Math.cos(a),s=Math.sin(a); + if (ax=="x") ij=[1,2,5,6,9,10,13,14]; + else if (ax=="y") ij=[2,0,6,4,10,8,14,12]; + else if (ax=="z") ij=[0,1,4,5,8,9,12,13]; + for (i=0;i<8;i++) v[i]=m[ij[i]]; + for (i=0,j=1;i<8;i+=2,j+=2) { + m[ij[i]]=v[i]*c-v[j]*s; + m[ij[j]]=v[i]*s+v[j]*c + } +} +function S_checkBrowser() { + if (document.getElementById) return true; else return false; +} +function S_zIndex(e,z) { document.getElementById(e).style.zIndex=z; } +function S_rgbColor(r,g,b) { + var i, c=[r,g,b]; + for(i=0; i<3; i++) { + c[i]=Math.floor(c[i]); + if(c[i]<0) c[i]=0; else if(c[i]>255) c[i]=255; + } + return c; +} +function S_rgbColorString(c) { + return "rgb("+c[0]+","+c[1]+","+c[2]+")"; +} +function S_vertice(x,y,z) { + this.x=x; this.y=y; this.z=z; this.w=1; + this.t=new S_vec3D(x,y,z); // transformed 3d + this.p=new S_vec2D(0,0); // projected 2d +} +function S_face(v0,v1,v2,c) { // 3 vertice faces + this.v=[v0,v1,v2]; this.c=c; this.b=0; // b:brightness + this.d=true; // display: true or false +} +// x coordinate, number of vertices, distance +function S_verticeRing(x,nv,d) { + var i,a,v=new Array(); + for(i=0;i<nv;i++) { + a=S_piDoubled*i/nv; + v[i]=new S_vertice(x,d*Math.sin(a),d*Math.cos(a)); + } + return v; +} +function S_triangleRing(r1,r2,c,clr) { // rows 1 & 2, cols, color + var i,j,tr=new Array(); + for(i=0,j=1;i<c;i++,j=++j%c) { + tr.push(new S_face(r1+i,r2+i,r2+j,clr)); + tr.push(new S_face(r1+i,r2+j,r1+j,clr)); + } + return tr; +} +function S_model(v,f) { + // vertice & face arrays, transformation matrix, display boolean + this.v=v; this.f=f, this.tm=S_matrix(), this.d=true; +} +S_model.prototype.S_rotateX=function(a) { + S_rotate(this.tm,"x",a*=S_deg2Rad); +} +S_model.prototype.S_rotateY=function(a) { + S_rotate(this.tm,"y",a*=S_deg2Rad); +} +S_model.prototype.S_rotateZ=function(a) { + S_rotate(this.tm,"z",a*=S_deg2Rad); +} +S_model.prototype.S_show=function() { this.d=true; } +S_model.prototype.S_hide=function() { this.d=false; } +function S_cube(d,c) { //distance & color + return new S_cone(d,d,Math.cos(Math.PI/4)*d*2,1,4,c); +} +function S_cylinder(w,h,r,c,clr,e) { + return new S_cone(w,w,h,r,c,clr,e); +} +// width, height, "rows", "columns", color, ends +function S_cone(w1,w2,h,r,c,clr,e) { + var i,r1=0,r2=c,v=new Array(),t=new Array(),rxc=r*c; + for(i=0;i<=r;i++) + v=v.concat(S_verticeRing(h*(0.5-i/r),c,w1*i/r+w2*(r-i)/r)); + for(i=0;i<r;i++,r1+=c,r2+=c) + t=t.concat(S_triangleRing(r1,r2,c,clr)); + if (e!="hideEnds") + for(i=1;i<(c-1);i++) { + t.push(new S_face(0,i,i+1,clr)); + t.push(new S_face(rxc,rxc+i+1,rxc+i,clr)); + } + return new S_model(v,t); +} +function S_sphere(d,r,c,clr) { + // distance, "rows">=2, "columns">=3, color paramaters + var v=new Array(),t=new Array(),r_1xc=(r-1)*c,r_2xc=(r-2)*c; + var i,j,tmp,r1=0,r2=c; + for(i=1;i<r;i++) { + tmp=Math.PI*i/r; + v=v.concat(S_verticeRing(d*Math.cos(tmp),c,Math.sin(tmp)*d)); + } + v.push(new S_vertice( d,0,0)); + v.push(new S_vertice(-d,0,0)); + for(i=0;i<(r-2);i++,r1+=c,r2+=c) + t=t.concat(S_triangleRing(r1,r2,c,clr)); + for(i=0,j=1;i<c;i++,j=++j%c) { + t.push(new S_face(r_1xc,i,j,clr)); + t.push(new S_face(r_1xc+1,r_2xc+j,r_2xc+i,clr)); + } + return new S_model(v,t); +} +S_model.prototype.S_scale=function(x) { + this.tm[0]*=x; this.tm[5]*=x; this.tm[10]*=x; +} +S_model.prototype.S_faceColor=function(i,c) { this.f[i].c=c; } +S_model.prototype.S_scaleX=function(s) { this.tm[0]*=s; } +S_model.prototype.S_scaleY=function(s) { this.tm[5]*=s; } +S_model.prototype.S_scaleZ=function(s) { this.tm[10]*=s; } +function S_scene(dv,l,t,w,h,cmra) { // left, top, width, height + this.dv=dv; + this.ps=1; // pixel size + this.l=l; this.t=t; this.w=w; this.h=h; + this.cx=l+w/2; this.cy=t+h/2; // center x, center y + this.dt="paint"; // output type + this.m=new Array(); // model array + this.lght=new S_light(); + this.lc=S_rgbColor(255,255,255); // light color + this.cmra=-cmra; // camera on z axis + this.bfr=S_buffer(h,w); +} +function S_buffer(h,w) { + var i, j, b=new Array(); + for(i=0;i<h;i++) { + b[i]=new Array(); + for(j=0;j<w;j++) b[i][j]=new S_pixel(); + } + return b; +} +function S_pixel() { // display boolean, color + this.d=true; this.c=0; +} +S_pixel.prototype.S_setColor=function(c) { + this.d=true; this.c=c; +} +S_pixel.prototype.S_hide=function() { this.d=false; } +S_scene.prototype.S_pixelSize=function(ps){ this.ps=ps; } +S_scene.prototype.S_widthAndHeight=function(w,h){ this.w=w; this.h=h; } +S_scene.prototype.S_center=function(cx,cy){ this.cx=cx; this.cy=cy; } +S_scene.prototype.S_paint=function(){ this.dt="paint"; } +S_scene.prototype.S_models=function() { + var i; this.m=new Array(); + for(i=0;i<arguments.length;i++) this.m.push(arguments[i]); +} +S_scene.prototype.S_lightColor=function(c){ this.lc=c; } +S_scene.prototype.S_project=function() { + var i, j, v, tm, d, m; + for(i=0;i<this.m.length;i++) { + m=this.m[i]; tm=this.m[i].tm; + for(j=0;j<m.v.length;j++) { + v=m.v[j]; + v.t.x=v.x*tm[0]+v.y*tm[4]+v.z*tm[8]+v.w*tm[12]; + v.t.y=v.x*tm[1]+v.y*tm[5]+v.z*tm[9]+v.w*tm[13]; + v.t.z=v.x*tm[2]+v.y*tm[6]+v.z*tm[10]+v.w*tm[14]; + d=(this.cmra-v.t.z/2); + if (d<0) { + v.p.x=(this.cmra*v.t.x/d)+this.cx; + v.p.y=-(this.cmra*v.t.y/d)+this.cy; + } + } + } +} +S_scene.prototype.S_display=function(disp){ + var i, j, k, s="", ds, c, cnt=0; // ds:div start + this.tr=new Array(); // triangles ready to draw + this.S_project(); + this.S_adjustLight(); + this.S_clearBuffer(); + for(i=0;i<this.m.length;i++) { + this.m[i].S_setupFaces(this.tr,this.lght.t); + for(j=0;j<this.tr.length;j++) { // loop through triangles + c=S_divColor(this.tr[j].c,this.lc,this.tr[j].b); + S_setupBuffer(this,this.tr[j].p,c); + } + } + for(i=0;i<this.h;i++) { + ds=-1; + for(j=0,k=1;j<this.w;j++,k++) { + if((this.bfr[i][j].d==true)&&(ds==-1)) ds=j; + if( (this.bfr[i][j].d==true)&& + ( (k==this.w)|| + (this.bfr[i][k].d==false)|| + (!S_sameColor(this.bfr[i][j].c, this.bfr[i][k].c)) ) ) { + s+=S_divString(S_rgbColorString(this.bfr[i][j].c),this.t+i*this.ps,this.l+ds*this.ps,this.ps,(k-ds)*this.ps); + ds=-1; + cnt++; + } + } + } + S_writeInnerHTML(this.dv,s); + if(disp=="ShowCount") alert(cnt); +} +S_scene.prototype.S_displayAndShowCount=function(){ + this.S_display("ShowCount"); +} +S_model.prototype.S_setupFaces=function(tr,lght) { + var i, j, fn, v, p=new Array(); // vertice & projection arrays + var z=new Array(); + for(i=0;i<this.f.length;i++) { // loop through faces + v=this.f[i].v; + for(j=0;j<3;j++) { p[j]=this.v[v[j]].p; } + for(j=0;j<3;j++) { z[j]=this.v[v[j]].t.z; } + if (((p[1].x-p[0].x)*(p[2].y-p[0].y))<((p[2].x-p[0].x)*(p[1].y-p[0].y))) { + this.f[i].d=true; + fn=S_faceNormal(this.v[v[0]].t, this.v[v[1]].t, this.v[v[2]].t); + this.f[i].b=S_faceIntensity(fn,lght); + tr.push(new S_triangle(fn,this.f[i].b,p.slice(),this.f[i].c,z)); + } else { this.f[i].d=false; } + } +} +// normal, brightness, array of 2D projection coordinates, and z depth +function S_triangle(fn,b,p,c,z) { + this.fn=fn; this.b=b; this.p=p; this.z=z; this.c=c; +} +function S_faceNormal(a,b,c){ + var cr=S_cross(S_subVec3D(b,a), S_subVec3D(b,c)); + return S_normalizeVec3D(cr); +} +function S_faceIntensity(fn,lght) { + var i=S_dotVec3D(fn,lght); return (i>0)?i:0; +} +function S_divColor(c,lc,b) { // c:array of colors + var i, clr=new Array(); + for(i=0;i<3;i++) clr[i]=Math.floor(c[i]+(lc[i]-c[i]+1)*b); + for(i=0;i<3;i++) if (clr[i]>lc[i]) { clr[i]=lc[i]; } + return S_rgbColor(clr[0],clr[1],clr[2]); +} +function S_sameColor(a,b) { + for(var i=0;i<3;i++) { if(a[i]!=b[i]) return false; } + return true; +} +function S_setupBuffer(scn,p,c) { + // temp, counters, min, max, scanline, vertice & slope arrays + var t,i,j,xmin=new Array(),xmax=new Array(),sl; + var v=new Array(), m=new Array(); + p.sort(function(a,b) { return a.y-b.y; } ); + for(i=0;i<3;i++) p[i].y=Math.floor(p[i].y); + v[0]=S_subVec2D(p[1],p[0]); + v[1]=S_subVec2D(p[2],p[0]); + v[2]=S_subVec2D(p[2],p[1]); + for(i=0;i<3;i++) { m[i]=(v[i].y!=0)?v[i].x/v[i].y:0; } + for(i=0,sl=scn.t;i<scn.h;i++,sl++) { + xmin[i]=1000;xmax[i]=0; + if((sl>=p[0].y)&&(sl<=p[2].y)) { + xmin[i]=xmax[i]=Math.floor(p[0].x+m[1]*(sl-p[0].y)); + } + if((sl>=p[0].y)&&(sl<=p[1].y)) { + t=Math.floor(p[0].x+m[0]*(sl-p[0].y)); + if(t<xmin[i]) xmin[i]=Math.floor(t); + else if(t>xmax[i]) xmax[i]=Math.floor(t); + } + if((sl>=p[1].y)&&(sl<=p[2].y)) { + t=Math.floor(p[1].x+m[2]*(sl-p[1].y)); + if(t<xmin[i]) xmin[i]=Math.floor(t); + else if(t>xmax[i]) xmax[i]=Math.floor(t); + } + for(j=0;j<scn.w;j++) + if((j>=(xmin[i]-scn.l))&&(j<=(xmax[i]-scn.l))) { + scn.bfr[i][j].d=true; scn.bfr[i][j].c=c; + } + } +} +function S_light() { + this.x=0; this.y=1; this.z=0; this.w=1; // original coordinates + this.t=new S_vec3D(0,1,0); // transformed coordinates + this.tm=new S_matrix(); +} +S_scene.prototype.S_adjustLight=function() { + var m=this.lght.tm, l=this.lght; + l.t.x=l.x*m[0]+l.y*m[4]+ l.z*m[8]+l.w*m[12]; + l.t.y=l.x*m[1]+l.y*m[5]+ l.z*m[9]+l.w*m[13]; + l.t.z=l.x*m[2]+l.y*m[6]+ l.z*m[10]+l.w*m[14]; + l.t=S_normalizeVec3D(l.t); +} +S_scene.prototype.S_lightRotateX=function(a) { + S_rotate(this.lght.tm,"x",a*=S_deg2Rad); +} +S_scene.prototype.S_lightRotateY=function(a) { + S_rotate(this.lght.tm,"y",a*=S_deg2Rad); +} +S_scene.prototype.S_lightRotateZ=function(a) { + S_rotate(this.lght.tm,"z",a*=S_deg2Rad); +} +S_scene.prototype.S_clearBuffer=function() { + for(var i=0;i<this.h;i++) + for(var j=0;j<this.w;j++) this.bfr[i][j].d=false; +} +function S_divString(b,t,l,h,w) { + var s='<div style="background-color:'+b+';position:absolute;'; + s+='top:'+t+'px;left:'+l+'px;height:'+h+'px;width:'+w; + return s+'px;font-size:0;visibility:visible"></div>'; +} +function S_writeInnerHTML(id,text) { + document.getElementById(id).innerHTML = text; +} +</script> +</head> +<body> +<div class="z1" id="graphicsDiv">Text to be replaced with graphics.</div> +<script type="text/javascript"> +if(S_checkBrowser()) { +var intrvl; +// Create a new scene with parameters for +// div id, left, top, width, height, and camera distance +var scn=new S_scene("graphicsDiv",75,25,100,100,300); +scn.S_pixelSize(3); // set scene pixel size +var c=S_rgbColor(0,0,127); // color +var c2=S_rgbColor(0,127,127); // color +var m=new S_cube(18,c); // model +m.S_faceColor(4,c2); +m.S_faceColor(5,c2); +m.S_scaleX(2.5); // scale model along x axis +scn.S_models(m); // add model(s) to scene +scn.S_lightRotateX(-25); // adjust light +function r(){ // rotation function +m.S_rotateX(11); // rotate model around y axis +m.S_rotateY(5); // rotate model around y axis +m.S_rotateZ(7); // rotate model around z axis +scn.S_display(); // display scene +} // end rotation function +intrvl=setInterval('r();',75); +} +</script> + +</body> +</html> + diff --git a/build/pgo/js-input/access-binary-trees.html b/build/pgo/js-input/access-binary-trees.html new file mode 100644 index 000000000..c2c6cf3d9 --- /dev/null +++ b/build/pgo/js-input/access-binary-trees.html @@ -0,0 +1,100 @@ +<!DOCTYPE html> +<head> +<!-- + Copyright (C) 2007 Apple Inc. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--> + +<title>SunSpider access-binary-trees</title> + +</head> + +<body> +<h3>access-binary-trees</h3> +<div id="console"> +</div> + +<script> + +var _sunSpiderStartDate = new Date(); + +/* The Great Computer Language Shootout + http://shootout.alioth.debian.org/ + contributed by Isaac Gouy */ + +function TreeNode(left,right,item){ + this.left = left; + this.right = right; + this.item = item; +} + +TreeNode.prototype.itemCheck = function(){ + if (this.left==null) return this.item; + else return this.item + this.left.itemCheck() - this.right.itemCheck(); +} + +function bottomUpTree(item,depth){ + if (depth>0){ + return new TreeNode( + bottomUpTree(2*item-1, depth-1) + ,bottomUpTree(2*item, depth-1) + ,item + ); + } + else { + return new TreeNode(null,null,item); + } +} + +var ret; + +for ( var n = 4; n <= 7; n += 1 ) { + var minDepth = 4; + var maxDepth = Math.max(minDepth + 2, n); + var stretchDepth = maxDepth + 1; + + var check = bottomUpTree(0,stretchDepth).itemCheck(); + + var longLivedTree = bottomUpTree(0,maxDepth); + for (var depth=minDepth; depth<=maxDepth; depth+=2){ + var iterations = 1 << (maxDepth - depth + minDepth); + + check = 0; + for (var i=1; i<=iterations; i++){ + check += bottomUpTree(i,depth).itemCheck(); + check += bottomUpTree(-i,depth).itemCheck(); + } + } + + ret = longLivedTree.itemCheck(); +} + + +var _sunSpiderInterval = new Date() - _sunSpiderStartDate; + +document.getElementById("console").innerHTML = _sunSpiderInterval; +</script> + + +</body> +</html> diff --git a/build/pgo/js-input/access-fannkuch.html b/build/pgo/js-input/access-fannkuch.html new file mode 100644 index 000000000..02b306ff2 --- /dev/null +++ b/build/pgo/js-input/access-fannkuch.html @@ -0,0 +1,116 @@ +<!DOCTYPE html> +<head> +<!-- + Copyright (C) 2007 Apple Inc. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--> + +<title>SunSpider access-fannkuch</title> + +</head> + +<body> +<h3>access-fannkuch</h3> +<div id="console"> +</div> + +<script> + +var _sunSpiderStartDate = new Date(); + +/* The Great Computer Language Shootout + http://shootout.alioth.debian.org/ + contributed by Isaac Gouy */ + +function fannkuch(n) { + var check = 0; + var perm = Array(n); + var perm1 = Array(n); + var count = Array(n); + var maxPerm = Array(n); + var maxFlipsCount = 0; + var m = n - 1; + + for (var i = 0; i < n; i++) perm1[i] = i; + var r = n; + + while (true) { + // write-out the first 30 permutations + if (check < 30){ + var s = ""; + for(var i=0; i<n; i++) s += (perm1[i]+1).toString(); + check++; + } + + while (r != 1) { count[r - 1] = r; r--; } + if (!(perm1[0] == 0 || perm1[m] == m)) { + for (var i = 0; i < n; i++) perm[i] = perm1[i]; + + var flipsCount = 0; + var k; + + while (!((k = perm[0]) == 0)) { + var k2 = (k + 1) >> 1; + for (var i = 0; i < k2; i++) { + var temp = perm[i]; perm[i] = perm[k - i]; perm[k - i] = temp; + } + flipsCount++; + } + + if (flipsCount > maxFlipsCount) { + maxFlipsCount = flipsCount; + for (var i = 0; i < n; i++) maxPerm[i] = perm1[i]; + } + } + + while (true) { + if (r == n) return maxFlipsCount; + var perm0 = perm1[0]; + var i = 0; + while (i < r) { + var j = i + 1; + perm1[i] = perm1[j]; + i = j; + } + perm1[r] = perm0; + + count[r] = count[r] - 1; + if (count[r] > 0) break; + r++; + } + } +} + +var n = 8; +var ret = fannkuch(n); + + + +var _sunSpiderInterval = new Date() - _sunSpiderStartDate; + +document.getElementById("console").innerHTML = _sunSpiderInterval; +</script> + + +</body> +</html> diff --git a/build/pgo/js-input/access-nbody.html b/build/pgo/js-input/access-nbody.html new file mode 100644 index 000000000..4ef73c855 --- /dev/null +++ b/build/pgo/js-input/access-nbody.html @@ -0,0 +1,219 @@ +<!DOCTYPE html> +<head> +<!-- + Copyright (C) 2007 Apple Inc. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--> + +<title>SunSpider access-nbody</title> + +</head> + +<body> +<h3>access-nbody</h3> +<div id="console"> +</div> + +<script> + +var _sunSpiderStartDate = new Date(); + +/* The Great Computer Language Shootout + http://shootout.alioth.debian.org/ + contributed by Isaac Gouy */ + +var PI = 3.141592653589793; +var SOLAR_MASS = 4 * PI * PI; +var DAYS_PER_YEAR = 365.24; + +function Body(x,y,z,vx,vy,vz,mass){ + this.x = x; + this.y = y; + this.z = z; + this.vx = vx; + this.vy = vy; + this.vz = vz; + this.mass = mass; +} + +Body.prototype.offsetMomentum = function(px,py,pz) { + this.vx = -px / SOLAR_MASS; + this.vy = -py / SOLAR_MASS; + this.vz = -pz / SOLAR_MASS; + return this; +} + +function Jupiter(){ + return new Body( + 4.84143144246472090e+00, + -1.16032004402742839e+00, + -1.03622044471123109e-01, + 1.66007664274403694e-03 * DAYS_PER_YEAR, + 7.69901118419740425e-03 * DAYS_PER_YEAR, + -6.90460016972063023e-05 * DAYS_PER_YEAR, + 9.54791938424326609e-04 * SOLAR_MASS + ); +} + +function Saturn(){ + return new Body( + 8.34336671824457987e+00, + 4.12479856412430479e+00, + -4.03523417114321381e-01, + -2.76742510726862411e-03 * DAYS_PER_YEAR, + 4.99852801234917238e-03 * DAYS_PER_YEAR, + 2.30417297573763929e-05 * DAYS_PER_YEAR, + 2.85885980666130812e-04 * SOLAR_MASS + ); +} + +function Uranus(){ + return new Body( + 1.28943695621391310e+01, + -1.51111514016986312e+01, + -2.23307578892655734e-01, + 2.96460137564761618e-03 * DAYS_PER_YEAR, + 2.37847173959480950e-03 * DAYS_PER_YEAR, + -2.96589568540237556e-05 * DAYS_PER_YEAR, + 4.36624404335156298e-05 * SOLAR_MASS + ); +} + +function Neptune(){ + return new Body( + 1.53796971148509165e+01, + -2.59193146099879641e+01, + 1.79258772950371181e-01, + 2.68067772490389322e-03 * DAYS_PER_YEAR, + 1.62824170038242295e-03 * DAYS_PER_YEAR, + -9.51592254519715870e-05 * DAYS_PER_YEAR, + 5.15138902046611451e-05 * SOLAR_MASS + ); +} + +function Sun(){ + return new Body(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, SOLAR_MASS); +} + + +function NBodySystem(bodies){ + this.bodies = bodies; + var px = 0.0; + var py = 0.0; + var pz = 0.0; + var size = this.bodies.length; + for (var i=0; i<size; i++){ + var b = this.bodies[i]; + var m = b.mass; + px += b.vx * m; + py += b.vy * m; + pz += b.vz * m; + } + this.bodies[0].offsetMomentum(px,py,pz); +} + +NBodySystem.prototype.advance = function(dt){ + var dx, dy, dz, distance, mag; + var size = this.bodies.length; + + for (var i=0; i<size; i++) { + var bodyi = this.bodies[i]; + for (var j=i+1; j<size; j++) { + var bodyj = this.bodies[j]; + dx = bodyi.x - bodyj.x; + dy = bodyi.y - bodyj.y; + dz = bodyi.z - bodyj.z; + + distance = Math.sqrt(dx*dx + dy*dy + dz*dz); + mag = dt / (distance * distance * distance); + + bodyi.vx -= dx * bodyj.mass * mag; + bodyi.vy -= dy * bodyj.mass * mag; + bodyi.vz -= dz * bodyj.mass * mag; + + bodyj.vx += dx * bodyi.mass * mag; + bodyj.vy += dy * bodyi.mass * mag; + bodyj.vz += dz * bodyi.mass * mag; + } + } + + for (var i=0; i<size; i++) { + var body = this.bodies[i]; + body.x += dt * body.vx; + body.y += dt * body.vy; + body.z += dt * body.vz; + } +} + +NBodySystem.prototype.energy = function(){ + var dx, dy, dz, distance; + var e = 0.0; + var size = this.bodies.length; + + for (var i=0; i<size; i++) { + var bodyi = this.bodies[i]; + + e += 0.5 * bodyi.mass * + ( bodyi.vx * bodyi.vx + + bodyi.vy * bodyi.vy + + bodyi.vz * bodyi.vz ); + + for (var j=i+1; j<size; j++) { + var bodyj = this.bodies[j]; + dx = bodyi.x - bodyj.x; + dy = bodyi.y - bodyj.y; + dz = bodyi.z - bodyj.z; + + distance = Math.sqrt(dx*dx + dy*dy + dz*dz); + e -= (bodyi.mass * bodyj.mass) / distance; + } + } + return e; +} + +var ret; + +for ( var n = 3; n <= 24; n *= 2 ) { + (function(){ + var bodies = new NBodySystem( Array( + Sun(),Jupiter(),Saturn(),Uranus(),Neptune() + )); + var max = n * 100; + + ret = bodies.energy(); + for (var i=0; i<max; i++){ + bodies.advance(0.01); + } + ret = bodies.energy(); + })(); +} + + +var _sunSpiderInterval = new Date() - _sunSpiderStartDate; + +document.getElementById("console").innerHTML = _sunSpiderInterval; +</script> + + +</body> +</html> diff --git a/build/pgo/js-input/access-nsieve.html b/build/pgo/js-input/access-nsieve.html new file mode 100644 index 000000000..c3ed067f1 --- /dev/null +++ b/build/pgo/js-input/access-nsieve.html @@ -0,0 +1,88 @@ +<!DOCTYPE html> +<head> +<!-- + Copyright (C) 2007 Apple Inc. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--> + +<title>SunSpider access-nsieve</title> + +</head> + +<body> +<h3>access-nsieve</h3> +<div id="console"> +</div> + +<script> + +var _sunSpiderStartDate = new Date(); + +// The Great Computer Language Shootout +// http://shootout.alioth.debian.org/ +// +// modified by Isaac Gouy + +function pad(number,width){ + var s = number.toString(); + var prefixWidth = width - s.length; + if (prefixWidth>0){ + for (var i=1; i<=prefixWidth; i++) s = " " + s; + } + return s; +} + +function nsieve(m, isPrime){ + var i, k, count; + + for (i=2; i<=m; i++) { isPrime[i] = true; } + count = 0; + + for (i=2; i<=m; i++){ + if (isPrime[i]) { + for (k=i+i; k<=m; k+=i) isPrime[k] = false; + count++; + } + } + return count; +} + +function sieve() { + for (var i = 1; i <= 3; i++ ) { + var m = (1<<i)*10000; + var flags = Array(m+1); + nsieve(m, flags); + } +} + +sieve(); + + +var _sunSpiderInterval = new Date() - _sunSpiderStartDate; + +document.getElementById("console").innerHTML = _sunSpiderInterval; +</script> + + +</body> +</html> diff --git a/build/pgo/js-input/bitops-3bit-bits-in-byte.html b/build/pgo/js-input/bitops-3bit-bits-in-byte.html new file mode 100644 index 000000000..c40be94ef --- /dev/null +++ b/build/pgo/js-input/bitops-3bit-bits-in-byte.html @@ -0,0 +1,82 @@ +<!DOCTYPE html> +<head> +<!-- + Copyright (C) 2007 Apple Inc. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--> + +<title>SunSpider bitops-3bit-bits-in-byte</title> + +</head> + +<body> +<h3>bitops-3bit-bits-in-byte</h3> +<div id="console"> +</div> + +<script> + +var _sunSpiderStartDate = new Date(); + +// Copyright (c) 2004 by Arthur Langereis (arthur_ext at domain xfinitegames, tld com + +// 1 op = 6 ANDs, 3 SHRs, 3 SHLs, 4 assigns, 2 ADDs +// O(1) +function fast3bitlookup(b) { +var c, bi3b = 0xE994; // 0b1110 1001 1001 0100; // 3 2 2 1 2 1 1 0 +c = 3 & (bi3b >> ((b << 1) & 14)); +c += 3 & (bi3b >> ((b >> 2) & 14)); +c += 3 & (bi3b >> ((b >> 5) & 6)); +return c; + +/* +lir4,0xE994; 9 instructions, no memory access, minimal register dependence, 6 shifts, 2 adds, 1 inline assign +rlwinmr5,r3,1,28,30 +rlwinmr6,r3,30,28,30 +rlwinmr7,r3,27,29,30 +rlwnmr8,r4,r5,30,31 +rlwnmr9,r4,r6,30,31 +rlwnmr10,r4,r7,30,31 +addr3,r8,r9 +addr3,r3,r10 +*/ +} + + +function TimeFunc(func) { +var x, y, t; +for(var x=0; x<500; x++) +for(var y=0; y<256; y++) func(y); +} + +TimeFunc(fast3bitlookup); + + +var _sunSpiderInterval = new Date() - _sunSpiderStartDate; + +document.getElementById("console").innerHTML = _sunSpiderInterval; +</script> + + +</body> +</html> diff --git a/build/pgo/js-input/bitops-bits-in-byte.html b/build/pgo/js-input/bitops-bits-in-byte.html new file mode 100644 index 000000000..4022c777f --- /dev/null +++ b/build/pgo/js-input/bitops-bits-in-byte.html @@ -0,0 +1,72 @@ +<!DOCTYPE html> +<head> +<!-- + Copyright (C) 2007 Apple Inc. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--> + +<title>SunSpider bitops-bits-in-byte</title> + +</head> + +<body> +<h3>bitops-bits-in-byte</h3> +<div id="console"> +</div> + +<script> + +var _sunSpiderStartDate = new Date(); + +// Copyright (c) 2004 by Arthur Langereis (arthur_ext at domain xfinitegames, tld com) + + +// 1 op = 2 assigns, 16 compare/branches, 8 ANDs, (0-8) ADDs, 8 SHLs +// O(n) +function bitsinbyte(b) { +var m = 1, c = 0; +while(m<0x100) { +if(b & m) c++; +m <<= 1; +} +return c; +} + +function TimeFunc(func) { +var x, y, t; +for(var x=0; x<350; x++) +for(var y=0; y<256; y++) func(y); +} + +TimeFunc(bitsinbyte); + + +var _sunSpiderInterval = new Date() - _sunSpiderStartDate; + + +document.getElementById("console").innerHTML = _sunSpiderInterval; +</script> + + +</body> +</html> diff --git a/build/pgo/js-input/bitops-bitwise-and.html b/build/pgo/js-input/bitops-bitwise-and.html new file mode 100644 index 000000000..cca513040 --- /dev/null +++ b/build/pgo/js-input/bitops-bitwise-and.html @@ -0,0 +1,78 @@ +<!DOCTYPE html> +<head> +<!-- + Copyright (C) 2007 Apple Inc. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--> + +<title>SunSpider bitops-bitwise-and</title> + +</head> + +<body> +<h3>bitops-bitwise-and</h3> +<div id="console"> +</div> + +<script> + +var _sunSpiderStartDate = new Date(); + +/* + * Copyright (C) 2007 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +bitwiseAndValue = 4294967296; +for (var i = 0; i < 600000; i++) + bitwiseAndValue = bitwiseAndValue & i; + + +var _sunSpiderInterval = new Date() - _sunSpiderStartDate; + +document.getElementById("console").innerHTML = _sunSpiderInterval; +</script> + + +</body> +</html> diff --git a/build/pgo/js-input/bitops-nsieve-bits.html b/build/pgo/js-input/bitops-nsieve-bits.html new file mode 100644 index 000000000..1849f9da2 --- /dev/null +++ b/build/pgo/js-input/bitops-nsieve-bits.html @@ -0,0 +1,82 @@ +<!DOCTYPE html> +<head> +<!-- + Copyright (C) 2007 Apple Inc. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--> + +<title>SunSpider bitops-nsieve-bits</title> + +</head> + +<body> +<h3>bitops-nsieve-bits</h3> +<div id="console"> +</div> + +<script> + +var _sunSpiderStartDate = new Date(); + +// The Great Computer Language Shootout +// http://shootout.alioth.debian.org +// +// Contributed by Ian Osgood + +function pad(n,width) { + var s = n.toString(); + while (s.length < width) s = ' ' + s; + return s; +} + +function primes(isPrime, n) { + var i, count = 0, m = 10000<<n, size = m+31>>5; + + for (i=0; i<size; i++) isPrime[i] = 0xffffffff; + + for (i=2; i<m; i++) + if (isPrime[i>>5] & 1<<(i&31)) { + for (var j=i+i; j<m; j+=i) + isPrime[j>>5] &= ~(1<<(j&31)); + count++; + } +} + +function sieve() { + for (var i = 4; i <= 4; i++) { + var isPrime = new Array((10000<<i)+31>>5); + primes(isPrime, i); + } +} + +sieve(); + + +var _sunSpiderInterval = new Date() - _sunSpiderStartDate; + +document.getElementById("console").innerHTML = _sunSpiderInterval; +</script> + + +</body> +</html> diff --git a/build/pgo/js-input/controlflow-recursive.html b/build/pgo/js-input/controlflow-recursive.html new file mode 100644 index 000000000..9a9651d4b --- /dev/null +++ b/build/pgo/js-input/controlflow-recursive.html @@ -0,0 +1,75 @@ +<!DOCTYPE html> +<head> +<!-- + Copyright (C) 2007 Apple Inc. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--> + +<title>SunSpider controlflow-recursive</title> + +</head> + +<body> +<h3>controlflow-recursive</h3> +<div id="console"> +</div> + +<script> + +var _sunSpiderStartDate = new Date(); + +// The Computer Language Shootout +// http://shootout.alioth.debian.org/ +// contributed by Isaac Gouy + +function ack(m,n){ + if (m==0) { return n+1; } + if (n==0) { return ack(m-1,1); } + return ack(m-1, ack(m,n-1) ); +} + +function fib(n) { + if (n < 2){ return 1; } + return fib(n-2) + fib(n-1); +} + +function tak(x,y,z) { + if (y >= x) return z; + return tak(tak(x-1,y,z), tak(y-1,z,x), tak(z-1,x,y)); +} + +for ( var i = 3; i <= 5; i++ ) { + ack(3,i); + fib(17.0+i); + tak(3*i+3,2*i+2,i+1); +} + + +var _sunSpiderInterval = new Date() - _sunSpiderStartDate; + +document.getElementById("console").innerHTML = _sunSpiderInterval; +</script> + + +</body> +</html> diff --git a/build/pgo/js-input/crypto-aes.html b/build/pgo/js-input/crypto-aes.html new file mode 100644 index 000000000..12f26b2fb --- /dev/null +++ b/build/pgo/js-input/crypto-aes.html @@ -0,0 +1,472 @@ +<!DOCTYPE html> +<head> +<!-- + Copyright (C) 2007 Apple Inc. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--> + +<title>SunSpider crypto-aes</title> + +</head> + +<body> +<h3>crypto-aes</h3> +<div id="console"> +</div> + +<script> + +var _sunSpiderStartDate = new Date(); + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +/* + * AES Cipher function: encrypt 'input' with Rijndael algorithm + * + * takes byte-array 'input' (16 bytes) + * 2D byte-array key schedule 'w' (Nr+1 x Nb bytes) + * + * applies Nr rounds (10/12/14) using key schedule w for 'add round key' stage + * + * returns byte-array encrypted value (16 bytes) + */ +function Cipher(input, w) { // main Cipher function [§5.1] + var Nb = 4; // block size (in words): no of columns in state (fixed at 4 for AES) + var Nr = w.length/Nb - 1; // no of rounds: 10/12/14 for 128/192/256-bit keys + + var state = [[],[],[],[]]; // initialise 4xNb byte-array 'state' with input [§3.4] + for (var i=0; i<4*Nb; i++) state[i%4][Math.floor(i/4)] = input[i]; + + state = AddRoundKey(state, w, 0, Nb); + + for (var round=1; round<Nr; round++) { + state = SubBytes(state, Nb); + state = ShiftRows(state, Nb); + state = MixColumns(state, Nb); + state = AddRoundKey(state, w, round, Nb); + } + + state = SubBytes(state, Nb); + state = ShiftRows(state, Nb); + state = AddRoundKey(state, w, Nr, Nb); + + var output = new Array(4*Nb); // convert state to 1-d array before returning [§3.4] + for (var i=0; i<4*Nb; i++) output[i] = state[i%4][Math.floor(i/4)]; + return output; +} + + +function SubBytes(s, Nb) { // apply SBox to state S [§5.1.1] + for (var r=0; r<4; r++) { + for (var c=0; c<Nb; c++) s[r][c] = Sbox[s[r][c]]; + } + return s; +} + + +function ShiftRows(s, Nb) { // shift row r of state S left by r bytes [§5.1.2] + var t = new Array(4); + for (var r=1; r<4; r++) { + for (var c=0; c<4; c++) t[c] = s[r][(c+r)%Nb]; // shift into temp copy + for (var c=0; c<4; c++) s[r][c] = t[c]; // and copy back + } // note that this will work for Nb=4,5,6, but not 7,8 (always 4 for AES): + return s; // see fp.gladman.plus.com/cryptography_technology/rijndael/aes.spec.311.pdf +} + + +function MixColumns(s, Nb) { // combine bytes of each col of state S [§5.1.3] + for (var c=0; c<4; c++) { + var a = new Array(4); // 'a' is a copy of the current column from 's' + var b = new Array(4); // 'b' is a•{02} in GF(2^8) + for (var i=0; i<4; i++) { + a[i] = s[i][c]; + b[i] = s[i][c]&0x80 ? s[i][c]<<1 ^ 0x011b : s[i][c]<<1; + } + // a[n] ^ b[n] is a•{03} in GF(2^8) + s[0][c] = b[0] ^ a[1] ^ b[1] ^ a[2] ^ a[3]; // 2*a0 + 3*a1 + a2 + a3 + s[1][c] = a[0] ^ b[1] ^ a[2] ^ b[2] ^ a[3]; // a0 * 2*a1 + 3*a2 + a3 + s[2][c] = a[0] ^ a[1] ^ b[2] ^ a[3] ^ b[3]; // a0 + a1 + 2*a2 + 3*a3 + s[3][c] = a[0] ^ b[0] ^ a[1] ^ a[2] ^ b[3]; // 3*a0 + a1 + a2 + 2*a3 + } + return s; +} + + +function AddRoundKey(state, w, rnd, Nb) { // xor Round Key into state S [§5.1.4] + for (var r=0; r<4; r++) { + for (var c=0; c<Nb; c++) state[r][c] ^= w[rnd*4+c][r]; + } + return state; +} + + +function KeyExpansion(key) { // generate Key Schedule (byte-array Nr+1 x Nb) from Key [§5.2] + var Nb = 4; // block size (in words): no of columns in state (fixed at 4 for AES) + var Nk = key.length/4 // key length (in words): 4/6/8 for 128/192/256-bit keys + var Nr = Nk + 6; // no of rounds: 10/12/14 for 128/192/256-bit keys + + var w = new Array(Nb*(Nr+1)); + var temp = new Array(4); + + for (var i=0; i<Nk; i++) { + var r = [key[4*i], key[4*i+1], key[4*i+2], key[4*i+3]]; + w[i] = r; + } + + for (var i=Nk; i<(Nb*(Nr+1)); i++) { + w[i] = new Array(4); + for (var t=0; t<4; t++) temp[t] = w[i-1][t]; + if (i % Nk == 0) { + temp = SubWord(RotWord(temp)); + for (var t=0; t<4; t++) temp[t] ^= Rcon[i/Nk][t]; + } else if (Nk > 6 && i%Nk == 4) { + temp = SubWord(temp); + } + for (var t=0; t<4; t++) w[i][t] = w[i-Nk][t] ^ temp[t]; + } + + return w; +} + +function SubWord(w) { // apply SBox to 4-byte word w + for (var i=0; i<4; i++) w[i] = Sbox[w[i]]; + return w; +} + +function RotWord(w) { // rotate 4-byte word w left by one byte + w[4] = w[0]; + for (var i=0; i<4; i++) w[i] = w[i+1]; + return w; +} + + +// Sbox is pre-computed multiplicative inverse in GF(2^8) used in SubBytes and KeyExpansion [§5.1.1] +var Sbox = [0x63,0x7c,0x77,0x7b,0xf2,0x6b,0x6f,0xc5,0x30,0x01,0x67,0x2b,0xfe,0xd7,0xab,0x76, + 0xca,0x82,0xc9,0x7d,0xfa,0x59,0x47,0xf0,0xad,0xd4,0xa2,0xaf,0x9c,0xa4,0x72,0xc0, + 0xb7,0xfd,0x93,0x26,0x36,0x3f,0xf7,0xcc,0x34,0xa5,0xe5,0xf1,0x71,0xd8,0x31,0x15, + 0x04,0xc7,0x23,0xc3,0x18,0x96,0x05,0x9a,0x07,0x12,0x80,0xe2,0xeb,0x27,0xb2,0x75, + 0x09,0x83,0x2c,0x1a,0x1b,0x6e,0x5a,0xa0,0x52,0x3b,0xd6,0xb3,0x29,0xe3,0x2f,0x84, + 0x53,0xd1,0x00,0xed,0x20,0xfc,0xb1,0x5b,0x6a,0xcb,0xbe,0x39,0x4a,0x4c,0x58,0xcf, + 0xd0,0xef,0xaa,0xfb,0x43,0x4d,0x33,0x85,0x45,0xf9,0x02,0x7f,0x50,0x3c,0x9f,0xa8, + 0x51,0xa3,0x40,0x8f,0x92,0x9d,0x38,0xf5,0xbc,0xb6,0xda,0x21,0x10,0xff,0xf3,0xd2, + 0xcd,0x0c,0x13,0xec,0x5f,0x97,0x44,0x17,0xc4,0xa7,0x7e,0x3d,0x64,0x5d,0x19,0x73, + 0x60,0x81,0x4f,0xdc,0x22,0x2a,0x90,0x88,0x46,0xee,0xb8,0x14,0xde,0x5e,0x0b,0xdb, + 0xe0,0x32,0x3a,0x0a,0x49,0x06,0x24,0x5c,0xc2,0xd3,0xac,0x62,0x91,0x95,0xe4,0x79, + 0xe7,0xc8,0x37,0x6d,0x8d,0xd5,0x4e,0xa9,0x6c,0x56,0xf4,0xea,0x65,0x7a,0xae,0x08, + 0xba,0x78,0x25,0x2e,0x1c,0xa6,0xb4,0xc6,0xe8,0xdd,0x74,0x1f,0x4b,0xbd,0x8b,0x8a, + 0x70,0x3e,0xb5,0x66,0x48,0x03,0xf6,0x0e,0x61,0x35,0x57,0xb9,0x86,0xc1,0x1d,0x9e, + 0xe1,0xf8,0x98,0x11,0x69,0xd9,0x8e,0x94,0x9b,0x1e,0x87,0xe9,0xce,0x55,0x28,0xdf, + 0x8c,0xa1,0x89,0x0d,0xbf,0xe6,0x42,0x68,0x41,0x99,0x2d,0x0f,0xb0,0x54,0xbb,0x16]; + +// Rcon is Round Constant used for the Key Expansion [1st col is 2^(r-1) in GF(2^8)] [§5.2] +var Rcon = [ [0x00, 0x00, 0x00, 0x00], + [0x01, 0x00, 0x00, 0x00], + [0x02, 0x00, 0x00, 0x00], + [0x04, 0x00, 0x00, 0x00], + [0x08, 0x00, 0x00, 0x00], + [0x10, 0x00, 0x00, 0x00], + [0x20, 0x00, 0x00, 0x00], + [0x40, 0x00, 0x00, 0x00], + [0x80, 0x00, 0x00, 0x00], + [0x1b, 0x00, 0x00, 0x00], + [0x36, 0x00, 0x00, 0x00] ]; + + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +/* + * Use AES to encrypt 'plaintext' with 'password' using 'nBits' key, in 'Counter' mode of operation + * - see http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf + * for each block + * - outputblock = cipher(counter, key) + * - cipherblock = plaintext xor outputblock + */ +function AESEncryptCtr(plaintext, password, nBits) { + if (!(nBits==128 || nBits==192 || nBits==256)) return ''; // standard allows 128/192/256 bit keys + + // for this example script, generate the key by applying Cipher to 1st 16/24/32 chars of password; + // for real-world applications, a more secure approach would be to hash the password e.g. with SHA-1 + var nBytes = nBits/8; // no bytes in key + var pwBytes = new Array(nBytes); + for (var i=0; i<nBytes; i++) pwBytes[i] = password.charCodeAt(i) & 0xff; + var key = Cipher(pwBytes, KeyExpansion(pwBytes)); + key = key.concat(key.slice(0, nBytes-16)); // key is now 16/24/32 bytes long + + // initialise counter block (NIST SP800-38A §B.2): millisecond time-stamp for nonce in 1st 8 bytes, + // block counter in 2nd 8 bytes + var blockSize = 16; // block size fixed at 16 bytes / 128 bits (Nb=4) for AES + var counterBlock = new Array(blockSize); // block size fixed at 16 bytes / 128 bits (Nb=4) for AES + var nonce = (new Date()).getTime(); // milliseconds since 1-Jan-1970 + + // encode nonce in two stages to cater for JavaScript 32-bit limit on bitwise ops + for (var i=0; i<4; i++) counterBlock[i] = (nonce >>> i*8) & 0xff; + for (var i=0; i<4; i++) counterBlock[i+4] = (nonce/0x100000000 >>> i*8) & 0xff; + + // generate key schedule - an expansion of the key into distinct Key Rounds for each round + var keySchedule = KeyExpansion(key); + + var blockCount = Math.ceil(plaintext.length/blockSize); + var ciphertext = new Array(blockCount); // ciphertext as array of strings + + for (var b=0; b<blockCount; b++) { + // set counter (block #) in last 8 bytes of counter block (leaving nonce in 1st 8 bytes) + // again done in two stages for 32-bit ops + for (var c=0; c<4; c++) counterBlock[15-c] = (b >>> c*8) & 0xff; + for (var c=0; c<4; c++) counterBlock[15-c-4] = (b/0x100000000 >>> c*8) + + var cipherCntr = Cipher(counterBlock, keySchedule); // -- encrypt counter block -- + + // calculate length of final block: + var blockLength = b<blockCount-1 ? blockSize : (plaintext.length-1)%blockSize+1; + + var ct = ''; + for (var i=0; i<blockLength; i++) { // -- xor plaintext with ciphered counter byte-by-byte -- + var plaintextByte = plaintext.charCodeAt(b*blockSize+i); + var cipherByte = plaintextByte ^ cipherCntr[i]; + ct += String.fromCharCode(cipherByte); + } + // ct is now ciphertext for this block + + ciphertext[b] = escCtrlChars(ct); // escape troublesome characters in ciphertext + } + + // convert the nonce to a string to go on the front of the ciphertext + var ctrTxt = ''; + for (var i=0; i<8; i++) ctrTxt += String.fromCharCode(counterBlock[i]); + ctrTxt = escCtrlChars(ctrTxt); + + // use '-' to separate blocks, use Array.join to concatenate arrays of strings for efficiency + return ctrTxt + '-' + ciphertext.join('-'); +} + + +/* + * Use AES to decrypt 'ciphertext' with 'password' using 'nBits' key, in Counter mode of operation + * + * for each block + * - outputblock = cipher(counter, key) + * - cipherblock = plaintext xor outputblock + */ +function AESDecryptCtr(ciphertext, password, nBits) { + if (!(nBits==128 || nBits==192 || nBits==256)) return ''; // standard allows 128/192/256 bit keys + + var nBytes = nBits/8; // no bytes in key + var pwBytes = new Array(nBytes); + for (var i=0; i<nBytes; i++) pwBytes[i] = password.charCodeAt(i) & 0xff; + var pwKeySchedule = KeyExpansion(pwBytes); + var key = Cipher(pwBytes, pwKeySchedule); + key = key.concat(key.slice(0, nBytes-16)); // key is now 16/24/32 bytes long + + var keySchedule = KeyExpansion(key); + + ciphertext = ciphertext.split('-'); // split ciphertext into array of block-length strings + + // recover nonce from 1st element of ciphertext + var blockSize = 16; // block size fixed at 16 bytes / 128 bits (Nb=4) for AES + var counterBlock = new Array(blockSize); + var ctrTxt = unescCtrlChars(ciphertext[0]); + for (var i=0; i<8; i++) counterBlock[i] = ctrTxt.charCodeAt(i); + + var plaintext = new Array(ciphertext.length-1); + + for (var b=1; b<ciphertext.length; b++) { + // set counter (block #) in last 8 bytes of counter block (leaving nonce in 1st 8 bytes) + for (var c=0; c<4; c++) counterBlock[15-c] = ((b-1) >>> c*8) & 0xff; + for (var c=0; c<4; c++) counterBlock[15-c-4] = ((b/0x100000000-1) >>> c*8) & 0xff; + + var cipherCntr = Cipher(counterBlock, keySchedule); // encrypt counter block + + ciphertext[b] = unescCtrlChars(ciphertext[b]); + + var pt = ''; + for (var i=0; i<ciphertext[b].length; i++) { + // -- xor plaintext with ciphered counter byte-by-byte -- + var ciphertextByte = ciphertext[b].charCodeAt(i); + var plaintextByte = ciphertextByte ^ cipherCntr[i]; + pt += String.fromCharCode(plaintextByte); + } + // pt is now plaintext for this block + + plaintext[b-1] = pt; // b-1 'cos no initial nonce block in plaintext + } + + return plaintext.join(''); +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +function escCtrlChars(str) { // escape control chars which might cause problems handling ciphertext + return str.replace(/[\0\t\n\v\f\r\xa0'"!-]/g, function(c) { return '!' + c.charCodeAt(0) + '!'; }); +} // \xa0 to cater for bug in Firefox; include '-' to leave it free for use as a block marker + +function unescCtrlChars(str) { // unescape potentially problematic control characters + return str.replace(/!\d\d?\d?!/g, function(c) { return String.fromCharCode(c.slice(1,-1)); }); +} +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +/* + * if escCtrlChars()/unescCtrlChars() still gives problems, use encodeBase64()/decodeBase64() instead + */ +var b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; + +function encodeBase64(str) { // http://tools.ietf.org/html/rfc4648 + var o1, o2, o3, h1, h2, h3, h4, bits, i=0, enc=''; + + str = encodeUTF8(str); // encode multi-byte chars into UTF-8 for byte-array + + do { // pack three octets into four hexets + o1 = str.charCodeAt(i++); + o2 = str.charCodeAt(i++); + o3 = str.charCodeAt(i++); + + bits = o1<<16 | o2<<8 | o3; + + h1 = bits>>18 & 0x3f; + h2 = bits>>12 & 0x3f; + h3 = bits>>6 & 0x3f; + h4 = bits & 0x3f; + + // end of string? index to '=' in b64 + if (isNaN(o3)) h4 = 64; + if (isNaN(o2)) h3 = 64; + + // use hexets to index into b64, and append result to encoded string + enc += b64.charAt(h1) + b64.charAt(h2) + b64.charAt(h3) + b64.charAt(h4); + } while (i < str.length); + + return enc; +} + +function decodeBase64(str) { + var o1, o2, o3, h1, h2, h3, h4, bits, i=0, enc=''; + + do { // unpack four hexets into three octets using index points in b64 + h1 = b64.indexOf(str.charAt(i++)); + h2 = b64.indexOf(str.charAt(i++)); + h3 = b64.indexOf(str.charAt(i++)); + h4 = b64.indexOf(str.charAt(i++)); + + bits = h1<<18 | h2<<12 | h3<<6 | h4; + + o1 = bits>>16 & 0xff; + o2 = bits>>8 & 0xff; + o3 = bits & 0xff; + + if (h3 == 64) enc += String.fromCharCode(o1); + else if (h4 == 64) enc += String.fromCharCode(o1, o2); + else enc += String.fromCharCode(o1, o2, o3); + } while (i < str.length); + + return decodeUTF8(enc); // decode UTF-8 byte-array back to Unicode +} + +function encodeUTF8(str) { // encode multi-byte string into utf-8 multiple single-byte characters + str = str.replace( + /[\u0080-\u07ff]/g, // U+0080 - U+07FF = 2-byte chars + function(c) { + var cc = c.charCodeAt(0); + return String.fromCharCode(0xc0 | cc>>6, 0x80 | cc&0x3f); } + ); + str = str.replace( + /[\u0800-\uffff]/g, // U+0800 - U+FFFF = 3-byte chars + function(c) { + var cc = c.charCodeAt(0); + return String.fromCharCode(0xe0 | cc>>12, 0x80 | cc>>6&0x3F, 0x80 | cc&0x3f); } + ); + return str; +} + +function decodeUTF8(str) { // decode utf-8 encoded string back into multi-byte characters + str = str.replace( + /[\u00c0-\u00df][\u0080-\u00bf]/g, // 2-byte chars + function(c) { + var cc = (c.charCodeAt(0)&0x1f)<<6 | c.charCodeAt(1)&0x3f; + return String.fromCharCode(cc); } + ); + str = str.replace( + /[\u00e0-\u00ef][\u0080-\u00bf][\u0080-\u00bf]/g, // 3-byte chars + function(c) { + var cc = (c.charCodeAt(0)&0x0f)<<12 | (c.charCodeAt(1)&0x3f<<6) | c.charCodeAt(2)&0x3f; + return String.fromCharCode(cc); } + ); + return str; +} + + +function byteArrayToHexStr(b) { // convert byte array to hex string for displaying test vectors + var s = ''; + for (var i=0; i<b.length; i++) s += b[i].toString(16) + ' '; + return s; +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +var plainText = "ROMEO: But, soft! what light through yonder window breaks?\n\ +It is the east, and Juliet is the sun.\n\ +Arise, fair sun, and kill the envious moon,\n\ +Who is already sick and pale with grief,\n\ +That thou her maid art far more fair than she:\n\ +Be not her maid, since she is envious;\n\ +Her vestal livery is but sick and green\n\ +And none but fools do wear it; cast it off.\n\ +It is my lady, O, it is my love!\n\ +O, that she knew she were!\n\ +She speaks yet she says nothing: what of that?\n\ +Her eye discourses; I will answer it.\n\ +I am too bold, 'tis not to me she speaks:\n\ +Two of the fairest stars in all the heaven,\n\ +Having some business, do entreat her eyes\n\ +To twinkle in their spheres till they return.\n\ +What if her eyes were there, they in her head?\n\ +The brightness of her cheek would shame those stars,\n\ +As daylight doth a lamp; her eyes in heaven\n\ +Would through the airy region stream so bright\n\ +That birds would sing and think it were not night.\n\ +See, how she leans her cheek upon her hand!\n\ +O, that I were a glove upon that hand,\n\ +That I might touch that cheek!\n\ +JULIET: Ay me!\n\ +ROMEO: She speaks:\n\ +O, speak again, bright angel! for thou art\n\ +As glorious to this night, being o'er my head\n\ +As is a winged messenger of heaven\n\ +Unto the white-upturned wondering eyes\n\ +Of mortals that fall back to gaze on him\n\ +When he bestrides the lazy-pacing clouds\n\ +And sails upon the bosom of the air."; + +var password = "O Romeo, Romeo! wherefore art thou Romeo?"; + +var cipherText = AESEncryptCtr(plainText, password, 256); +var decryptedText = AESDecryptCtr(cipherText, password, 256); + + +var _sunSpiderInterval = new Date() - _sunSpiderStartDate; + +document.getElementById("console").innerHTML = _sunSpiderInterval; +</script> + + +</body> +</html> diff --git a/build/pgo/js-input/crypto-md5.html b/build/pgo/js-input/crypto-md5.html new file mode 100644 index 000000000..8395107ce --- /dev/null +++ b/build/pgo/js-input/crypto-md5.html @@ -0,0 +1,336 @@ +<!DOCTYPE html> +<head> +<!-- + Copyright (C) 2007 Apple Inc. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--> + +<title>SunSpider crypto-md5</title> + +</head> + +<body> +<h3>crypto-md5</h3> +<div id="console"> +</div> + +<script> + +var _sunSpiderStartDate = new Date(); + +/* + * A JavaScript implementation of the RSA Data Security, Inc. MD5 Message + * Digest Algorithm, as defined in RFC 1321. + * Version 2.1 Copyright (C) Paul Johnston 1999 - 2002. + * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet + * Distributed under the BSD License + * See http://pajhome.org.uk/crypt/md5 for more info. + */ + +/* + * Configurable variables. You may need to tweak these to be compatible with + * the server-side, but the defaults work in most cases. + */ +var hexcase = 0; /* hex output format. 0 - lowercase; 1 - uppercase */ +var b64pad = ""; /* base-64 pad character. "=" for strict RFC compliance */ +var chrsz = 8; /* bits per input character. 8 - ASCII; 16 - Unicode */ + +/* + * These are the functions you'll usually want to call + * They take string arguments and return either hex or base-64 encoded strings + */ +function hex_md5(s){ return binl2hex(core_md5(str2binl(s), s.length * chrsz));} +function b64_md5(s){ return binl2b64(core_md5(str2binl(s), s.length * chrsz));} +function str_md5(s){ return binl2str(core_md5(str2binl(s), s.length * chrsz));} +function hex_hmac_md5(key, data) { return binl2hex(core_hmac_md5(key, data)); } +function b64_hmac_md5(key, data) { return binl2b64(core_hmac_md5(key, data)); } +function str_hmac_md5(key, data) { return binl2str(core_hmac_md5(key, data)); } + +/* + * Perform a simple self-test to see if the VM is working + */ +function md5_vm_test() +{ + return hex_md5("abc") == "900150983cd24fb0d6963f7d28e17f72"; +} + +/* + * Calculate the MD5 of an array of little-endian words, and a bit length + */ +function core_md5(x, len) +{ + /* append padding */ + x[len >> 5] |= 0x80 << ((len) % 32); + x[(((len + 64) >>> 9) << 4) + 14] = len; + + var a = 1732584193; + var b = -271733879; + var c = -1732584194; + var d = 271733878; + + for(var i = 0; i < x.length; i += 16) + { + var olda = a; + var oldb = b; + var oldc = c; + var oldd = d; + + a = md5_ff(a, b, c, d, x[i+ 0], 7 , -680876936); + d = md5_ff(d, a, b, c, x[i+ 1], 12, -389564586); + c = md5_ff(c, d, a, b, x[i+ 2], 17, 606105819); + b = md5_ff(b, c, d, a, x[i+ 3], 22, -1044525330); + a = md5_ff(a, b, c, d, x[i+ 4], 7 , -176418897); + d = md5_ff(d, a, b, c, x[i+ 5], 12, 1200080426); + c = md5_ff(c, d, a, b, x[i+ 6], 17, -1473231341); + b = md5_ff(b, c, d, a, x[i+ 7], 22, -45705983); + a = md5_ff(a, b, c, d, x[i+ 8], 7 , 1770035416); + d = md5_ff(d, a, b, c, x[i+ 9], 12, -1958414417); + c = md5_ff(c, d, a, b, x[i+10], 17, -42063); + b = md5_ff(b, c, d, a, x[i+11], 22, -1990404162); + a = md5_ff(a, b, c, d, x[i+12], 7 , 1804603682); + d = md5_ff(d, a, b, c, x[i+13], 12, -40341101); + c = md5_ff(c, d, a, b, x[i+14], 17, -1502002290); + b = md5_ff(b, c, d, a, x[i+15], 22, 1236535329); + + a = md5_gg(a, b, c, d, x[i+ 1], 5 , -165796510); + d = md5_gg(d, a, b, c, x[i+ 6], 9 , -1069501632); + c = md5_gg(c, d, a, b, x[i+11], 14, 643717713); + b = md5_gg(b, c, d, a, x[i+ 0], 20, -373897302); + a = md5_gg(a, b, c, d, x[i+ 5], 5 , -701558691); + d = md5_gg(d, a, b, c, x[i+10], 9 , 38016083); + c = md5_gg(c, d, a, b, x[i+15], 14, -660478335); + b = md5_gg(b, c, d, a, x[i+ 4], 20, -405537848); + a = md5_gg(a, b, c, d, x[i+ 9], 5 , 568446438); + d = md5_gg(d, a, b, c, x[i+14], 9 , -1019803690); + c = md5_gg(c, d, a, b, x[i+ 3], 14, -187363961); + b = md5_gg(b, c, d, a, x[i+ 8], 20, 1163531501); + a = md5_gg(a, b, c, d, x[i+13], 5 , -1444681467); + d = md5_gg(d, a, b, c, x[i+ 2], 9 , -51403784); + c = md5_gg(c, d, a, b, x[i+ 7], 14, 1735328473); + b = md5_gg(b, c, d, a, x[i+12], 20, -1926607734); + + a = md5_hh(a, b, c, d, x[i+ 5], 4 , -378558); + d = md5_hh(d, a, b, c, x[i+ 8], 11, -2022574463); + c = md5_hh(c, d, a, b, x[i+11], 16, 1839030562); + b = md5_hh(b, c, d, a, x[i+14], 23, -35309556); + a = md5_hh(a, b, c, d, x[i+ 1], 4 , -1530992060); + d = md5_hh(d, a, b, c, x[i+ 4], 11, 1272893353); + c = md5_hh(c, d, a, b, x[i+ 7], 16, -155497632); + b = md5_hh(b, c, d, a, x[i+10], 23, -1094730640); + a = md5_hh(a, b, c, d, x[i+13], 4 , 681279174); + d = md5_hh(d, a, b, c, x[i+ 0], 11, -358537222); + c = md5_hh(c, d, a, b, x[i+ 3], 16, -722521979); + b = md5_hh(b, c, d, a, x[i+ 6], 23, 76029189); + a = md5_hh(a, b, c, d, x[i+ 9], 4 , -640364487); + d = md5_hh(d, a, b, c, x[i+12], 11, -421815835); + c = md5_hh(c, d, a, b, x[i+15], 16, 530742520); + b = md5_hh(b, c, d, a, x[i+ 2], 23, -995338651); + + a = md5_ii(a, b, c, d, x[i+ 0], 6 , -198630844); + d = md5_ii(d, a, b, c, x[i+ 7], 10, 1126891415); + c = md5_ii(c, d, a, b, x[i+14], 15, -1416354905); + b = md5_ii(b, c, d, a, x[i+ 5], 21, -57434055); + a = md5_ii(a, b, c, d, x[i+12], 6 , 1700485571); + d = md5_ii(d, a, b, c, x[i+ 3], 10, -1894986606); + c = md5_ii(c, d, a, b, x[i+10], 15, -1051523); + b = md5_ii(b, c, d, a, x[i+ 1], 21, -2054922799); + a = md5_ii(a, b, c, d, x[i+ 8], 6 , 1873313359); + d = md5_ii(d, a, b, c, x[i+15], 10, -30611744); + c = md5_ii(c, d, a, b, x[i+ 6], 15, -1560198380); + b = md5_ii(b, c, d, a, x[i+13], 21, 1309151649); + a = md5_ii(a, b, c, d, x[i+ 4], 6 , -145523070); + d = md5_ii(d, a, b, c, x[i+11], 10, -1120210379); + c = md5_ii(c, d, a, b, x[i+ 2], 15, 718787259); + b = md5_ii(b, c, d, a, x[i+ 9], 21, -343485551); + + a = safe_add(a, olda); + b = safe_add(b, oldb); + c = safe_add(c, oldc); + d = safe_add(d, oldd); + } + return Array(a, b, c, d); + +} + +/* + * These functions implement the four basic operations the algorithm uses. + */ +function md5_cmn(q, a, b, x, s, t) +{ + return safe_add(bit_rol(safe_add(safe_add(a, q), safe_add(x, t)), s),b); +} +function md5_ff(a, b, c, d, x, s, t) +{ + return md5_cmn((b & c) | ((~b) & d), a, b, x, s, t); +} +function md5_gg(a, b, c, d, x, s, t) +{ + return md5_cmn((b & d) | (c & (~d)), a, b, x, s, t); +} +function md5_hh(a, b, c, d, x, s, t) +{ + return md5_cmn(b ^ c ^ d, a, b, x, s, t); +} +function md5_ii(a, b, c, d, x, s, t) +{ + return md5_cmn(c ^ (b | (~d)), a, b, x, s, t); +} + +/* + * Calculate the HMAC-MD5, of a key and some data + */ +function core_hmac_md5(key, data) +{ + var bkey = str2binl(key); + if(bkey.length > 16) bkey = core_md5(bkey, key.length * chrsz); + + var ipad = Array(16), opad = Array(16); + for(var i = 0; i < 16; i++) + { + ipad[i] = bkey[i] ^ 0x36363636; + opad[i] = bkey[i] ^ 0x5C5C5C5C; + } + + var hash = core_md5(ipad.concat(str2binl(data)), 512 + data.length * chrsz); + return core_md5(opad.concat(hash), 512 + 128); +} + +/* + * Add integers, wrapping at 2^32. This uses 16-bit operations internally + * to work around bugs in some JS interpreters. + */ +function safe_add(x, y) +{ + var lsw = (x & 0xFFFF) + (y & 0xFFFF); + var msw = (x >> 16) + (y >> 16) + (lsw >> 16); + return (msw << 16) | (lsw & 0xFFFF); +} + +/* + * Bitwise rotate a 32-bit number to the left. + */ +function bit_rol(num, cnt) +{ + return (num << cnt) | (num >>> (32 - cnt)); +} + +/* + * Convert a string to an array of little-endian words + * If chrsz is ASCII, characters >255 have their hi-byte silently ignored. + */ +function str2binl(str) +{ + var bin = Array(); + var mask = (1 << chrsz) - 1; + for(var i = 0; i < str.length * chrsz; i += chrsz) + bin[i>>5] |= (str.charCodeAt(i / chrsz) & mask) << (i%32); + return bin; +} + +/* + * Convert an array of little-endian words to a string + */ +function binl2str(bin) +{ + var str = ""; + var mask = (1 << chrsz) - 1; + for(var i = 0; i < bin.length * 32; i += chrsz) + str += String.fromCharCode((bin[i>>5] >>> (i % 32)) & mask); + return str; +} + +/* + * Convert an array of little-endian words to a hex string. + */ +function binl2hex(binarray) +{ + var hex_tab = hexcase ? "0123456789ABCDEF" : "0123456789abcdef"; + var str = ""; + for(var i = 0; i < binarray.length * 4; i++) + { + str += hex_tab.charAt((binarray[i>>2] >> ((i%4)*8+4)) & 0xF) + + hex_tab.charAt((binarray[i>>2] >> ((i%4)*8 )) & 0xF); + } + return str; +} + +/* + * Convert an array of little-endian words to a base-64 string + */ +function binl2b64(binarray) +{ + var tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + var str = ""; + for(var i = 0; i < binarray.length * 4; i += 3) + { + var triplet = (((binarray[i >> 2] >> 8 * ( i %4)) & 0xFF) << 16) + | (((binarray[i+1 >> 2] >> 8 * ((i+1)%4)) & 0xFF) << 8 ) + | ((binarray[i+2 >> 2] >> 8 * ((i+2)%4)) & 0xFF); + for(var j = 0; j < 4; j++) + { + if(i * 8 + j * 6 > binarray.length * 32) str += b64pad; + else str += tab.charAt((triplet >> 6*(3-j)) & 0x3F); + } + } + return str; +} + +var plainText = "Rebellious subjects, enemies to peace,\n\ +Profaners of this neighbour-stained steel,--\n\ +Will they not hear? What, ho! you men, you beasts,\n\ +That quench the fire of your pernicious rage\n\ +With purple fountains issuing from your veins,\n\ +On pain of torture, from those bloody hands\n\ +Throw your mistemper'd weapons to the ground,\n\ +And hear the sentence of your moved prince.\n\ +Three civil brawls, bred of an airy word,\n\ +By thee, old Capulet, and Montague,\n\ +Have thrice disturb'd the quiet of our streets,\n\ +And made Verona's ancient citizens\n\ +Cast by their grave beseeming ornaments,\n\ +To wield old partisans, in hands as old,\n\ +Canker'd with peace, to part your canker'd hate:\n\ +If ever you disturb our streets again,\n\ +Your lives shall pay the forfeit of the peace.\n\ +For this time, all the rest depart away:\n\ +You Capulet; shall go along with me:\n\ +And, Montague, come you this afternoon,\n\ +To know our further pleasure in this case,\n\ +To old Free-town, our common judgment-place.\n\ +Once more, on pain of death, all men depart." + +for (var i = 0; i <4; i++) { + plainText += plainText; +} + +var md5Output = hex_md5(plainText); + + +var _sunSpiderInterval = new Date() - _sunSpiderStartDate; + +document.getElementById("console").innerHTML = _sunSpiderInterval; +</script> + + +</body> +</html> diff --git a/build/pgo/js-input/crypto-otp.html b/build/pgo/js-input/crypto-otp.html new file mode 100644 index 000000000..f6e1ca295 --- /dev/null +++ b/build/pgo/js-input/crypto-otp.html @@ -0,0 +1,1344 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> +<head> +<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" /> +<title>One-Time Pad Generator</title> +<meta name="description" content="JavaScript One-Time Pad Generator" /> +<meta name="author" content="John Walker" /> +<meta name="keywords" content="one, time, pad, generator, onetime, cryptography, JavaScript" /> +<style type="text/css"> + a:link, a:visited { + background-color: inherit; + color: rgb(0%, 0%, 80%); + text-decoration: none; + } + + a:hover { + background-color: rgb(30%, 30%, 100%); + color: rgb(100%, 100%, 100%); + } + + a:active { + color: rgb(100%, 0%, 0%); + background-color: rgb(30%, 30%, 100%); + } + + a.i:link, a.i:visited, a.i:hover { + background-color: inherit; + color: inherit; + text-decoration: none; + } + + body { + margin-left: 15%; + margin-right: 10%; + background-color: #FFFFFF; + color: #000000; + } + + body.jsgen { + margin-left: 5%; + margin-right: 5%; + } + + dt { + margin-top: 0.5em; + } + + img.button { + border: 0px; + vertical-align: middle; + } + + img.keyicon { + vertical-align: bottom; + } + + p, dd, li { + text-align: justify; + } + + p.centre { + text-align: center; + } + + table.r { + float: right; + } + + table.c { + background-color: #E0E0E0; + color: #000000; + margin-left: auto; + margin-right: auto; + } + + td.c { + text-align: center; + } + + textarea { + background-color: #FFFFD0; + color: #000000; + } +</style> +<script type="text/javascript"> +//<![CDATA[ + + loadTime = (new Date()).getTime(); + +/* + + L'Ecuyer's two-sequence generator with a Bays-Durham shuffle + on the back-end. Schrage's algorithm is used to perform + 64-bit modular arithmetic within the 32-bit constraints of + JavaScript. + + Bays, C. and S. D. Durham. ACM Trans. Math. Software: 2 (1976) + 59-64. + + L'Ecuyer, P. Communications of the ACM: 31 (1968) 742-774. + + Schrage, L. ACM Trans. Math. Software: 5 (1979) 132-138. + +*/ + +function uGen(old, a, q, r, m) { // Schrage's modular multiplication algorithm + var t; + + t = Math.floor(old / q); + t = a * (old - (t * q)) - (t * r); + return Math.round((t < 0) ? (t + m) : t); +} + +function LEnext() { // Return next raw value + var i; + + this.gen1 = uGen(this.gen1, 40014, 53668, 12211, 2147483563); + this.gen2 = uGen(this.gen2, 40692, 52774, 3791, 2147483399); + + /* Extract shuffle table index from most significant part + of the previous result. */ + + i = Math.floor(this.state / 67108862); + + // New state is sum of generators modulo one of their moduli + + this.state = Math.round((this.shuffle[i] + this.gen2) % 2147483563); + + // Replace value in shuffle table with generator 1 result + + this.shuffle[i] = this.gen1; + + return this.state; +} + +// Return next random integer between 0 and n inclusive + +function LEnint(n) { + return Math.floor(this.next() / (1 + 2147483562 / (n + 1))); +} + +// Constructor. Called with seed value + +function LEcuyer(s) { + var i; + + this.shuffle = new Array(32); + this.gen1 = this.gen2 = (s & 0x7FFFFFFF); + for (i = 0; i < 19; i++) { + this.gen1 = uGen(this.gen1, 40014, 53668, 12211, 2147483563); + } + + // Fill the shuffle table with values + + for (i = 0; i < 32; i++) { + this.gen1 = uGen(this.gen1, 40014, 53668, 12211, 2147483563); + this.shuffle[31 - i] = this.gen1; + } + this.state = this.shuffle[0]; + this.next = LEnext; + this.nextInt = LEnint; +} + +function sepchar() { + if (rsep) { + var seps = "!#$%&()*+,-./:;<=>?@[]^_{|}~"; + return seps.charAt(sepran.nextInt(seps.length - 1)); + } + return "-"; +} + +/* + * md5.jvs 1.0b 27/06/96 + * + * Javascript implementation of the RSA Data Security, Inc. MD5 + * Message-Digest Algorithm. + * + * Copyright (c) 1996 Henri Torgemane. All Rights Reserved. + * + * Permission to use, copy, modify, and distribute this software + * and its documentation for any purposes and without + * fee is hereby granted provided that this copyright notice + * appears in all copies. + * + * Of course, this soft is provided "as is" without express or implied + * warranty of any kind. + + This version contains some trivial reformatting modifications + by John Walker. + + */ + +function array(n) { + for (i = 0; i < n; i++) { + this[i] = 0; + } + this.length = n; +} + +/* Some basic logical functions had to be rewritten because of a bug in + * Javascript.. Just try to compute 0xffffffff >> 4 with it.. + * Of course, these functions are slower than the original would be, but + * at least, they work! + */ + +function integer(n) { + return n % (0xffffffff + 1); +} + +function shr(a, b) { + a = integer(a); + b = integer(b); + if (a - 0x80000000 >= 0) { + a = a % 0x80000000; + a >>= b; + a += 0x40000000 >> (b - 1); + } else { + a >>= b; + } + return a; +} + +function shl1(a) { + a = a % 0x80000000; + if (a & 0x40000000 == 0x40000000) { + a -= 0x40000000; + a *= 2; + a += 0x80000000; + } else { + a *= 2; + } + return a; +} + +function shl(a, b) { + a = integer(a); + b = integer(b); + for (var i = 0; i < b; i++) { + a = shl1(a); + } + return a; +} + +function and(a, b) { + a = integer(a); + b = integer(b); + var t1 = a - 0x80000000; + var t2 = b - 0x80000000; + if (t1 >= 0) { + if (t2 >= 0) { + return ((t1 & t2) + 0x80000000); + } else { + return (t1 & b); + } + } else { + if (t2 >= 0) { + return (a & t2); + } else { + return (a & b); + } + } +} + +function or(a, b) { + a = integer(a); + b = integer(b); + var t1 = a - 0x80000000; + var t2 = b - 0x80000000; + if (t1 >= 0) { + if (t2 >= 0) { + return ((t1 | t2) + 0x80000000); + } else { + return ((t1 | b) + 0x80000000); + } + } else { + if (t2 >= 0) { + return ((a | t2) + 0x80000000); + } else { + return (a | b); + } + } +} + +function xor(a, b) { + a = integer(a); + b = integer(b); + var t1 = a - 0x80000000; + var t2 = b - 0x80000000; + if (t1 >= 0) { + if (t2 >= 0) { + return (t1 ^ t2); + } else { + return ((t1 ^ b) + 0x80000000); + } + } else { + if (t2 >= 0) { + return ((a ^ t2) + 0x80000000); + } else { + return (a ^ b); + } + } +} + +function not(a) { + a = integer(a); + return 0xffffffff - a; +} + +/* Here begin the real algorithm */ + +var state = new array(4); +var count = new array(2); + count[0] = 0; + count[1] = 0; +var buffer = new array(64); +var transformBuffer = new array(16); +var digestBits = new array(16); + +var S11 = 7; +var S12 = 12; +var S13 = 17; +var S14 = 22; +var S21 = 5; +var S22 = 9; +var S23 = 14; +var S24 = 20; +var S31 = 4; +var S32 = 11; +var S33 = 16; +var S34 = 23; +var S41 = 6; +var S42 = 10; +var S43 = 15; +var S44 = 21; + +function F(x, y, z) { + return or(and(x, y), and(not(x), z)); +} + +function G(x, y, z) { + return or(and(x, z), and(y, not(z))); +} + +function H(x, y, z) { + return xor(xor(x, y), z); +} + +function I(x, y, z) { + return xor(y ,or(x , not(z))); +} + +function rotateLeft(a, n) { + return or(shl(a, n), (shr(a, (32 - n)))); +} + +function FF(a, b, c, d, x, s, ac) { + a = a + F(b, c, d) + x + ac; + a = rotateLeft(a, s); + a = a + b; + return a; +} + +function GG(a, b, c, d, x, s, ac) { + a = a + G(b, c, d) + x + ac; + a = rotateLeft(a, s); + a = a + b; + return a; +} + +function HH(a, b, c, d, x, s, ac) { + a = a + H(b, c, d) + x + ac; + a = rotateLeft(a, s); + a = a + b; + return a; +} + +function II(a, b, c, d, x, s, ac) { + a = a + I(b, c, d) + x + ac; + a = rotateLeft(a, s); + a = a + b; + return a; +} + +function transform(buf, offset) { + var a = 0, b = 0, c = 0, d = 0; + var x = transformBuffer; + + a = state[0]; + b = state[1]; + c = state[2]; + d = state[3]; + + for (i = 0; i < 16; i++) { + x[i] = and(buf[i * 4 + offset], 0xFF); + for (j = 1; j < 4; j++) { + x[i] += shl(and(buf[i * 4 + j + offset] ,0xFF), j * 8); + } + } + + /* Round 1 */ + a = FF( a, b, c, d, x[ 0], S11, 0xd76aa478); /* 1 */ + d = FF( d, a, b, c, x[ 1], S12, 0xe8c7b756); /* 2 */ + c = FF( c, d, a, b, x[ 2], S13, 0x242070db); /* 3 */ + b = FF( b, c, d, a, x[ 3], S14, 0xc1bdceee); /* 4 */ + a = FF( a, b, c, d, x[ 4], S11, 0xf57c0faf); /* 5 */ + d = FF( d, a, b, c, x[ 5], S12, 0x4787c62a); /* 6 */ + c = FF( c, d, a, b, x[ 6], S13, 0xa8304613); /* 7 */ + b = FF( b, c, d, a, x[ 7], S14, 0xfd469501); /* 8 */ + a = FF( a, b, c, d, x[ 8], S11, 0x698098d8); /* 9 */ + d = FF( d, a, b, c, x[ 9], S12, 0x8b44f7af); /* 10 */ + c = FF( c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */ + b = FF( b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */ + a = FF( a, b, c, d, x[12], S11, 0x6b901122); /* 13 */ + d = FF( d, a, b, c, x[13], S12, 0xfd987193); /* 14 */ + c = FF( c, d, a, b, x[14], S13, 0xa679438e); /* 15 */ + b = FF( b, c, d, a, x[15], S14, 0x49b40821); /* 16 */ + + /* Round 2 */ + a = GG( a, b, c, d, x[ 1], S21, 0xf61e2562); /* 17 */ + d = GG( d, a, b, c, x[ 6], S22, 0xc040b340); /* 18 */ + c = GG( c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */ + b = GG( b, c, d, a, x[ 0], S24, 0xe9b6c7aa); /* 20 */ + a = GG( a, b, c, d, x[ 5], S21, 0xd62f105d); /* 21 */ + d = GG( d, a, b, c, x[10], S22, 0x2441453); /* 22 */ + c = GG( c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */ + b = GG( b, c, d, a, x[ 4], S24, 0xe7d3fbc8); /* 24 */ + a = GG( a, b, c, d, x[ 9], S21, 0x21e1cde6); /* 25 */ + d = GG( d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */ + c = GG( c, d, a, b, x[ 3], S23, 0xf4d50d87); /* 27 */ + b = GG( b, c, d, a, x[ 8], S24, 0x455a14ed); /* 28 */ + a = GG( a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */ + d = GG( d, a, b, c, x[ 2], S22, 0xfcefa3f8); /* 30 */ + c = GG( c, d, a, b, x[ 7], S23, 0x676f02d9); /* 31 */ + b = GG( b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */ + + /* Round 3 */ + a = HH( a, b, c, d, x[ 5], S31, 0xfffa3942); /* 33 */ + d = HH( d, a, b, c, x[ 8], S32, 0x8771f681); /* 34 */ + c = HH( c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */ + b = HH( b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */ + a = HH( a, b, c, d, x[ 1], S31, 0xa4beea44); /* 37 */ + d = HH( d, a, b, c, x[ 4], S32, 0x4bdecfa9); /* 38 */ + c = HH( c, d, a, b, x[ 7], S33, 0xf6bb4b60); /* 39 */ + b = HH( b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */ + a = HH( a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */ + d = HH( d, a, b, c, x[ 0], S32, 0xeaa127fa); /* 42 */ + c = HH( c, d, a, b, x[ 3], S33, 0xd4ef3085); /* 43 */ + b = HH( b, c, d, a, x[ 6], S34, 0x4881d05); /* 44 */ + a = HH( a, b, c, d, x[ 9], S31, 0xd9d4d039); /* 45 */ + d = HH( d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */ + c = HH( c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */ + b = HH( b, c, d, a, x[ 2], S34, 0xc4ac5665); /* 48 */ + + /* Round 4 */ + a = II( a, b, c, d, x[ 0], S41, 0xf4292244); /* 49 */ + d = II( d, a, b, c, x[ 7], S42, 0x432aff97); /* 50 */ + c = II( c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */ + b = II( b, c, d, a, x[ 5], S44, 0xfc93a039); /* 52 */ + a = II( a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */ + d = II( d, a, b, c, x[ 3], S42, 0x8f0ccc92); /* 54 */ + c = II( c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */ + b = II( b, c, d, a, x[ 1], S44, 0x85845dd1); /* 56 */ + a = II( a, b, c, d, x[ 8], S41, 0x6fa87e4f); /* 57 */ + d = II( d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */ + c = II( c, d, a, b, x[ 6], S43, 0xa3014314); /* 59 */ + b = II( b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */ + a = II( a, b, c, d, x[ 4], S41, 0xf7537e82); /* 61 */ + d = II( d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */ + c = II( c, d, a, b, x[ 2], S43, 0x2ad7d2bb); /* 63 */ + b = II( b, c, d, a, x[ 9], S44, 0xeb86d391); /* 64 */ + + state[0] += a; + state[1] += b; + state[2] += c; + state[3] += d; + +} + +function init() { + count[0] = count[1] = 0; + state[0] = 0x67452301; + state[1] = 0xefcdab89; + state[2] = 0x98badcfe; + state[3] = 0x10325476; + for (i = 0; i < digestBits.length; i++) { + digestBits[i] = 0; + } +} + +function update(b) { + var index, i; + + index = and(shr(count[0],3) , 0x3F); + if (count[0] < 0xFFFFFFFF - 7) { + count[0] += 8; + } else { + count[1]++; + count[0] -= 0xFFFFFFFF + 1; + count[0] += 8; + } + buffer[index] = and(b, 0xff); + if (index >= 63) { + transform(buffer, 0); + } +} + +function finish() { + var bits = new array(8); + var padding; + var i = 0, index = 0, padLen = 0; + + for (i = 0; i < 4; i++) { + bits[i] = and(shr(count[0], (i * 8)), 0xFF); + } + for (i = 0; i < 4; i++) { + bits[i + 4] = and(shr(count[1], (i * 8)), 0xFF); + } + index = and(shr(count[0], 3), 0x3F); + padLen = (index < 56) ? (56 - index) : (120 - index); + padding = new array(64); + padding[0] = 0x80; + for (i = 0; i < padLen; i++) { + update(padding[i]); + } + for (i = 0; i < 8; i++) { + update(bits[i]); + } + + for (i = 0; i < 4; i++) { + for (j = 0; j < 4; j++) { + digestBits[i * 4 + j] = and(shr(state[i], (j * 8)) , 0xFF); + } + } +} + +/* End of the MD5 algorithm */ + +function gen() { + window.status = "Generating..."; + document.getElementById('onetime').pad.value = ""; + + lower = document.getElementById('onetime').textcase.selectedIndex == 0; + upper = document.getElementById('onetime').textcase.selectedIndex == 1; + mixed = document.getElementById('onetime').textcase.selectedIndex == 2; + rsep = document.getElementById('onetime').rsep.checked; + if (!(numeric = document.getElementById('onetime').keytype[0].checked)) { + english = document.getElementById('onetime').keytype[1].checked; + gibberish = document.getElementById('onetime').keytype[3].checked; + } + clockseed = document.getElementById('onetime').seedy[0].checked + makesig = document.getElementById('onetime').dosig.checked; + npass = document.getElementById('onetime').nkeys.value; + pw_length = Math.round(document.getElementById('onetime').klength.value); + sep = document.getElementById('onetime').sep.value; + linelen = document.getElementById('onetime').linelen.value; +// 01234567890123456789012345678901 + charcodes = " " + + "!\"#$%&'()*+,-./0123456789:;<=>?" + + "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_" + + "`abcdefghijklmnopqrstuvwxyz{|}~"; + + if (clockseed) { + var n, j, ran0; + + /* Obtain seed from the clock. To reduce the likelihood + of the seed being guessed, we create the seed by combining + the time of the request with the time the page was loaded, + then use that composite value to seed an auxiliary generator + which is cycled between one and 32 times based on the time + derived initial seed, with the output of the generator fed + back into the seed we use to generate the pad. */ + + seed = Math.round((new Date()).getTime() % Math.pow(2, 31)); + ran0 = new LEcuyer((seed ^ Math.round(loadTime % Math.pow(2, 31))) & 0x7FFFFFFF); + for (j = 0; j < (5 + ((seed >> 3) & 0xF)); j++) { + n = ran0.nextInt(31); + } + while (n-- >= 0) { + seed = ((seed << 11) | (seed >>> (32 - 11))) ^ ran0.next(); + } + seed &= 0x7FFFFFFF; + document.getElementById('onetime').seeder.value = seed; + } else { + var useed, seedNum; + + /* Obtain seed from user specification. If the seed is a + decimal number, use it as-is. If it contains any + non-numeric characters, construct a hash code and + use that as the seed. */ + + useed = document.getElementById('onetime').seeder.value; + seedNum = true; + for (i = 0; i < useed.length; i++) { + if ("0123456789".indexOf(useed.charAt(i)) == -1) { + seedNum = false; + break; + } + } + if (seedNum) { + seed = Math.round(Math.floor(document.getElementById('onetime').seeder.value) % Math.pow(2, 31)); + document.getElementById('onetime').seeder.value = seed; + } else { + var s, t, iso, hex; + + iso = ""; + hex = "0123456789ABCDEF"; + for (i = 32; i < 256; i++) { + if (i < 127 || i >= 160) { + // Why not "s = i.toString(16);"? Doesn't work in Netscape 3.0 + iso += "%" + hex.charAt(i >> 4) + hex.charAt(i & 0xF); + } + } + iso = unescape(iso); + s = 0; + for (i = 0; i < useed.length; i++) { + t = iso.indexOf(useed.charAt(i)); + if (t < 0) { + t = 17; + } + s = 0x7FFFFFFF & (((s << 5) | (s >> (32 - 5))) ^ t); + } + seed = s; + } + } + ran1 = new LEcuyer(seed); + ran2 = new LEcuyer(seed); + if (rsep) { + /* Use a separate random generator for separators + so that results are the same for a given seed + for both choices of separators. */ + sepran = new LEcuyer(seed); + } + + ndig = 1; + j = 10; + while (npass >= j) { + ndig++; + j *= 10; + } + pw_item = pw_length + (sep > 0 ? (pw_length / sep) : 0); + pw_item += ndig + 5; + j = pw_item * 3; + if (j < 132) { + j = 132; + } + npline = Math.floor(linelen / pw_item); + if (npline < 1) { + npline = 0; + } + v = ""; + md5v = ""; + lineno = 0; + if (!numeric) { + letters = "abcdefghijklmnopqrstuvwxyz"; + if (upper) { + letters = letters.toUpperCase(); + } + if (english) { + + // Frequency of English digraphs (from D. Edwards 1/27/66) + + frequency = new Array( + new Array(4, 20, 28, 52, 2, 11, 28, 4, 32, 4, 6, 62, + 23, 167, 2, 14, 0, 83, 76, 127, 7, 25, 8, 1, + 9, 1), /* aa - az */ + + new Array(13, 0, 0, 0, 55, 0, 0, 0, 8, 2, 0, 22, 0, 0, + 11, 0, 0, 15, 4, 2, 13, 0, 0, 0, 15, 0), /* ba - bz */ + + new Array(32, 0, 7, 1, 69, 0, 0, 33, 17, 0, 10, 9, 1, + 0, 50, 3, 0, 10, 0, 28, 11, 0, 0, 0, 3, 0), /* ca - cz */ + + new Array(40, 16, 9, 5, 65, 18, 3, 9, 56, 0, 1, 4, 15, + 6, 16, 4, 0, 21, 18, 53, 19, 5, 15, 0, 3, 0), /* da - dz */ + + new Array(84, 20, 55, 125, 51, 40, 19, 16, 50, 1, 4, + 55, 54, 146, 35, 37, 6, 191, 149, 65, 9, 26, + 21, 12, 5, 0), /* ea - ez */ + + new Array(19, 3, 5, 1, 19, 21, 1, 3, 30, 2, 0, 11, 1, + 0, 51, 0, 0, 26, 8, 47, 6, 3, 3, 0, 2, 0), /* fa - fz */ + + new Array(20, 4, 3, 2, 35, 1, 3, 15, 18, 0, 0, 5, 1, + 4, 21, 1, 1, 20, 9, 21, 9, 0, 5, 0, 1, 0), /* ga - gz */ + + new Array(101, 1, 3, 0, 270, 5, 1, 6, 57, 0, 0, 0, 3, + 2, 44, 1, 0, 3, 10, 18, 6, 0, 5, 0, 3, 0), /* ha - hz */ + + new Array(40, 7, 51, 23, 25, 9, 11, 3, 0, 0, 2, 38, + 25, 202, 56, 12, 1, 46, 79, 117, 1, 22, 0, + 4, 0, 3), /* ia - iz */ + + new Array(3, 0, 0, 0, 5, 0, 0, 0, 1, 0, 0, 0, 0, 0, 4, + 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0), /* ja - jz */ + + new Array(1, 0, 0, 0, 11, 0, 0, 0, 13, 0, 0, 0, 0, 2, + 0, 0, 0, 0, 6, 2, 1, 0, 2, 0, 1, 0), /* ka - kz */ + + new Array(44, 2, 5, 12, 62, 7, 5, 2, 42, 1, 1, 53, 2, + 2, 25, 1, 1, 2, 16, 23, 9, 0, 1, 0, 33, 0), /* la - lz */ + + new Array(52, 14, 1, 0, 64, 0, 0, 3, 37, 0, 0, 0, 7, + 1, 17, 18, 1, 2, 12, 3, 8, 0, 1, 0, 2, 0), /* ma - mz */ + + new Array(42, 10, 47, 122, 63, 19, 106, 12, 30, 1, 6, + 6, 9, 7, 54, 7, 1, 7, 44, 124, 6, 1, 15, 0, + 12, 0), /* na - nz */ + + new Array(7, 12, 14, 17, 5, 95, 3, 5, 14, 0, 0, 19, + 41, 134, 13, 23, 0, 91, 23, 42, 55, 16, 28, + 0, 4, 1), /* oa - oz */ + + new Array(19, 1, 0, 0, 37, 0, 0, 4, 8, 0, 0, 15, 1, 0, + 27, 9, 0, 33, 14, 7, 6, 0, 0, 0, 0, 0), /* pa - pz */ + + new Array(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 17, 0, 0, 0, 0, 0), /* qa - qz */ + + new Array(83, 8, 16, 23, 169, 4, 8, 8, 77, 1, 10, 5, + 26, 16, 60, 4, 0, 24, 37, 55, 6, 11, 4, 0, + 28, 0), /* ra - rz */ + + new Array(65, 9, 17, 9, 73, 13, 1, 47, 75, 3, 0, 7, + 11, 12, 56, 17, 6, 9, 48, 116, 35, 1, 28, 0, + 4, 0), /* sa - sz */ + + new Array(57, 22, 3, 1, 76, 5, 2, 330, 126, 1, 0, 14, + 10, 6, 79, 7, 0, 49, 50, 56, 21, 2, 27, 0, + 24, 0), /* ta - tz */ + + new Array(11, 5, 9, 6, 9, 1, 6, 0, 9, 0, 1, 19, 5, 31, + 1, 15, 0, 47, 39, 31, 0, 3, 0, 0, 0, 0), /* ua - uz */ + + new Array(7, 0, 0, 0, 72, 0, 0, 0, 28, 0, 0, 0, 0, 0, + 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0), /* va - vz */ + + new Array(36, 1, 1, 0, 38, 0, 0, 33, 36, 0, 0, 4, 1, + 8, 15, 0, 0, 0, 4, 2, 0, 0, 1, 0, 0, 0), /* wa - wz */ + + new Array(1, 0, 2, 0, 0, 1, 0, 0, 3, 0, 0, 0, 0, 0, 1, + 5, 0, 0, 0, 3, 0, 0, 1, 0, 0, 0), /* xa - xz */ + + new Array(14, 5, 4, 2, 7, 12, 12, 6, 10, 0, 0, 3, 7, + 5, 17, 3, 0, 4, 16, 30, 0, 0, 5, 0, 0, 0), /* ya - yz */ + + new Array(1, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) /* za - zz */ ); + + // This MUST be equal to the sum of the equivalent rows above. + + row_sums = new Array( + 796, 160, 284, 401, 1276, 262, 199, 539, 777, + 16, 39, 351, 243, 751, 662, 181, 17, 683, + 662, 968, 248, 115, 180, 17, 162, 5 + ); + + // Frequencies of starting characters. + + start_freq = new Array( + 1299, 425, 725, 271, 375, 470, 93, 223, 1009, + 24, 20, 355, 379, 319, 823, 618, 21, 317, + 962, 1991, 271, 104, 516, 6, 16, 14 + ); + + // This MUST be equal to the sum of all elements in the above array. + + total_sum = 11646; + } + if (gibberish) { + gibber = "abcdefghijklmnopqrstuvwxyz" + + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + + "0123456789" + + "!#$%&()*+,-./:;<=>?@[]^_{|}~"; + if (upper) { + /* Convert to upper case, leaving two copies of the + alphabet for two reasons: first, to favour letters + over gnarl, and second, to change only the letter case + when the mode is selected. */ + gibber = gibber.toUpperCase(); + } else if (lower) { + gibber = gibber.toLowerCase(); + } + } + } + for (line = 1; line <= npass; line++) { + password = ""; + if (numeric) { + for (nchars = 0; nchars < pw_length; nchars++) { + if ((sep > 0) && ((nchars % sep) == 0) && (nchars > 0)) { + password += sepchar(); + } + password += ran1.nextInt(9); + } + } else if (!english) { + for (nchars = 0; nchars < pw_length; nchars++) { + if ((sep > 0) && ((nchars % sep) == 0) && (nchars > 0)) { + password += sepchar(); + } + if (gibberish) { + password += gibber.charAt(ran1.nextInt(gibber.length - 1)); + } else { + password += letters.charAt(ran1.nextInt(25)); + } + } + } else { + position = ran1.nextInt(total_sum - 1); + for (row_position = 0, j = 0; position >= row_position; + row_position += start_freq[j], j++) { + continue; + } + + password = letters.charAt(i = j - 1); + nch = 1; + for (nchars = pw_length - 1; nchars; --nchars) { + + // Now find random position within the row. + + position = ran1.nextInt(row_sums[i] - 1); + for (row_position = 0, j = 0; + position >= row_position; + row_position += frequency[i][j], j++) { + } + + if ((sep > 0) && ((nch % sep) == 0)) { + password += sepchar(); + } + nch++; + password += letters.charAt(i = j - 1); + } + } + + if ((!numeric) && (!gibberish) && mixed) { + var pwm = ''; + var j; + for (j = 0; j < password.length; j++) { + pwm += ran2.nextInt(1) ? (password.charAt(j)) : (password.charAt(j).toUpperCase()); + } + password = pwm; + } + + /* If requested, calculate the MD5 signature for this key and + and save for later appending to the results. */ + + if (makesig) { + var n, m, hex = "0123456789ABCDEF"; + + init(); + for (m = 0; m < password.length; m++) { + update(32 + charcodes.indexOf(password.charAt(m))); + } + finish(); + + for (n = 0; n < 16; n++) { + md5v += hex.charAt(digestBits[n] >> 4); + md5v += hex.charAt(digestBits[n] & 0xF); + } + md5v += "\n"; + } + + aline = "" + line; + while (aline.length < ndig) { + aline = " " + aline; + } + v += aline + ") " + password; + + if ((++lineno) >= npline) { + v += "\n"; + lineno = 0; + } else { + v += " "; + } + } + + if (makesig) { + v += "\n---------- MD5 Signatures ----------\n" + md5v; + } + + document.getElementById('onetime').pad.value = v; + window.status = "Done."; +} + +function loadHandler() { + for (var i = 0; i < 25; i++) { + gen(); + } +}; + +//]]> +</script> + +</head> + +<body class="jsgen" onload="loadHandler();"> + +<h1><img src="key.gif" class="keyicon" alt="" + width="40" height="40" /> One-Time Pad Generator</h1> + +<p> +This page, which requires that your browser support JavaScript +(see <a href="#why"><cite>Why JavaScript</cite></a> below), +generates one-time pads or password lists in a variety of +forms. It is based a high-quality pseudorandom sequence +generator, which can be seeded either from the current date +and time, or from a seed you provide. Fill in the form below +to select the format of the pad and press “Generate” to +create the pad in the text box. You can then copy and paste +the generated pad into another window to use as you wish. +Each of the labels on the request form is linked to a description +of that parameter. +</p> + +<form id="onetime" action="#" onsubmit="return false;"> + +<p class="centre"> +<b>Output:</b> +<a href="#NumberOfKeys">Number of keys</a>: <input type="text" name="nkeys" value="20" size="4" maxlength="12" /> +<a href="#LineLength">Line length</a>: <input type="text" name="linelen" value="48" size="3" maxlength="12" /> +<br /> +<b>Format:</b> +<a href="#KeyLength">Key length</a>: <input type="text" name="klength" value="8" size="3" maxlength="12" /> +<a href="#GroupLength">Group length</a>: <input type="text" name="sep" value="4" size="2" maxlength="12" /> + +<br /> +<b>Composition:</b> +<a href="#KeyText">Key text</a>: <input type="radio" name="keytype" /> Numeric +<input type="radio" name="keytype" /> Word-like +<input type="radio" name="keytype" checked="checked" /> Alphabetic +<input type="radio" name="keytype" /> Gibberish +<br /> +<a href="#LetterCase">Letters:</a> +<select size="i" name="textcase"> + + <option value="1" selected="selected">Lower case</option> + <option value="2">Upper case</option> + <option value="3">Mixed case</option> +</select> + +<input type="checkbox" name="rsep" /> <a href="#RandomSep">Random separators</a> +<input type="checkbox" name="dosig" /> <a href="#Signatures">Include signatures</a> + +<br /> +<b><a href="#Seed">Seed:</a></b> +<input type="radio" name="seedy" checked="checked" /> From clock +<input type="radio" name="seedy" /> User-defined: +<input type="text" name="seeder" value="" size="12" maxlength="128" + onchange="document.getElementById('onetime').seedy[1].checked=true;" /> +<br /> +<input type="button" value=" Generate " onclick="gen();" /> + +<input type="button" value=" Clear " onclick="document.getElementById('onetime').pad.value = '';" /> + +<input type="button" value=" Select " onclick="document.getElementById('onetime').pad.select();" /><br /> +<textarea name="pad" rows="12" cols="72"> + +Uh, oh. It appears your browser either does not support +JavaScript or that JavaScript has been disabled. You'll +have to replace your browser with one supporting JavaScript +(or enable it, if that's the problem) before you can use +this page. +</textarea> +</p> + +</form> + +<script type="text/javascript"> +//<![CDATA[ + // Clear out "sorry, no JavaScript" message from text box. + document.getElementById('onetime').pad.value = ""; +//]]> +</script> + +<h2><a name="details">Details</a></h2> + +<p> +Each of the fields in the one-time pad request form is described +below. +</p> + +<h3><a name="output">Output</a></h3> + +<h4><a name="NumberOfKeys">Number of keys</a></h4> + +<p> +Enter the number of keys you'd like to generate. If you generate +more than fit in the results text box, you can use the scroll +bar to view the additional lines. +</p> + +<h4><a name="LineLength">Line length</a></h4> + +<p> +Lines in the output will be limited to the given length (or contain +only one key if the line length is less than required for a single +key). If the line length is greater than the width of the results +box, you can use the horizontal scroll bar to view the rest of the +line. Enter <tt>0</tt> to force one key per line; this is handy +when you're preparing a list of keys to be read by a computer program. +</p> + +<h3><a name="format">Format</a></h3> + +<h4><a name="KeyLength">Key length</a></h4> + +<p> +Each key will contain this number of characters, not counting +separators between groups. +</p> + +<h4><a name="GroupLength">Group length</a></h4> + +<p> +If a nonzero value is entered in this field, the key will be broken +into groups of the given number of characters by separators. Humans +find it easier to read and remember sequences of characters when +divided into groups of five or fewer characters. +</p> + +<h3><a name="composition">Composition</a></h3> + +<h4><a name="KeyText">Key text</a></h4> + +<p> +This set of radio buttons lets you select the character set used in +the keys. The alternatives are listed in order of +increasing security. +</p> + +<blockquote> +<dl> +<dt><b>Numeric</b></dt> +<dd>Keys contain only the decimal digits “0” through “9”. + <em>Least secure.</em></dd> + +<dt><b>Word-like</b></dt> +<dd>Keys are composed of alphabetic characters which obey the + digraph statistics of English text. Such keys contain + sequences of vowels and consonants familiar to speakers + of Western languages, and are therefore usually easier to + memorise but, for a given key length, are less secure than + purely random letters.</dd> + +<dt><b>Alphabetic</b></dt> +<dd>Keys consist of letters of the alphabet chosen at random. + Each character has an equal probability of being one of + the 26 letters.</dd> + +<dt><b>Gibberish</b></dt> +<dd>Keys use most of the printable ASCII character set, excluding + only characters frequently used for quoting purposes. This + option provides the greatest security for a given key length, + but most people find keys like this difficult to memorise or + even transcribe from a printed pad. If a human is in the loop, + it's often better to use a longer alphabetic or word-like key. + <em>Most secure.</em></dd> +</dl> + +</blockquote> + +<h4><a name="LetterCase">Letters</a></h4> + +<p> +The case of letters in keys generated with Word-like, Alphabetic, and +Gibberish key text will be as chosen. Most people find it easier to +read lower case letters than all capitals, but for some applications +(for example, where keys must be scanned optically by hardware that +only recognises capital letters), capitals are required. Selecting +“Mixed case” creates keys with a mix of upper- and +lower-case letters; such keys are more secure than those with uniform +letter case, but do not pass the “telephone test”: you +can't read them across a (hopefully secure) voice link without having +to indicate whether each letter is or is not a capital. +</p> + +<h4><a name="RandomSep">Random separators</a></h4> + +<p> +When the <a href="#KeyLength">Key length</a> is longer than +a nonzero <a href="#GroupLength">Group length</a> specification, +the key is divided into sequences of the given group length +by separator characters. By default, a hyphen, “<tt>-</tt>”, is used +to separate groups. If you check this box, separators will be +chosen at random among punctuation marks generally acceptable +for applications such as passwords. If you're generating passwords +for a computer system, random separators dramatically increase +the difficulty of guessing passwords by exhaustive search. +</p> + +<h4><a name="Signatures">Include signatures</a></h4> + +<p> + +When this box is checked, at the end of the list of keys, preceded by +a line beginning with ten dashes “<tt>-</tt>”, the 128 bit MD5 signature of +each key is given, one per line, with signatures expressed as 32 +hexadecimal digits. Key signatures can be used to increase security +when keys are used to control access to computer systems or databases. +Instead of storing a copy of the keys, the computer stores their +signatures. When the user enters a key, its signature is computed +with the same MD5 algorithm used to generate it initially, and the key +is accepted only if the signature matches. Since discovering +a key which will generate a given signature is believed to be +computationally prohibitive, even if the list of signatures stored on +the computer is compromised, that information will not permit an +intruder to deduce a valid key. +</p> + +<p> +Signature calculation is a computationally intense process for which +JavaScript is not ideally suited; be patient while signatures are +generated, especially if your computer has modest +processing speed. +</p> + +<p> +For signature-based validation to be secure, it is essential +the original keys be long enough to prohibit discovery of matching +signatures by exhaustive search. Suppose, for example, one used +four digit numeric keys, as used for Personal Identification +Numbers (PINs) by many credit card systems. Since only 10,000 +different keys exist, one could simply compute the signatures of +every possible key from 0000 through 9999, permitting an attacker who +came into possession of the table of signatures to recover the +keys by a simple lookup process. For maximum security, keys must +contain at least as much information as the 128 bit signatures +computed from them. This implies a minimum key length (not counting +non-random separator characters) for the various key formats as +follows: +</p> + +<table class="c" border="border" cellpadding="4"> +<tr><th>Key Composition</th> <th>Minimum Characters</th></tr> + +<tr><td>Numeric</td> <td class="c">39</td></tr> +<tr><td>Word-like</td> <td class="c">30</td></tr> +<tr><td>Alphabetic</td> <td class="c">28</td></tr> +<tr><td>Gibberish</td> <td class="c">20</td></tr> +</table> + +<p> +It should be noted that for many practical applications there is no +need for anything approaching 128-bit security. The guidelines above +apply only in the case where maximum protection in the event of +undetected compromise of key signatures occurs. In many +cases, much shorter keys are acceptable, especially when it is assumed +that a compromise of the system's password or signature database would +be only part of a much more serious subversion of all resources +on the system. +</p> + +<h3><a name="Seed">Seed</a></h3> + +<p> +The <em>seed</em> is the starting value which determines all +subsequent values in the pseudorandom sequence used to generate +the one-time pad. Given the seed, the pad can be reproduced. The +seed is a 31-bit number which can be derived from the date and +time at which the one-time pad was requested, or from a +user-defined seed value. If the user-defined seed consists +entirely of decimal digits, it is used directly as the seed, +modulo 2<sup>31</sup>; if a string containing non-digit characters +is entered, it is used to compute a <em>hash code</em> which is +used to seed the generator. + +</p> + +<p> +When the clock is used to create the seed, the seed value is entered +in the User-defined box to allow you, by checking “User-defined”, +to produce additional pads with the same seed. +</p> + +<h2><a name="why">Why JavaScript?</a></h2> + +<p> +At first glance, JavaScript may seem an odd choice for programming +a page such as this. The one-time pad generator program is rather +large and complicated, and downloading it to your browser takes longer +than would be required for a Java applet or to transfer a +one-time pad generated by a CGI program on the Web server. I chose +JavaScript for two reasons: <em>security</em> and <em>transparency</em>. + +</p> + +<p> +<b>Security.</b> +The sole reason for the existence of one-time pads is to +provide a source of information known only to people to whom +they have been distributed in a secure manner. This means +the generation process cannot involve any link whose security +is suspect. If the pad were generated on a Web server and +transmitted to you, it would have to pass over the +Internet, where any intermediate site might make a copy +of your pad before you even received it. Even if some +mechanism such as encryption could absolutely prevent the +pad's being intercepted, you'd still have no way to be sure +the site generating the pad didn't keep a copy +in a file, conveniently tagged with your Internet address. +</p> + +<p> +In order to have any degree of security, it is essential +that the pad be generated on <em>your</em> computer, without +involving any transmission or interaction with other +sites on the Internet. A Web browser with JavaScript makes +this possible, since the generation program embedded in this +page runs entirely on your own computer and does not +transmit anything over the Internet. Its output appears +only in the text box, allowing you to cut and paste it +to another application. From there on, its security is +up to you. +</p> + +<p> +Security is never absolute. A one-time pad generated with +this page might be compromised in a variety of ways, including +the following: + +</p> + +<ul> +<li> Your Web browser and/or JavaScript interpreter may + contain bugs or deliberate security violations + which report activity on your computer back to some + other Internet site.</li> + +<li> Some other applet running on another page of your + browser, perhaps without your being aware of its + existence, is spying on other windows.</li> + +<li> Some other application running on your computer + may have compromised your system's security and + be snooping on your activity.</li> + +<li> Your Web browser may be keeping a “history log” + + or “cache” of data you generate. Somebody may + come along later and recover a copy of the pad + from that log.</li> + +<li> The implementation of this page may contain a bug + or deliberate error which makes its output + predictable. This is why <a href="#trans"><cite>transparency</cite></a>, + discussed below, is essential.</li> + +<li> Your computer's security may have been compromised + physically; when's the last time you checked that a + bug that transmits your keystrokes and/or screen + contents to that white van parked down the street + wasn't lurking inside your computer cabinet?</li> +</ul> + +<p> +One can whip oneself into a fine fever of paranoia worrying about +things like this. One way to rule out the most probable risks +is to download a copy of the generator page and run it +from a “<tt>file:</tt>” URL on a computer which has no network +connection whatsoever and is located in a secure location +under your control. And look very carefully at any files +created by your Web browser. You may find the most interesting +things squirreled away there…. +</p> + +<p> +<b><a name="trans">Transparency</a>.</b> +Any security-related tool is only as good as its design +and implementation. <em>Transparency</em> means that, in +essence, all the moving parts are visible so you can judge +for yourself whether the tool merits your confidence. In +the case of a program, this means that source code must +be available, and that you can verify that the program +you're running corresponds to the source code provided. + +</p> + +<p> +The very nature of JavaScript achieves this transparency. +The program is embedded into this actual Web page; to +examine it you need only use your browser's “View Source” +facility, or save the page into a file on your computer +and read it with a text editor. JavaScript's being +an interpreted language eliminates the risk of your running +a program different from the purported source code: with +an interpreted language what you read is what you run. +</p> + +<p> +Transparency is important even if you don't know enough about +programming or security to determine whether the program +contains any flaws. The very fact that it can be examined +by anybody allows those with the required expertise to pass +judgment, and you can form your own conclusions based on +their analysis. +</p> + +<h2>Credits</h2> + +<p> + +The pseudorandom sequence generator is based on L'Ecuyer's +two-sequence generator as described in +<cite>Communications of the ACM</cite>, Vol. 31 (1968), page 742. +A Bays-Durham shuffle is used to guard against regularities +lurking in L'Ecuyer's algorithm; see +<cite>ACM Transactions on Mathematical Software</cite>, Vol. 2 (1976) +pages 59–64 for details. +</p> + +<p> +The JavaScript implementation of the +<a href="http://www.ietf.org/rfc/rfc1321.txt"><b>MD5 message-digest algorithm</b></a> +was developed by Henri Torgemane; please view the source code of this +page to examine the code, including the copyright notice and +conditions of use. The MD5 algorithm was developed by Ron Rivest. +</p> + +<p /> + +<hr /> + +<p /> + +<table class="r"> +<tr><td align="center"> + <a class="i" href="http://validator.w3.org/check?uri=referer"><img + class="button" + src="valid-xhtml10.png" + alt="Valid XHTML 1.0" height="31" width="88" /></a> +</td></tr> +</table> + +<address> +by <a href="/">John Walker</a><br /> +May 26, 1997<br /> + +Updated: November 2006 +</address> + +<p class="centre"> +<em>This document is in the public domain.</em> +</p> +</body> +</html> + diff --git a/build/pgo/js-input/crypto-sha1.html b/build/pgo/js-input/crypto-sha1.html new file mode 100644 index 000000000..01d0b56f3 --- /dev/null +++ b/build/pgo/js-input/crypto-sha1.html @@ -0,0 +1,274 @@ +<!DOCTYPE html> +<head> +<!-- + Copyright (C) 2007 Apple Inc. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--> + +<title>SunSpider crypto-sha1</title> + +</head> + +<body> +<h3>crypto-sha1</h3> +<div id="console"> +</div> + +<script> + +var _sunSpiderStartDate = new Date(); + +/* + * A JavaScript implementation of the Secure Hash Algorithm, SHA-1, as defined + * in FIPS PUB 180-1 + * Version 2.1a Copyright Paul Johnston 2000 - 2002. + * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet + * Distributed under the BSD License + * See http://pajhome.org.uk/crypt/md5 for details. + */ + +/* + * Configurable variables. You may need to tweak these to be compatible with + * the server-side, but the defaults work in most cases. + */ +var hexcase = 0; /* hex output format. 0 - lowercase; 1 - uppercase */ +var b64pad = ""; /* base-64 pad character. "=" for strict RFC compliance */ +var chrsz = 8; /* bits per input character. 8 - ASCII; 16 - Unicode */ + +/* + * These are the functions you'll usually want to call + * They take string arguments and return either hex or base-64 encoded strings + */ +function hex_sha1(s){return binb2hex(core_sha1(str2binb(s),s.length * chrsz));} +function b64_sha1(s){return binb2b64(core_sha1(str2binb(s),s.length * chrsz));} +function str_sha1(s){return binb2str(core_sha1(str2binb(s),s.length * chrsz));} +function hex_hmac_sha1(key, data){ return binb2hex(core_hmac_sha1(key, data));} +function b64_hmac_sha1(key, data){ return binb2b64(core_hmac_sha1(key, data));} +function str_hmac_sha1(key, data){ return binb2str(core_hmac_sha1(key, data));} + +/* + * Perform a simple self-test to see if the VM is working + */ +function sha1_vm_test() +{ + return hex_sha1("abc") == "a9993e364706816aba3e25717850c26c9cd0d89d"; +} + +/* + * Calculate the SHA-1 of an array of big-endian words, and a bit length + */ +function core_sha1(x, len) +{ + /* append padding */ + x[len >> 5] |= 0x80 << (24 - len % 32); + x[((len + 64 >> 9) << 4) + 15] = len; + + var w = Array(80); + var a = 1732584193; + var b = -271733879; + var c = -1732584194; + var d = 271733878; + var e = -1009589776; + + for(var i = 0; i < x.length; i += 16) + { + var olda = a; + var oldb = b; + var oldc = c; + var oldd = d; + var olde = e; + + for(var j = 0; j < 80; j++) + { + if(j < 16) w[j] = x[i + j]; + else w[j] = rol(w[j-3] ^ w[j-8] ^ w[j-14] ^ w[j-16], 1); + var t = safe_add(safe_add(rol(a, 5), sha1_ft(j, b, c, d)), + safe_add(safe_add(e, w[j]), sha1_kt(j))); + e = d; + d = c; + c = rol(b, 30); + b = a; + a = t; + } + + a = safe_add(a, olda); + b = safe_add(b, oldb); + c = safe_add(c, oldc); + d = safe_add(d, oldd); + e = safe_add(e, olde); + } + return Array(a, b, c, d, e); + +} + +/* + * Perform the appropriate triplet combination function for the current + * iteration + */ +function sha1_ft(t, b, c, d) +{ + if(t < 20) return (b & c) | ((~b) & d); + if(t < 40) return b ^ c ^ d; + if(t < 60) return (b & c) | (b & d) | (c & d); + return b ^ c ^ d; +} + +/* + * Determine the appropriate additive constant for the current iteration + */ +function sha1_kt(t) +{ + return (t < 20) ? 1518500249 : (t < 40) ? 1859775393 : + (t < 60) ? -1894007588 : -899497514; +} + +/* + * Calculate the HMAC-SHA1 of a key and some data + */ +function core_hmac_sha1(key, data) +{ + var bkey = str2binb(key); + if(bkey.length > 16) bkey = core_sha1(bkey, key.length * chrsz); + + var ipad = Array(16), opad = Array(16); + for(var i = 0; i < 16; i++) + { + ipad[i] = bkey[i] ^ 0x36363636; + opad[i] = bkey[i] ^ 0x5C5C5C5C; + } + + var hash = core_sha1(ipad.concat(str2binb(data)), 512 + data.length * chrsz); + return core_sha1(opad.concat(hash), 512 + 160); +} + +/* + * Add integers, wrapping at 2^32. This uses 16-bit operations internally + * to work around bugs in some JS interpreters. + */ +function safe_add(x, y) +{ + var lsw = (x & 0xFFFF) + (y & 0xFFFF); + var msw = (x >> 16) + (y >> 16) + (lsw >> 16); + return (msw << 16) | (lsw & 0xFFFF); +} + +/* + * Bitwise rotate a 32-bit number to the left. + */ +function rol(num, cnt) +{ + return (num << cnt) | (num >>> (32 - cnt)); +} + +/* + * Convert an 8-bit or 16-bit string to an array of big-endian words + * In 8-bit function, characters >255 have their hi-byte silently ignored. + */ +function str2binb(str) +{ + var bin = Array(); + var mask = (1 << chrsz) - 1; + for(var i = 0; i < str.length * chrsz; i += chrsz) + bin[i>>5] |= (str.charCodeAt(i / chrsz) & mask) << (32 - chrsz - i%32); + return bin; +} + +/* + * Convert an array of big-endian words to a string + */ +function binb2str(bin) +{ + var str = ""; + var mask = (1 << chrsz) - 1; + for(var i = 0; i < bin.length * 32; i += chrsz) + str += String.fromCharCode((bin[i>>5] >>> (32 - chrsz - i%32)) & mask); + return str; +} + +/* + * Convert an array of big-endian words to a hex string. + */ +function binb2hex(binarray) +{ + var hex_tab = hexcase ? "0123456789ABCDEF" : "0123456789abcdef"; + var str = ""; + for(var i = 0; i < binarray.length * 4; i++) + { + str += hex_tab.charAt((binarray[i>>2] >> ((3 - i%4)*8+4)) & 0xF) + + hex_tab.charAt((binarray[i>>2] >> ((3 - i%4)*8 )) & 0xF); + } + return str; +} + +/* + * Convert an array of big-endian words to a base-64 string + */ +function binb2b64(binarray) +{ + var tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + var str = ""; + for(var i = 0; i < binarray.length * 4; i += 3) + { + var triplet = (((binarray[i >> 2] >> 8 * (3 - i %4)) & 0xFF) << 16) + | (((binarray[i+1 >> 2] >> 8 * (3 - (i+1)%4)) & 0xFF) << 8 ) + | ((binarray[i+2 >> 2] >> 8 * (3 - (i+2)%4)) & 0xFF); + for(var j = 0; j < 4; j++) + { + if(i * 8 + j * 6 > binarray.length * 32) str += b64pad; + else str += tab.charAt((triplet >> 6*(3-j)) & 0x3F); + } + } + return str; +} + + +var plainText = "Two households, both alike in dignity,\n\ +In fair Verona, where we lay our scene,\n\ +From ancient grudge break to new mutiny,\n\ +Where civil blood makes civil hands unclean.\n\ +From forth the fatal loins of these two foes\n\ +A pair of star-cross'd lovers take their life;\n\ +Whole misadventured piteous overthrows\n\ +Do with their death bury their parents' strife.\n\ +The fearful passage of their death-mark'd love,\n\ +And the continuance of their parents' rage,\n\ +Which, but their children's end, nought could remove,\n\ +Is now the two hours' traffic of our stage;\n\ +The which if you with patient ears attend,\n\ +What here shall miss, our toil shall strive to mend."; + +for (var i = 0; i <4; i++) { + plainText += plainText; +} + +var sha1Output = hex_sha1(plainText); + + +var _sunSpiderInterval = new Date() - _sunSpiderStartDate; + +document.getElementById("console").innerHTML = _sunSpiderInterval; +</script> + + +</body> +</html> diff --git a/build/pgo/js-input/date-format-tofte.html b/build/pgo/js-input/date-format-tofte.html new file mode 100644 index 000000000..b8e477342 --- /dev/null +++ b/build/pgo/js-input/date-format-tofte.html @@ -0,0 +1,349 @@ +<!DOCTYPE html> +<head> +<!-- + Copyright (C) 2007 Apple Inc. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--> + +<title>SunSpider date-format-tofte</title> + +</head> + +<body> +<h3>date-format-tofte</h3> +<div id="console"> +</div> + +<script> + +var _sunSpiderStartDate = new Date(); + +function arrayExists(array, x) { + for (var i = 0; i < array.length; i++) { + if (array[i] == x) return true; + } + return false; +} + +Date.prototype.formatDate = function (input,time) { + // formatDate : + // a PHP date like function, for formatting date strings + // See: http://www.php.net/date + // + // input : format string + // time : epoch time (seconds, and optional) + // + // if time is not passed, formatting is based on + // the current "this" date object's set time. + // + // supported: + // a, A, B, d, D, F, g, G, h, H, i, j, l (lowercase L), L, + // m, M, n, O, r, s, S, t, U, w, W, y, Y, z + // + // unsupported: + // I (capital i), T, Z + + var switches = ["a", "A", "B", "d", "D", "F", "g", "G", "h", "H", + "i", "j", "l", "L", "m", "M", "n", "O", "r", "s", + "S", "t", "U", "w", "W", "y", "Y", "z"]; + var daysLong = ["Sunday", "Monday", "Tuesday", "Wednesday", + "Thursday", "Friday", "Saturday"]; + var daysShort = ["Sun", "Mon", "Tue", "Wed", + "Thu", "Fri", "Sat"]; + var monthsShort = ["Jan", "Feb", "Mar", "Apr", + "May", "Jun", "Jul", "Aug", "Sep", + "Oct", "Nov", "Dec"]; + var monthsLong = ["January", "February", "March", "April", + "May", "June", "July", "August", "September", + "October", "November", "December"]; + var daysSuffix = ["st", "nd", "rd", "th", "th", "th", "th", // 1st - 7th + "th", "th", "th", "th", "th", "th", "th", // 8th - 14th + "th", "th", "th", "th", "th", "th", "st", // 15th - 21st + "nd", "rd", "th", "th", "th", "th", "th", // 22nd - 28th + "th", "th", "st"]; // 29th - 31st + + function a() { + // Lowercase Ante meridiem and Post meridiem + return self.getHours() > 11? "pm" : "am"; + } + function A() { + // Uppercase Ante meridiem and Post meridiem + return self.getHours() > 11? "PM" : "AM"; + } + + function B(){ + // Swatch internet time. code simply grabbed from ppk, + // since I was feeling lazy: + // http://www.xs4all.nl/~ppk/js/beat.html + var off = (self.getTimezoneOffset() + 60)*60; + var theSeconds = (self.getHours() * 3600) + + (self.getMinutes() * 60) + + self.getSeconds() + off; + var beat = Math.floor(theSeconds/86.4); + if (beat > 1000) beat -= 1000; + if (beat < 0) beat += 1000; + if ((""+beat).length == 1) beat = "00"+beat; + if ((""+beat).length == 2) beat = "0"+beat; + return beat; + } + + function d() { + // Day of the month, 2 digits with leading zeros + return new String(self.getDate()).length == 1? + "0"+self.getDate() : self.getDate(); + } + function D() { + // A textual representation of a day, three letters + return daysShort[self.getDay()]; + } + function F() { + // A full textual representation of a month + return monthsLong[self.getMonth()]; + } + function g() { + // 12-hour format of an hour without leading zeros + return self.getHours() > 12? self.getHours()-12 : self.getHours(); + } + function G() { + // 24-hour format of an hour without leading zeros + return self.getHours(); + } + function h() { + // 12-hour format of an hour with leading zeros + if (self.getHours() > 12) { + var s = new String(self.getHours()-12); + return s.length == 1? + "0"+ (self.getHours()-12) : self.getHours()-12; + } else { + var s = new String(self.getHours()); + return s.length == 1? + "0"+self.getHours() : self.getHours(); + } + } + function H() { + // 24-hour format of an hour with leading zeros + return new String(self.getHours()).length == 1? + "0"+self.getHours() : self.getHours(); + } + function i() { + // Minutes with leading zeros + return new String(self.getMinutes()).length == 1? + "0"+self.getMinutes() : self.getMinutes(); + } + function j() { + // Day of the month without leading zeros + return self.getDate(); + } + function l() { + // A full textual representation of the day of the week + return daysLong[self.getDay()]; + } + function L() { + // leap year or not. 1 if leap year, 0 if not. + // the logic should match iso's 8601 standard. + var y_ = Y(); + if ( + (y_ % 4 == 0 && y_ % 100 != 0) || + (y_ % 4 == 0 && y_ % 100 == 0 && y_ % 400 == 0) + ) { + return 1; + } else { + return 0; + } + } + function m() { + // Numeric representation of a month, with leading zeros + return self.getMonth() < 9? + "0"+(self.getMonth()+1) : + self.getMonth()+1; + } + function M() { + // A short textual representation of a month, three letters + return monthsShort[self.getMonth()]; + } + function n() { + // Numeric representation of a month, without leading zeros + return self.getMonth()+1; + } + function O() { + // Difference to Greenwich time (GMT) in hours + var os = Math.abs(self.getTimezoneOffset()); + var h = ""+Math.floor(os/60); + var m = ""+(os%60); + h.length == 1? h = "0"+h:1; + m.length == 1? m = "0"+m:1; + return self.getTimezoneOffset() < 0 ? "+"+h+m : "-"+h+m; + } + function r() { + // RFC 822 formatted date + var r; // result + // Thu , 21 Dec 2000 + r = D() + ", " + j() + " " + M() + " " + Y() + + // 16 : 01 : 07 +0200 + " " + H() + ":" + i() + ":" + s() + " " + O(); + return r; + } + function S() { + // English ordinal suffix for the day of the month, 2 characters + return daysSuffix[self.getDate()-1]; + } + function s() { + // Seconds, with leading zeros + return new String(self.getSeconds()).length == 1? + "0"+self.getSeconds() : self.getSeconds(); + } + function t() { + + // thanks to Matt Bannon for some much needed code-fixes here! + var daysinmonths = [null,31,28,31,30,31,30,31,31,30,31,30,31]; + if (L()==1 && n()==2) return 29; // leap day + return daysinmonths[n()]; + } + function U() { + // Seconds since the Unix Epoch (January 1 1970 00:00:00 GMT) + return Math.round(self.getTime()/1000); + } + function W() { + // Weeknumber, as per ISO specification: + // http://www.cl.cam.ac.uk/~mgk25/iso-time.html + + // if the day is three days before newyears eve, + // there's a chance it's "week 1" of next year. + // here we check for that. + var beforeNY = 364+L() - z(); + var afterNY = z(); + var weekday = w()!=0?w()-1:6; // makes sunday (0), into 6. + if (beforeNY <= 2 && weekday <= 2-beforeNY) { + return 1; + } + // similarly, if the day is within threedays of newyears + // there's a chance it belongs in the old year. + var ny = new Date("January 1 " + Y() + " 00:00:00"); + var nyDay = ny.getDay()!=0?ny.getDay()-1:6; + if ( + (afterNY <= 2) && + (nyDay >=4) && + (afterNY >= (6-nyDay)) + ) { + // Since I'm not sure we can just always return 53, + // i call the function here again, using the last day + // of the previous year, as the date, and then just + // return that week. + var prevNY = new Date("December 31 " + (Y()-1) + " 00:00:00"); + return prevNY.formatDate("W"); + } + + // week 1, is the week that has the first thursday in it. + // note that this value is not zero index. + if (nyDay <= 3) { + // first day of the year fell on a thursday, or earlier. + return 1 + Math.floor( ( z() + nyDay ) / 7 ); + } else { + // first day of the year fell on a friday, or later. + return 1 + Math.floor( ( z() - ( 7 - nyDay ) ) / 7 ); + } + } + function w() { + // Numeric representation of the day of the week + return self.getDay(); + } + + function Y() { + // A full numeric representation of a year, 4 digits + + // we first check, if getFullYear is supported. if it + // is, we just use that. ppks code is nice, but wont + // work with dates outside 1900-2038, or something like that + if (self.getFullYear) { + var newDate = new Date("January 1 2001 00:00:00 +0000"); + var x = newDate .getFullYear(); + if (x == 2001) { + // i trust the method now + return self.getFullYear(); + } + } + // else, do this: + // codes thanks to ppk: + // http://www.xs4all.nl/~ppk/js/introdate.html + var x = self.getYear(); + var y = x % 100; + y += (y < 38) ? 2000 : 1900; + return y; + } + function y() { + // A two-digit representation of a year + var y = Y()+""; + return y.substring(y.length-2,y.length); + } + function z() { + // The day of the year, zero indexed! 0 through 366 + var t = new Date("January 1 " + Y() + " 00:00:00"); + var diff = self.getTime() - t.getTime(); + return Math.floor(diff/1000/60/60/24); + } + + var self = this; + if (time) { + // save time + var prevTime = self.getTime(); + self.setTime(time); + } + + var ia = input.split(""); + var ij = 0; + while (ia[ij]) { + if (ia[ij] == "\\") { + // this is our way of allowing users to escape stuff + ia.splice(ij,1); + } else { + if (arrayExists(switches,ia[ij])) { + ia[ij] = eval(ia[ij] + "()"); + } + } + ij++; + } + // reset time, back to what it was + if (prevTime) { + self.setTime(prevTime); + } + return ia.join(""); +} + +var date = new Date("1/1/2007 1:11:11"); + +for (i = 0; i < 500; ++i) { + var shortFormat = date.formatDate("Y-m-d"); + var longFormat = date.formatDate("l, F d, Y g:i:s A"); + date.setTime(date.getTime() + 84266956); +} + + + +var _sunSpiderInterval = new Date() - _sunSpiderStartDate; + +document.getElementById("console").innerHTML = _sunSpiderInterval; +</script> + + +</body> +</html> diff --git a/build/pgo/js-input/date-format-xparb.html b/build/pgo/js-input/date-format-xparb.html new file mode 100644 index 000000000..dd35713d1 --- /dev/null +++ b/build/pgo/js-input/date-format-xparb.html @@ -0,0 +1,467 @@ +<!DOCTYPE html> +<head> +<!-- + Copyright (C) 2007 Apple Inc. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--> + +<title>SunSpider date-format-xparb</title> + +</head> + +<body> +<h3>date-format-xparb</h3> +<div id="console"> +</div> + +<script> + +var _sunSpiderStartDate = new Date(); + +/* + * Copyright (C) 2004 Baron Schwartz <baron at sequent dot org> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, version 2.1. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more + * details. + */ + +Date.parseFunctions = {count:0}; +Date.parseRegexes = []; +Date.formatFunctions = {count:0}; + +Date.prototype.dateFormat = function(format) { + if (Date.formatFunctions[format] == null) { + Date.createNewFormat(format); + } + var func = Date.formatFunctions[format]; + return this[func](); +} + +Date.createNewFormat = function(format) { + var funcName = "format" + Date.formatFunctions.count++; + Date.formatFunctions[format] = funcName; + var code = "Date.prototype." + funcName + " = function(){return "; + var special = false; + var ch = ''; + for (var i = 0; i < format.length; ++i) { + ch = format.charAt(i); + if (!special && ch == "\\") { + special = true; + } + else if (special) { + special = false; + code += "'" + String.escape(ch) + "' + "; + } + else { + code += Date.getFormatCode(ch); + } + } + eval(code.substring(0, code.length - 3) + ";}"); +} + +Date.getFormatCode = function(character) { + switch (character) { + case "d": + return "String.leftPad(this.getDate(), 2, '0') + "; + case "D": + return "Date.dayNames[this.getDay()].substring(0, 3) + "; + case "j": + return "this.getDate() + "; + case "l": + return "Date.dayNames[this.getDay()] + "; + case "S": + return "this.getSuffix() + "; + case "w": + return "this.getDay() + "; + case "z": + return "this.getDayOfYear() + "; + case "W": + return "this.getWeekOfYear() + "; + case "F": + return "Date.monthNames[this.getMonth()] + "; + case "m": + return "String.leftPad(this.getMonth() + 1, 2, '0') + "; + case "M": + return "Date.monthNames[this.getMonth()].substring(0, 3) + "; + case "n": + return "(this.getMonth() + 1) + "; + case "t": + return "this.getDaysInMonth() + "; + case "L": + return "(this.isLeapYear() ? 1 : 0) + "; + case "Y": + return "this.getFullYear() + "; + case "y": + return "('' + this.getFullYear()).substring(2, 4) + "; + case "a": + return "(this.getHours() < 12 ? 'am' : 'pm') + "; + case "A": + return "(this.getHours() < 12 ? 'AM' : 'PM') + "; + case "g": + return "((this.getHours() %12) ? this.getHours() % 12 : 12) + "; + case "G": + return "this.getHours() + "; + case "h": + return "String.leftPad((this.getHours() %12) ? this.getHours() % 12 : 12, 2, '0') + "; + case "H": + return "String.leftPad(this.getHours(), 2, '0') + "; + case "i": + return "String.leftPad(this.getMinutes(), 2, '0') + "; + case "s": + return "String.leftPad(this.getSeconds(), 2, '0') + "; + case "O": + return "this.getGMTOffset() + "; + case "T": + return "this.getTimezone() + "; + case "Z": + return "(this.getTimezoneOffset() * -60) + "; + default: + return "'" + String.escape(character) + "' + "; + } +} + +Date.parseDate = function(input, format) { + if (Date.parseFunctions[format] == null) { + Date.createParser(format); + } + var func = Date.parseFunctions[format]; + return Date[func](input); +} + +Date.createParser = function(format) { + var funcName = "parse" + Date.parseFunctions.count++; + var regexNum = Date.parseRegexes.length; + var currentGroup = 1; + Date.parseFunctions[format] = funcName; + + var code = "Date." + funcName + " = function(input){\n" + + "var y = -1, m = -1, d = -1, h = -1, i = -1, s = -1;\n" + + "var d = new Date();\n" + + "y = d.getFullYear();\n" + + "m = d.getMonth();\n" + + "d = d.getDate();\n" + + "var results = input.match(Date.parseRegexes[" + regexNum + "]);\n" + + "if (results && results.length > 0) {" + var regex = ""; + + var special = false; + var ch = ''; + for (var i = 0; i < format.length; ++i) { + ch = format.charAt(i); + if (!special && ch == "\\") { + special = true; + } + else if (special) { + special = false; + regex += String.escape(ch); + } + else { + obj = Date.formatCodeToRegex(ch, currentGroup); + currentGroup += obj.g; + regex += obj.s; + if (obj.g && obj.c) { + code += obj.c; + } + } + } + + code += "if (y > 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0)\n" + + "{return new Date(y, m, d, h, i, s);}\n" + + "else if (y > 0 && m >= 0 && d > 0 && h >= 0 && i >= 0)\n" + + "{return new Date(y, m, d, h, i);}\n" + + "else if (y > 0 && m >= 0 && d > 0 && h >= 0)\n" + + "{return new Date(y, m, d, h);}\n" + + "else if (y > 0 && m >= 0 && d > 0)\n" + + "{return new Date(y, m, d);}\n" + + "else if (y > 0 && m >= 0)\n" + + "{return new Date(y, m);}\n" + + "else if (y > 0)\n" + + "{return new Date(y);}\n" + + "}return null;}"; + + Date.parseRegexes[regexNum] = new RegExp("^" + regex + "$"); + eval(code); +} + +Date.formatCodeToRegex = function(character, currentGroup) { + switch (character) { + case "D": + return {g:0, + c:null, + s:"(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)"}; + case "j": + case "d": + return {g:1, + c:"d = parseInt(results[" + currentGroup + "], 10);\n", + s:"(\\d{1,2})"}; + case "l": + return {g:0, + c:null, + s:"(?:" + Date.dayNames.join("|") + ")"}; + case "S": + return {g:0, + c:null, + s:"(?:st|nd|rd|th)"}; + case "w": + return {g:0, + c:null, + s:"\\d"}; + case "z": + return {g:0, + c:null, + s:"(?:\\d{1,3})"}; + case "W": + return {g:0, + c:null, + s:"(?:\\d{2})"}; + case "F": + return {g:1, + c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "].substring(0, 3)], 10);\n", + s:"(" + Date.monthNames.join("|") + ")"}; + case "M": + return {g:1, + c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "]], 10);\n", + s:"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)"}; + case "n": + case "m": + return {g:1, + c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n", + s:"(\\d{1,2})"}; + case "t": + return {g:0, + c:null, + s:"\\d{1,2}"}; + case "L": + return {g:0, + c:null, + s:"(?:1|0)"}; + case "Y": + return {g:1, + c:"y = parseInt(results[" + currentGroup + "], 10);\n", + s:"(\\d{4})"}; + case "y": + return {g:1, + c:"var ty = parseInt(results[" + currentGroup + "], 10);\n" + + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n", + s:"(\\d{1,2})"}; + case "a": + return {g:1, + c:"if (results[" + currentGroup + "] == 'am') {\n" + + "if (h == 12) { h = 0; }\n" + + "} else { if (h < 12) { h += 12; }}", + s:"(am|pm)"}; + case "A": + return {g:1, + c:"if (results[" + currentGroup + "] == 'AM') {\n" + + "if (h == 12) { h = 0; }\n" + + "} else { if (h < 12) { h += 12; }}", + s:"(AM|PM)"}; + case "g": + case "G": + case "h": + case "H": + return {g:1, + c:"h = parseInt(results[" + currentGroup + "], 10);\n", + s:"(\\d{1,2})"}; + case "i": + return {g:1, + c:"i = parseInt(results[" + currentGroup + "], 10);\n", + s:"(\\d{2})"}; + case "s": + return {g:1, + c:"s = parseInt(results[" + currentGroup + "], 10);\n", + s:"(\\d{2})"}; + case "O": + return {g:0, + c:null, + s:"[+-]\\d{4}"}; + case "T": + return {g:0, + c:null, + s:"[A-Z]{3}"}; + case "Z": + return {g:0, + c:null, + s:"[+-]\\d{1,5}"}; + default: + return {g:0, + c:null, + s:String.escape(character)}; + } +} + +Date.prototype.getTimezone = function() { + return this.toString().replace( + /^.*? ([A-Z]{3}) [0-9]{4}.*$/, "$1").replace( + /^.*?\(([A-Z])[a-z]+ ([A-Z])[a-z]+ ([A-Z])[a-z]+\)$/, "$1$2$3"); +} + +Date.prototype.getGMTOffset = function() { + return (this.getTimezoneOffset() > 0 ? "-" : "+") + + String.leftPad(Math.floor(this.getTimezoneOffset() / 60), 2, "0") + + String.leftPad(this.getTimezoneOffset() % 60, 2, "0"); +} + +Date.prototype.getDayOfYear = function() { + var num = 0; + Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28; + for (var i = 0; i < this.getMonth(); ++i) { + num += Date.daysInMonth[i]; + } + return num + this.getDate() - 1; +} + +Date.prototype.getWeekOfYear = function() { + // Skip to Thursday of this week + var now = this.getDayOfYear() + (4 - this.getDay()); + // Find the first Thursday of the year + var jan1 = new Date(this.getFullYear(), 0, 1); + var then = (7 - jan1.getDay() + 4); + document.write(then); + return String.leftPad(((now - then) / 7) + 1, 2, "0"); +} + +Date.prototype.isLeapYear = function() { + var year = this.getFullYear(); + return ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year))); +} + +Date.prototype.getFirstDayOfMonth = function() { + var day = (this.getDay() - (this.getDate() - 1)) % 7; + return (day < 0) ? (day + 7) : day; +} + +Date.prototype.getLastDayOfMonth = function() { + var day = (this.getDay() + (Date.daysInMonth[this.getMonth()] - this.getDate())) % 7; + return (day < 0) ? (day + 7) : day; +} + +Date.prototype.getDaysInMonth = function() { + Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28; + return Date.daysInMonth[this.getMonth()]; +} + +Date.prototype.getSuffix = function() { + switch (this.getDate()) { + case 1: + case 21: + case 31: + return "st"; + case 2: + case 22: + return "nd"; + case 3: + case 23: + return "rd"; + default: + return "th"; + } +} + +String.escape = function(string) { + return string.replace(/('|\\)/g, "\\$1"); +} + +String.leftPad = function (val, size, ch) { + var result = new String(val); + if (ch == null) { + ch = " "; + } + while (result.length < size) { + result = ch + result; + } + return result; +} + +Date.daysInMonth = [31,28,31,30,31,30,31,31,30,31,30,31]; +Date.monthNames = + ["January", + "February", + "March", + "April", + "May", + "June", + "July", + "August", + "September", + "October", + "November", + "December"]; +Date.dayNames = + ["Sunday", + "Monday", + "Tuesday", + "Wednesday", + "Thursday", + "Friday", + "Saturday"]; +Date.y2kYear = 50; +Date.monthNumbers = { + Jan:0, + Feb:1, + Mar:2, + Apr:3, + May:4, + Jun:5, + Jul:6, + Aug:7, + Sep:8, + Oct:9, + Nov:10, + Dec:11}; +Date.patterns = { + ISO8601LongPattern:"Y-m-d H:i:s", + ISO8601ShortPattern:"Y-m-d", + ShortDatePattern: "n/j/Y", + LongDatePattern: "l, F d, Y", + FullDateTimePattern: "l, F d, Y g:i:s A", + MonthDayPattern: "F d", + ShortTimePattern: "g:i A", + LongTimePattern: "g:i:s A", + SortableDateTimePattern: "Y-m-d\\TH:i:s", + UniversalSortableDateTimePattern: "Y-m-d H:i:sO", + YearMonthPattern: "F, Y"}; + +var date = new Date("1/1/2007 1:11:11"); + +for (i = 0; i < 4000; ++i) { + var shortFormat = date.dateFormat("Y-m-d"); + var longFormat = date.dateFormat("l, F d, Y g:i:s A"); + date.setTime(date.getTime() + 84266956); +} + + +var _sunSpiderInterval = new Date() - _sunSpiderStartDate; + +document.getElementById("console").innerHTML = _sunSpiderInterval; +</script> + + +</body> +</html> diff --git a/build/pgo/js-input/key.gif b/build/pgo/js-input/key.gif Binary files differnew file mode 100644 index 000000000..050311fc6 --- /dev/null +++ b/build/pgo/js-input/key.gif diff --git a/build/pgo/js-input/math-cordic.html b/build/pgo/js-input/math-cordic.html new file mode 100644 index 000000000..ec28f9ddd --- /dev/null +++ b/build/pgo/js-input/math-cordic.html @@ -0,0 +1,145 @@ +<!DOCTYPE html> +<head> +<!-- + Copyright (C) 2007 Apple Inc. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--> + +<title>SunSpider math-cordic</title> + +</head> + +<body> +<h3>math-cordic</h3> +<div id="console"> +</div> + +<script> + +var _sunSpiderStartDate = new Date(); + +/* + * Copyright (C) Rich Moore. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/////. Start CORDIC + +var AG_CONST = 0.6072529350; + +function FIXED(X) +{ + return X * 65536.0; +} + +function FLOAT(X) +{ + return X / 65536.0; +} + +function DEG2RAD(X) +{ + return 0.017453 * (X); +} + +var Angles = [ + FIXED(45.0), FIXED(26.565), FIXED(14.0362), FIXED(7.12502), + FIXED(3.57633), FIXED(1.78991), FIXED(0.895174), FIXED(0.447614), + FIXED(0.223811), FIXED(0.111906), FIXED(0.055953), + FIXED(0.027977) + ]; + + +function cordicsincos() { + var X; + var Y; + var TargetAngle; + var CurrAngle; + var Step; + + X = FIXED(AG_CONST); /* AG_CONST * cos(0) */ + Y = 0; /* AG_CONST * sin(0) */ + + TargetAngle = FIXED(28.027); + CurrAngle = 0; + for (Step = 0; Step < 12; Step++) { + var NewX; + if (TargetAngle > CurrAngle) { + NewX = X - (Y >> Step); + Y = (X >> Step) + Y; + X = NewX; + CurrAngle += Angles[Step]; + } else { + NewX = X + (Y >> Step); + Y = -(X >> Step) + Y; + X = NewX; + CurrAngle -= Angles[Step]; + } + } +} + +///// End CORDIC + +function cordic( runs ) { + var start = new Date(); + + for ( var i = 0 ; i < runs ; i++ ) { + cordicsincos(); + } + + var end = new Date(); + + return end.getTime() - start.getTime(); +} + +cordic(25000); + + +var _sunSpiderInterval = new Date() - _sunSpiderStartDate; + +document.getElementById("console").innerHTML = _sunSpiderInterval; +</script> + + +</body> +</html> diff --git a/build/pgo/js-input/math-partial-sums.html b/build/pgo/js-input/math-partial-sums.html new file mode 100644 index 000000000..b78b96248 --- /dev/null +++ b/build/pgo/js-input/math-partial-sums.html @@ -0,0 +1,83 @@ +<!DOCTYPE html> +<head> +<!-- + Copyright (C) 2007 Apple Inc. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--> + +<title>SunSpider math-partial-sums</title> + +</head> + +<body> +<h3>math-partial-sums</h3> +<div id="console"> +</div> + +<script> + +var _sunSpiderStartDate = new Date(); + +// The Computer Language Shootout +// http://shootout.alioth.debian.org/ +// contributed by Isaac Gouy + +function partial(n){ + var a1 = a2 = a3 = a4 = a5 = a6 = a7 = a8 = a9 = 0.0; + var twothirds = 2.0/3.0; + var alt = -1.0; + var k2 = k3 = sk = ck = 0.0; + + for (var k = 1; k <= n; k++){ + k2 = k*k; + k3 = k2*k; + sk = Math.sin(k); + ck = Math.cos(k); + alt = -alt; + + a1 += Math.pow(twothirds,k-1); + a2 += Math.pow(k,-0.5); + a3 += 1.0/(k*(k+1.0)); + a4 += 1.0/(k3 * sk*sk); + a5 += 1.0/(k3 * ck*ck); + a6 += 1.0/k; + a7 += 1.0/k2; + a8 += alt/k; + a9 += alt/(2*k -1); + } +} + +for (var i = 1024; i <= 16384; i *= 2) { + partial(i); +} + + + +var _sunSpiderInterval = new Date() - _sunSpiderStartDate; + +document.getElementById("console").innerHTML = _sunSpiderInterval; +</script> + + +</body> +</html> diff --git a/build/pgo/js-input/math-spectral-norm.html b/build/pgo/js-input/math-spectral-norm.html new file mode 100644 index 000000000..2949f9d78 --- /dev/null +++ b/build/pgo/js-input/math-spectral-norm.html @@ -0,0 +1,101 @@ +<!DOCTYPE html> +<head> +<!-- + Copyright (C) 2007 Apple Inc. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--> + +<title>SunSpider math-spectral-norm</title> + +</head> + +<body> +<h3>math-spectral-norm</h3> +<div id="console"> +</div> + +<script> + +var _sunSpiderStartDate = new Date(); + +// The Great Computer Language Shootout +// http://shootout.alioth.debian.org/ +// +// contributed by Ian Osgood + +function A(i,j) { + return 1/((i+j)*(i+j+1)/2+i+1); +} + +function Au(u,v) { + for (var i=0; i<u.length; ++i) { + var t = 0; + for (var j=0; j<u.length; ++j) + t += A(i,j) * u[j]; + v[i] = t; + } +} + +function Atu(u,v) { + for (var i=0; i<u.length; ++i) { + var t = 0; + for (var j=0; j<u.length; ++j) + t += A(j,i) * u[j]; + v[i] = t; + } +} + +function AtAu(u,v,w) { + Au(u,w); + Atu(w,v); +} + +function spectralnorm(n) { + var i, u=[], v=[], w=[], vv=0, vBv=0; + for (i=0; i<n; ++i) { + u[i] = 1; v[i] = w[i] = 0; + } + for (i=0; i<10; ++i) { + AtAu(u,v,w); + AtAu(v,u,w); + } + for (i=0; i<n; ++i) { + vBv += u[i]*v[i]; + vv += v[i]*v[i]; + } + return Math.sqrt(vBv/vv); +} + +for (var i = 6; i <= 48; i *= 2) { + spectralnorm(i); +} + + +var _sunSpiderInterval = new Date() - _sunSpiderStartDate; + +document.getElementById("console").innerHTML = _sunSpiderInterval; +</script> + + +</body> +</html> diff --git a/build/pgo/js-input/regexp-dna.html b/build/pgo/js-input/regexp-dna.html new file mode 100644 index 000000000..a49021967 --- /dev/null +++ b/build/pgo/js-input/regexp-dna.html @@ -0,0 +1,1762 @@ +<!DOCTYPE html> +<head> +<!-- + Copyright (C) 2007 Apple Inc. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--> + +<title>SunSpider regexp-dna</title> + +</head> + +<body> +<h3>regexp-dna</h3> +<div id="console"> +</div> + +<script> + +var _sunSpiderStartDate = new Date(); + +// The Computer Language Shootout +// http://shootout.alioth.debian.org/ +// +// contributed by Jesse Millikan +// Base on the Ruby version by jose fco. gonzalez + +var l; +var dnaInput = ">ONE Homo sapiens alu\n\ +GGCCGGGCGCGGTGGCTCACGCCTGTAATCCCAGCACTTTGGGAGGCCGAGGCGGGCGGA\n\ +TCACCTGAGGTCAGGAGTTCGAGACCAGCCTGGCCAACATGGTGAAACCCCGTCTCTACT\n\ +AAAAATACAAAAATTAGCCGGGCGTGGTGGCGCGCGCCTGTAATCCCAGCTACTCGGGAG\n\ +GCTGAGGCAGGAGAATCGCTTGAACCCGGGAGGCGGAGGTTGCAGTGAGCCGAGATCGCG\n\ +CCACTGCACTCCAGCCTGGGCGACAGAGCGAGACTCCGTCTCAAAAAGGCCGGGCGCGGT\n\ +GGCTCACGCCTGTAATCCCAGCACTTTGGGAGGCCGAGGCGGGCGGATCACCTGAGGTCA\n\ +GGAGTTCGAGACCAGCCTGGCCAACATGGTGAAACCCCGTCTCTACTAAAAATACAAAAA\n\ +TTAGCCGGGCGTGGTGGCGCGCGCCTGTAATCCCAGCTACTCGGGAGGCTGAGGCAGGAG\n\ +AATCGCTTGAACCCGGGAGGCGGAGGTTGCAGTGAGCCGAGATCGCGCCACTGCACTCCA\n\ +GCCTGGGCGACAGAGCGAGACTCCGTCTCAAAAAGGCCGGGCGCGGTGGCTCACGCCTGT\n\ +AATCCCAGCACTTTGGGAGGCCGAGGCGGGCGGATCACCTGAGGTCAGGAGTTCGAGACC\n\ +AGCCTGGCCAACATGGTGAAACCCCGTCTCTACTAAAAATACAAAAATTAGCCGGGCGTG\n\ +GTGGCGCGCGCCTGTAATCCCAGCTACTCGGGAGGCTGAGGCAGGAGAATCGCTTGAACC\n\ +CGGGAGGCGGAGGTTGCAGTGAGCCGAGATCGCGCCACTGCACTCCAGCCTGGGCGACAG\n\ +AGCGAGACTCCGTCTCAAAAAGGCCGGGCGCGGTGGCTCACGCCTGTAATCCCAGCACTT\n\ +TGGGAGGCCGAGGCGGGCGGATCACCTGAGGTCAGGAGTTCGAGACCAGCCTGGCCAACA\n\ +TGGTGAAACCCCGTCTCTACTAAAAATACAAAAATTAGCCGGGCGTGGTGGCGCGCGCCT\n\ +GTAATCCCAGCTACTCGGGAGGCTGAGGCAGGAGAATCGCTTGAACCCGGGAGGCGGAGG\n\ +TTGCAGTGAGCCGAGATCGCGCCACTGCACTCCAGCCTGGGCGACAGAGCGAGACTCCGT\n\ +CTCAAAAAGGCCGGGCGCGGTGGCTCACGCCTGTAATCCCAGCACTTTGGGAGGCCGAGG\n\ +CGGGCGGATCACCTGAGGTCAGGAGTTCGAGACCAGCCTGGCCAACATGGTGAAACCCCG\n\ +TCTCTACTAAAAATACAAAAATTAGCCGGGCGTGGTGGCGCGCGCCTGTAATCCCAGCTA\n\ +CTCGGGAGGCTGAGGCAGGAGAATCGCTTGAACCCGGGAGGCGGAGGTTGCAGTGAGCCG\n\ +AGATCGCGCCACTGCACTCCAGCCTGGGCGACAGAGCGAGACTCCGTCTCAAAAAGGCCG\n\ +GGCGCGGTGGCTCACGCCTGTAATCCCAGCACTTTGGGAGGCCGAGGCGGGCGGATCACC\n\ +TGAGGTCAGGAGTTCGAGACCAGCCTGGCCAACATGGTGAAACCCCGTCTCTACTAAAAA\n\ +TACAAAAATTAGCCGGGCGTGGTGGCGCGCGCCTGTAATCCCAGCTACTCGGGAGGCTGA\n\ +GGCAGGAGAATCGCTTGAACCCGGGAGGCGGAGGTTGCAGTGAGCCGAGATCGCGCCACT\n\ +GCACTCCAGCCTGGGCGACAGAGCGAGACTCCGTCTCAAAAAGGCCGGGCGCGGTGGCTC\n\ +ACGCCTGTAATCCCAGCACTTTGGGAGGCCGAGGCGGGCGGATCACCTGAGGTCAGGAGT\n\ +TCGAGACCAGCCTGGCCAACATGGTGAAACCCCGTCTCTACTAAAAATACAAAAATTAGC\n\ +CGGGCGTGGTGGCGCGCGCCTGTAATCCCAGCTACTCGGGAGGCTGAGGCAGGAGAATCG\n\ +CTTGAACCCGGGAGGCGGAGGTTGCAGTGAGCCGAGATCGCGCCACTGCACTCCAGCCTG\n\ +GGCGACAGAGCGAGACTCCGTCTCAAAAAGGCCGGGCGCGGTGGCTCACGCCTGTAATCC\n\ +CAGCACTTTGGGAGGCCGAGGCGGGCGGATCACCTGAGGTCAGGAGTTCGAGACCAGCCT\n\ +GGCCAACATGGTGAAACCCCGTCTCTACTAAAAATACAAAAATTAGCCGGGCGTGGTGGC\n\ +GCGCGCCTGTAATCCCAGCTACTCGGGAGGCTGAGGCAGGAGAATCGCTTGAACCCGGGA\n\ +GGCGGAGGTTGCAGTGAGCCGAGATCGCGCCACTGCACTCCAGCCTGGGCGACAGAGCGA\n\ +GACTCCGTCTCAAAAAGGCCGGGCGCGGTGGCTCACGCCTGTAATCCCAGCACTTTGGGA\n\ +GGCCGAGGCGGGCGGATCACCTGAGGTCAGGAGTTCGAGACCAGCCTGGCCAACATGGTG\n\ +AAACCCCGTCTCTACTAAAAATACAAAAATTAGCCGGGCGTGGTGGCGCGCGCCTGTAAT\n\ +CCCAGCTACTCGGGAGGCTGAGGCAGGAGAATCGCTTGAACCCGGGAGGCGGAGGTTGCA\n\ +GTGAGCCGAGATCGCGCCACTGCACTCCAGCCTGGGCGACAGAGCGAGACTCCGTCTCAA\n\ +AAAGGCCGGGCGCGGTGGCTCACGCCTGTAATCCCAGCACTTTGGGAGGCCGAGGCGGGC\n\ +GGATCACCTGAGGTCAGGAGTTCGAGACCAGCCTGGCCAACATGGTGAAACCCCGTCTCT\n\ +ACTAAAAATACAAAAATTAGCCGGGCGTGGTGGCGCGCGCCTGTAATCCCAGCTACTCGG\n\ +GAGGCTGAGGCAGGAGAATCGCTTGAACCCGGGAGGCGGAGGTTGCAGTGAGCCGAGATC\n\ +GCGCCACTGCACTCCAGCCTGGGCGACAGAGCGAGACTCCGTCTCAAAAAGGCCGGGCGC\n\ +GGTGGCTCACGCCTGTAATCCCAGCACTTTGGGAGGCCGAGGCGGGCGGATCACCTGAGG\n\ +TCAGGAGTTCGAGACCAGCCTGGCCAACATGGTGAAACCCCGTCTCTACTAAAAATACAA\n\ +AAATTAGCCGGGCGTGGTGGCGCGCGCCTGTAATCCCAGCTACTCGGGAGGCTGAGGCAG\n\ +GAGAATCGCTTGAACCCGGGAGGCGGAGGTTGCAGTGAGCCGAGATCGCGCCACTGCACT\n\ +CCAGCCTGGGCGACAGAGCGAGACTCCGTCTCAAAAAGGCCGGGCGCGGTGGCTCACGCC\n\ +TGTAATCCCAGCACTTTGGGAGGCCGAGGCGGGCGGATCACCTGAGGTCAGGAGTTCGAG\n\ +ACCAGCCTGGCCAACATGGTGAAACCCCGTCTCTACTAAAAATACAAAAATTAGCCGGGC\n\ +GTGGTGGCGCGCGCCTGTAATCCCAGCTACTCGGGAGGCTGAGGCAGGAGAATCGCTTGA\n\ +ACCCGGGAGGCGGAGGTTGCAGTGAGCCGAGATCGCGCCACTGCACTCCAGCCTGGGCGA\n\ +CAGAGCGAGACTCCGTCTCAAAAAGGCCGGGCGCGGTGGCTCACGCCTGTAATCCCAGCA\n\ +CTTTGGGAGGCCGAGGCGGGCGGATCACCTGAGGTCAGGAGTTCGAGACCAGCCTGGCCA\n\ +ACATGGTGAAACCCCGTCTCTACTAAAAATACAAAAATTAGCCGGGCGTGGTGGCGCGCG\n\ +CCTGTAATCCCAGCTACTCGGGAGGCTGAGGCAGGAGAATCGCTTGAACCCGGGAGGCGG\n\ +AGGTTGCAGTGAGCCGAGATCGCGCCACTGCACTCCAGCCTGGGCGACAGAGCGAGACTC\n\ +CGTCTCAAAAAGGCCGGGCGCGGTGGCTCACGCCTGTAATCCCAGCACTTTGGGAGGCCG\n\ +AGGCGGGCGGATCACCTGAGGTCAGGAGTTCGAGACCAGCCTGGCCAACATGGTGAAACC\n\ +CCGTCTCTACTAAAAATACAAAAATTAGCCGGGCGTGGTGGCGCGCGCCTGTAATCCCAG\n\ +CTACTCGGGAGGCTGAGGCAGGAGAATCGCTTGAACCCGGGAGGCGGAGGTTGCAGTGAG\n\ +CCGAGATCGCGCCACTGCACTCCAGCCTGGGCGACAGAGCGAGACTCCGTCTCAAAAAGG\n\ +CCGGGCGCGGTGGCTCACGCCTGTAATCCCAGCACTTTGGGAGGCCGAGGCGGGCGGATC\n\ +ACCTGAGGTCAGGAGTTCGAGACCAGCCTGGCCAACATGGTGAAACCCCGTCTCTACTAA\n\ +AAATACAAAAATTAGCCGGGCGTGGTGGCGCGCGCCTGTAATCCCAGCTACTCGGGAGGC\n\ +TGAGGCAGGAGAATCGCTTGAACCCGGGAGGCGGAGGTTGCAGTGAGCCGAGATCGCGCC\n\ +ACTGCACTCCAGCCTGGGCGACAGAGCGAGACTCCGTCTCAAAAAGGCCGGGCGCGGTGG\n\ +CTCACGCCTGTAATCCCAGCACTTTGGGAGGCCGAGGCGGGCGGATCACCTGAGGTCAGG\n\ +AGTTCGAGACCAGCCTGGCCAACATGGTGAAACCCCGTCTCTACTAAAAATACAAAAATT\n\ +AGCCGGGCGTGGTGGCGCGCGCCTGTAATCCCAGCTACTCGGGAGGCTGAGGCAGGAGAA\n\ +TCGCTTGAACCCGGGAGGCGGAGGTTGCAGTGAGCCGAGATCGCGCCACTGCACTCCAGC\n\ +CTGGGCGACAGAGCGAGACTCCGTCTCAAAAAGGCCGGGCGCGGTGGCTCACGCCTGTAA\n\ +TCCCAGCACTTTGGGAGGCCGAGGCGGGCGGATCACCTGAGGTCAGGAGTTCGAGACCAG\n\ +CCTGGCCAACATGGTGAAACCCCGTCTCTACTAAAAATACAAAAATTAGCCGGGCGTGGT\n\ +GGCGCGCGCCTGTAATCCCAGCTACTCGGGAGGCTGAGGCAGGAGAATCGCTTGAACCCG\n\ +GGAGGCGGAGGTTGCAGTGAGCCGAGATCGCGCCACTGCACTCCAGCCTGGGCGACAGAG\n\ +CGAGACTCCGTCTCAAAAAGGCCGGGCGCGGTGGCTCACGCCTGTAATCCCAGCACTTTG\n\ +GGAGGCCGAGGCGGGCGGATCACCTGAGGTCAGGAGTTCGAGACCAGCCTGGCCAACATG\n\ +GTGAAACCCCGTCTCTACTAAAAATACAAAAATTAGCCGGGCGTGGTGGCGCGCGCCTGT\n\ +AATCCCAGCTACTCGGGAGGCTGAGGCAGGAGAATCGCTTGAACCCGGGAGGCGGAGGTT\n\ +GCAGTGAGCCGAGATCGCGCCACTGCACTCCAGCCTGGGCGACAGAGCGAGACTCCGTCT\n\ +CAAAAAGGCCGGGCGCGGTGGCTCACGCCTGTAATCCCAGCACTTTGGGAGGCCGAGGCG\n\ +GGCGGATCACCTGAGGTCAGGAGTTCGAGACCAGCCTGGCCAACATGGTGAAACCCCGTC\n\ +TCTACTAAAAATACAAAAATTAGCCGGGCGTGGTGGCGCGCGCCTGTAATCCCAGCTACT\n\ +CGGGAGGCTGAGGCAGGAGAATCGCTTGAACCCGGGAGGCGGAGGTTGCAGTGAGCCGAG\n\ +ATCGCGCCACTGCACTCCAGCCTGGGCGACAGAGCGAGACTCCGTCTCAAAAAGGCCGGG\n\ +CGCGGTGGCTCACGCCTGTAATCCCAGCACTTTGGGAGGCCGAGGCGGGCGGATCACCTG\n\ +AGGTCAGGAGTTCGAGACCAGCCTGGCCAACATGGTGAAACCCCGTCTCTACTAAAAATA\n\ +CAAAAATTAGCCGGGCGTGGTGGCGCGCGCCTGTAATCCCAGCTACTCGGGAGGCTGAGG\n\ +CAGGAGAATCGCTTGAACCCGGGAGGCGGAGGTTGCAGTGAGCCGAGATCGCGCCACTGC\n\ +ACTCCAGCCTGGGCGACAGAGCGAGACTCCGTCTCAAAAAGGCCGGGCGCGGTGGCTCAC\n\ +GCCTGTAATCCCAGCACTTTGGGAGGCCGAGGCGGGCGGATCACCTGAGGTCAGGAGTTC\n\ +GAGACCAGCCTGGCCAACATGGTGAAACCCCGTCTCTACTAAAAATACAAAAATTAGCCG\n\ +GGCGTGGTGGCGCGCGCCTGTAATCCCAGCTACTCGGGAGGCTGAGGCAGGAGAATCGCT\n\ +TGAACCCGGGAGGCGGAGGTTGCAGTGAGCCGAGATCGCGCCACTGCACTCCAGCCTGGG\n\ +CGACAGAGCGAGACTCCGTCTCAAAAAGGCCGGGCGCGGTGGCTCACGCCTGTAATCCCA\n\ +GCACTTTGGGAGGCCGAGGCGGGCGGATCACCTGAGGTCAGGAGTTCGAGACCAGCCTGG\n\ +CCAACATGGTGAAACCCCGTCTCTACTAAAAATACAAAAATTAGCCGGGCGTGGTGGCGC\n\ +GCGCCTGTAATCCCAGCTACTCGGGAGGCTGAGGCAGGAGAATCGCTTGAACCCGGGAGG\n\ +CGGAGGTTGCAGTGAGCCGAGATCGCGCCACTGCACTCCAGCCTGGGCGACAGAGCGAGA\n\ +CTCCGTCTCAAAAAGGCCGGGCGCGGTGGCTCACGCCTGTAATCCCAGCACTTTGGGAGG\n\ +CCGAGGCGGGCGGATCACCTGAGGTCAGGAGTTCGAGACCAGCCTGGCCAACATGGTGAA\n\ +ACCCCGTCTCTACTAAAAATACAAAAATTAGCCGGGCGTGGTGGCGCGCGCCTGTAATCC\n\ +CAGCTACTCGGGAGGCTGAGGCAGGAGAATCGCTTGAACCCGGGAGGCGGAGGTTGCAGT\n\ +GAGCCGAGATCGCGCCACTGCACTCCAGCCTGGGCGACAGAGCGAGACTCCGTCTCAAAA\n\ +AGGCCGGGCGCGGTGGCTCACGCCTGTAATCCCAGCACTTTGGGAGGCCGAGGCGGGCGG\n\ +ATCACCTGAGGTCAGGAGTTCGAGACCAGCCTGGCCAACATGGTGAAACCCCGTCTCTAC\n\ +TAAAAATACAAAAATTAGCCGGGCGTGGTGGCGCGCGCCTGTAATCCCAGCTACTCGGGA\n\ +GGCTGAGGCAGGAGAATCGCTTGAACCCGGGAGGCGGAGGTTGCAGTGAGCCGAGATCGC\n\ +GCCACTGCACTCCAGCCTGGGCGACAGAGCGAGACTCCGTCTCAAAAAGGCCGGGCGCGG\n\ +TGGCTCACGCCTGTAATCCCAGCACTTTGGGAGGCCGAGGCGGGCGGATCACCTGAGGTC\n\ +AGGAGTTCGAGACCAGCCTGGCCAACATGGTGAAACCCCGTCTCTACTAAAAATACAAAA\n\ +ATTAGCCGGGCGTGGTGGCGCGCGCCTGTAATCCCAGCTACTCGGGAGGCTGAGGCAGGA\n\ +GAATCGCTTGAACCCGGGAGGCGGAGGTTGCAGTGAGCCGAGATCGCGCCACTGCACTCC\n\ +AGCCTGGGCGACAGAGCGAGACTCCGTCTCAAAAAGGCCGGGCGCGGTGGCTCACGCCTG\n\ +TAATCCCAGCACTTTGGGAGGCCGAGGCGGGCGGATCACCTGAGGTCAGGAGTTCGAGAC\n\ +CAGCCTGGCCAACATGGTGAAACCCCGTCTCTACTAAAAATACAAAAATTAGCCGGGCGT\n\ +GGTGGCGCGCGCCTGTAATCCCAGCTACTCGGGAGGCTGAGGCAGGAGAATCGCTTGAAC\n\ +CCGGGAGGCGGAGGTTGCAGTGAGCCGAGATCGCGCCACTGCACTCCAGCCTGGGCGACA\n\ +GAGCGAGACTCCGTCTCAAAAAGGCCGGGCGCGGTGGCTCACGCCTGTAATCCCAGCACT\n\ +TTGGGAGGCCGAGGCGGGCGGATCACCTGAGGTCAGGAGTTCGAGACCAGCCTGGCCAAC\n\ +ATGGTGAAACCCCGTCTCTACTAAAAATACAAAAATTAGCCGGGCGTGGTGGCGCGCGCC\n\ +TGTAATCCCAGCTACTCGGGAGGCTGAGGCAGGAGAATCGCTTGAACCCGGGAGGCGGAG\n\ +GTTGCAGTGAGCCGAGATCGCGCCACTGCACTCCAGCCTGGGCGACAGAGCGAGACTCCG\n\ +TCTCAAAAAGGCCGGGCGCGGTGGCTCACGCCTGTAATCCCAGCACTTTGGGAGGCCGAG\n\ +GCGGGCGGATCACCTGAGGTCAGGAGTTCGAGACCAGCCTGGCCAACATGGTGAAACCCC\n\ +GTCTCTACTAAAAATACAAAAATTAGCCGGGCGTGGTGGCGCGCGCCTGTAATCCCAGCT\n\ +ACTCGGGAGGCTGAGGCAGGAGAATCGCTTGAACCCGGGAGGCGGAGGTTGCAGTGAGCC\n\ +GAGATCGCGCCACTGCACTCCAGCCTGGGCGACAGAGCGAGACTCCGTCTCAAAAAGGCC\n\ +GGGCGCGGTGGCTCACGCCTGTAATCCCAGCACTTTGGGAGGCCGAGGCGGGCGGATCAC\n\ +CTGAGGTCAGGAGTTCGAGACCAGCCTGGCCAACATGGTGAAACCCCGTCTCTACTAAAA\n\ +ATACAAAAATTAGCCGGGCGTGGTGGCGCGCGCCTGTAATCCCAGCTACTCGGGAGGCTG\n\ +AGGCAGGAGAATCGCTTGAACCCGGGAGGCGGAGGTTGCAGTGAGCCGAGATCGCGCCAC\n\ +TGCACTCCAGCCTGGGCGACAGAGCGAGACTCCGTCTCAAAAAGGCCGGGCGCGGTGGCT\n\ +CACGCCTGTAATCCCAGCACTTTGGGAGGCCGAGGCGGGCGGATCACCTGAGGTCAGGAG\n\ +TTCGAGACCAGCCTGGCCAACATGGTGAAACCCCGTCTCTACTAAAAATACAAAAATTAG\n\ +CCGGGCGTGGTGGCGCGCGCCTGTAATCCCAGCTACTCGGGAGGCTGAGGCAGGAGAATC\n\ +GCTTGAACCCGGGAGGCGGAGGTTGCAGTGAGCCGAGATCGCGCCACTGCACTCCAGCCT\n\ +GGGCGACAGAGCGAGACTCCGTCTCAAAAAGGCCGGGCGCGGTGGCTCACGCCTGTAATC\n\ +CCAGCACTTTGGGAGGCCGAGGCGGGCGGATCACCTGAGGTCAGGAGTTCGAGACCAGCC\n\ +TGGCCAACATGGTGAAACCCCGTCTCTACTAAAAATACAAAAATTAGCCGGGCGTGGTGG\n\ +CGCGCGCCTGTAATCCCAGCTACTCGGGAGGCTGAGGCAGGAGAATCGCTTGAACCCGGG\n\ +AGGCGGAGGTTGCAGTGAGCCGAGATCGCGCCACTGCACTCCAGCCTGGGCGACAGAGCG\n\ +AGACTCCGTCTCAAAAAGGCCGGGCGCGGTGGCTCACGCCTGTAATCCCAGCACTTTGGG\n\ +AGGCCGAGGCGGGCGGATCACCTGAGGTCAGGAGTTCGAGACCAGCCTGGCCAACATGGT\n\ +GAAACCCCGTCTCTACTAAAAATACAAAAATTAGCCGGGCGTGGTGGCGCGCGCCTGTAA\n\ +TCCCAGCTACTCGGGAGGCTGAGGCAGGAGAATCGCTTGAACCCGGGAGGCGGAGGTTGC\n\ +AGTGAGCCGAGATCGCGCCACTGCACTCCAGCCTGGGCGACAGAGCGAGACTCCGTCTCA\n\ +AAAAGGCCGGGCGCGGTGGCTCACGCCTGTAATCCCAGCACTTTGGGAGGCCGAGGCGGG\n\ +CGGATCACCTGAGGTCAGGAGTTCGAGACCAGCCTGGCCAACATGGTGAAACCCCGTCTC\n\ +TACTAAAAATACAAAAATTAGCCGGGCGTGGTGGCGCGCGCCTGTAATCCCAGCTACTCG\n\ +GGAGGCTGAGGCAGGAGAATCGCTTGAACCCGGGAGGCGGAGGTTGCAGTGAGCCGAGAT\n\ +CGCGCCACTGCACTCCAGCCTGGGCGACAGAGCGAGACTCCGTCTCAAAAAGGCCGGGCG\n\ +CGGTGGCTCACGCCTGTAATCCCAGCACTTTGGGAGGCCGAGGCGGGCGGATCACCTGAG\n\ +GTCAGGAGTTCGAGACCAGCCTGGCCAACATGGTGAAACCCCGTCTCTACTAAAAATACA\n\ +AAAATTAGCCGGGCGTGGTGGCGCGCGCCTGTAATCCCAGCTACTCGGGAGGCTGAGGCA\n\ +GGAGAATCGCTTGAACCCGGGAGGCGGAGGTTGCAGTGAGCCGAGATCGCGCCACTGCAC\n\ +TCCAGCCTGGGCGACAGAGCGAGACTCCGTCTCAAAAAGGCCGGGCGCGGTGGCTCACGC\n\ +CTGTAATCCCAGCACTTTGGGAGGCCGAGGCGGGCGGATCACCTGAGGTCAGGAGTTCGA\n\ +GACCAGCCTGGCCAACATGGTGAAACCCCGTCTCTACTAAAAATACAAAAATTAGCCGGG\n\ +CGTGGTGGCGCGCGCCTGTAATCCCAGCTACTCGGGAGGCTGAGGCAGGAGAATCGCTTG\n\ +AACCCGGGAGGCGGAGGTTGCAGTGAGCCGAGATCGCGCCACTGCACTCCAGCCTGGGCG\n\ +ACAGAGCGAGACTCCGTCTCAAAAAGGCCGGGCGCGGTGGCTCACGCCTGTAATCCCAGC\n\ +ACTTTGGGAGGCCGAGGCGGGCGGATCACCTGAGGTCAGGAGTTCGAGACCAGCCTGGCC\n\ +AACATGGTGAAACCCCGTCTCTACTAAAAATACAAAAATTAGCCGGGCGTGGTGGCGCGC\n\ +GCCTGTAATCCCAGCTACTCGGGAGGCTGAGGCAGGAGAATCGCTTGAACCCGGGAGGCG\n\ +GAGGTTGCAGTGAGCCGAGATCGCGCCACTGCACTCCAGCCTGGGCGACAGAGCGAGACT\n\ +CCGTCTCAAAAAGGCCGGGCGCGGTGGCTCACGCCTGTAATCCCAGCACTTTGGGAGGCC\n\ +GAGGCGGGCGGATCACCTGAGGTCAGGAGTTCGAGACCAGCCTGGCCAACATGGTGAAAC\n\ +CCCGTCTCTACTAAAAATACAAAAATTAGCCGGGCGTGGTGGCGCGCGCCTGTAATCCCA\n\ +GCTACTCGGGAGGCTGAGGCAGGAGAATCGCTTGAACCCGGGAGGCGGAGGTTGCAGTGA\n\ +GCCGAGATCGCGCCACTGCACTCCAGCCTGGGCGACAGAGCGAGACTCCGTCTCAAAAAG\n\ +GCCGGGCGCGGTGGCTCACGCCTGTAATCCCAGCACTTTGGGAGGCCGAGGCGGGCGGAT\n\ +CACCTGAGGTCAGGAGTTCGAGACCAGCCTGGCCAACATGGTGAAACCCCGTCTCTACTA\n\ +AAAATACAAAAATTAGCCGGGCGTGGTGGCGCGCGCCTGTAATCCCAGCTACTCGGGAGG\n\ +CTGAGGCAGGAGAATCGCTTGAACCCGGGAGGCGGAGGTTGCAGTGAGCCGAGATCGCGC\n\ +CACTGCACTCCAGCCTGGGCGACAGAGCGAGACTCCGTCTCAAAAAGGCCGGGCGCGGTG\n\ +GCTCACGCCTGTAATCCCAGCACTTTGGGAGGCCGAGGCGGGCGGATCACCTGAGGTCAG\n\ +GAGTTCGAGACCAGCCTGGCCAACATGGTGAAACCCCGTCTCTACTAAAAATACAAAAAT\n\ +TAGCCGGGCGTGGTGGCGCGCGCCTGTAATCCCAGCTACTCGGGAGGCTGAGGCAGGAGA\n\ +ATCGCTTGAACCCGGGAGGCGGAGGTTGCAGTGAGCCGAGATCGCGCCACTGCACTCCAG\n\ +CCTGGGCGACAGAGCGAGACTCCGTCTCAAAAAGGCCGGGCGCGGTGGCTCACGCCTGTA\n\ +ATCCCAGCACTTTGGGAGGCCGAGGCGGGCGGATCACCTGAGGTCAGGAGTTCGAGACCA\n\ +GCCTGGCCAACATGGTGAAACCCCGTCTCTACTAAAAATACAAAAATTAGCCGGGCGTGG\n\ +TGGCGCGCGCCTGTAATCCCAGCTACTCGGGAGGCTGAGGCAGGAGAATCGCTTGAACCC\n\ +GGGAGGCGGAGGTTGCAGTGAGCCGAGATCGCGCCACTGCACTCCAGCCTGGGCGACAGA\n\ +GCGAGACTCCGTCTCAAAAAGGCCGGGCGCGGTGGCTCACGCCTGTAATCCCAGCACTTT\n\ +GGGAGGCCGAGGCGGGCGGATCACCTGAGGTCAGGAGTTCGAGACCAGCCTGGCCAACAT\n\ +GGTGAAACCCCGTCTCTACTAAAAATACAAAAATTAGCCGGGCGTGGTGGCGCGCGCCTG\n\ +TAATCCCAGCTACTCGGGAGGCTGAGGCAGGAGAATCGCTTGAACCCGGGAGGCGGAGGT\n\ +TGCAGTGAGCCGAGATCGCGCCACTGCACTCCAGCCTGGGCGACAGAGCGAGACTCCGTC\n\ +TCAAAAAGGCCGGGCGCGGTGGCTCACGCCTGTAATCCCAGCACTTTGGGAGGCCGAGGC\n\ +GGGCGGATCACCTGAGGTCAGGAGTTCGAGACCAGCCTGGCCAACATGGTGAAACCCCGT\n\ +CTCTACTAAAAATACAAAAATTAGCCGGGCGTGGTGGCGCGCGCCTGTAATCCCAGCTAC\n\ +TCGGGAGGCTGAGGCAGGAGAATCGCTTGAACCCGGGAGGCGGAGGTTGCAGTGAGCCGA\n\ +GATCGCGCCACTGCACTCCAGCCTGGGCGACAGAGCGAGACTCCGTCTCAAAAAGGCCGG\n\ +GCGCGGTGGCTCACGCCTGTAATCCCAGCACTTTGGGAGGCCGAGGCGGGCGGATCACCT\n\ +GAGGTCAGGAGTTCGAGACCAGCCTGGCCAACATGGTGAAACCCCGTCTCTACTAAAAAT\n\ +ACAAAAATTAGCCGGGCGTGGTGGCGCGCGCCTGTAATCCCAGCTACTCGGGAGGCTGAG\n\ +GCAGGAGAATCGCTTGAACCCGGGAGGCGGAGGTTGCAGTGAGCCGAGATCGCGCCACTG\n\ +CACTCCAGCCTGGGCGACAGAGCGAGACTCCGTCTCAAAAAGGCCGGGCGCGGTGGCTCA\n\ +CGCCTGTAATCCCAGCACTTTGGGAGGCCGAGGCGGGCGGATCACCTGAGGTCAGGAGTT\n\ +CGAGACCAGCCTGGCCAACATGGTGAAACCCCGTCTCTACTAAAAATACAAAAATTAGCC\n\ +GGGCGTGGTGGCGCGCGCCTGTAATCCCAGCTACTCGGGAGGCTGAGGCAGGAGAATCGC\n\ +TTGAACCCGGGAGGCGGAGGTTGCAGTGAGCCGAGATCGCGCCACTGCACTCCAGCCTGG\n\ +GCGACAGAGCGAGACTCCGTCTCAAAAAGGCCGGGCGCGGTGGCTCACGCCTGTAATCCC\n\ +AGCACTTTGGGAGGCCGAGGCGGGCGGATCACCTGAGGTCAGGAGTTCGAGACCAGCCTG\n\ +GCCAACATGGTGAAACCCCGTCTCTACTAAAAATACAAAAATTAGCCGGGCGTGGTGGCG\n\ +CGCGCCTGTAATCCCAGCTACTCGGGAGGCTGAGGCAGGAGAATCGCTTGAACCCGGGAG\n\ +GCGGAGGTTGCAGTGAGCCGAGATCGCGCCACTGCACTCCAGCCTGGGCGACAGAGCGAG\n\ +ACTCCGTCTCAAAAAGGCCGGGCGCGGTGGCTCACGCCTGTAATCCCAGCACTTTGGGAG\n\ +GCCGAGGCGGGCGGATCACCTGAGGTCAGGAGTTCGAGACCAGCCTGGCCAACATGGTGA\n\ +AACCCCGTCTCTACTAAAAATACAAAAATTAGCCGGGCGTGGTGGCGCGCGCCTGTAATC\n\ +CCAGCTACTCGGGAGGCTGAGGCAGGAGAATCGCTTGAACCCGGGAGGCGGAGGTTGCAG\n\ +TGAGCCGAGATCGCGCCACTGCACTCCAGCCTGGGCGACAGAGCGAGACTCCGTCTCAAA\n\ +AAGGCCGGGCGCGGTGGCTCACGCCTGTAATCCCAGCACTTTGGGAGGCCGAGGCGGGCG\n\ +GATCACCTGAGGTCAGGAGTTCGAGACCAGCCTGGCCAACATGGTGAAACCCCGTCTCTA\n\ +CTAAAAATACAAAAATTAGCCGGGCGTGGTGGCGCGCGCCTGTAATCCCAGCTACTCGGG\n\ +AGGCTGAGGCAGGAGAATCGCTTGAACCCGGGAGGCGGAGGTTGCAGTGAGCCGAGATCG\n\ +CGCCACTGCACTCCAGCCTGGGCGACAGAGCGAGACTCCGTCTCAAAAAGGCCGGGCGCG\n\ +GTGGCTCACGCCTGTAATCCCAGCACTTTGGGAGGCCGAGGCGGGCGGATCACCTGAGGT\n\ +CAGGAGTTCGAGACCAGCCTGGCCAACATGGTGAAACCCCGTCTCTACTAAAAATACAAA\n\ +AATTAGCCGGGCGTGGTGGCGCGCGCCTGTAATCCCAGCTACTCGGGAGGCTGAGGCAGG\n\ +AGAATCGCTTGAACCCGGGAGGCGGAGGTTGCAGTGAGCCGAGATCGCGCCACTGCACTC\n\ +CAGCCTGGGCGACAGAGCGAGACTCCGTCTCAAAAAGGCCGGGCGCGGTGGCTCACGCCT\n\ +GTAATCCCAGCACTTTGGGAGGCCGAGGCGGGCGGATCACCTGAGGTCAGGAGTTCGAGA\n\ +CCAGCCTGGCCAACATGGTGAAACCCCGTCTCTACTAAAAATACAAAAATTAGCCGGGCG\n\ +TGGTGGCGCGCGCCTGTAATCCCAGCTACTCGGGAGGCTGAGGCAGGAGAATCGCTTGAA\n\ +CCCGGGAGGCGGAGGTTGCAGTGAGCCGAGATCGCGCCACTGCACTCCAGCCTGGGCGAC\n\ +AGAGCGAGACTCCGTCTCAAAAAGGCCGGGCGCGGTGGCTCACGCCTGTAATCCCAGCAC\n\ +TTTGGGAGGCCGAGGCGGGCGGATCACCTGAGGTCAGGAGTTCGAGACCAGCCTGGCCAA\n\ +CATGGTGAAACCCCGTCTCTACTAAAAATACAAAAATTAGCCGGGCGTGGTGGCGCGCGC\n\ +CTGTAATCCCAGCTACTCGGGAGGCTGAGGCAGGAGAATCGCTTGAACCCGGGAGGCGGA\n\ +GGTTGCAGTGAGCCGAGATCGCGCCACTGCACTCCAGCCTGGGCGACAGAGCGAGACTCC\n\ +GTCTCAAAAAGGCCGGGCGCGGTGGCTCACGCCTGTAATCCCAGCACTTTGGGAGGCCGA\n\ +GGCGGGCGGATCACCTGAGGTCAGGAGTTCGAGACCAGCCTGGCCAACATGGTGAAACCC\n\ +CGTCTCTACTAAAAATACAAAAATTAGCCGGGCGTGGTGGCGCGCGCCTGTAATCCCAGC\n\ +TACTCGGGAGGCTGAGGCAGGAGAATCGCTTGAACCCGGGAGGCGGAGGTTGCAGTGAGC\n\ +CGAGATCGCGCCACTGCACTCCAGCCTGGGCGACAGAGCGAGACTCCGTCTCAAAAAGGC\n\ +CGGGCGCGGTGGCTCACGCCTGTAATCCCAGCACTTTGGGAGGCCGAGGCGGGCGGATCA\n\ +CCTGAGGTCAGGAGTTCGAGACCAGCCTGGCCAACATGGTGAAACCCCGTCTCTACTAAA\n\ +AATACAAAAATTAGCCGGGCGTGGTGGCGCGCGCCTGTAATCCCAGCTACTCGGGAGGCT\n\ +GAGGCAGGAGAATCGCTTGAACCCGGGAGGCGGAGGTTGCAGTGAGCCGAGATCGCGCCA\n\ +CTGCACTCCAGCCTGGGCGACAGAGCGAGACTCCGTCTCAAAAAGGCCGGGCGCGGTGGC\n\ +TCACGCCTGTAATCCCAGCACTTTGGGAGGCCGAGGCGGGCGGATCACCTGAGGTCAGGA\n\ +GTTCGAGACCAGCCTGGCCAACATGGTGAAACCCCGTCTCTACTAAAAATACAAAAATTA\n\ +GCCGGGCGTGGTGGCGCGCGCCTGTAATCCCAGCTACTCGGGAGGCTGAGGCAGGAGAAT\n\ +CGCTTGAACCCGGGAGGCGGAGGTTGCAGTGAGCCGAGATCGCGCCACTGCACTCCAGCC\n\ +TGGGCGACAGAGCGAGACTCCGTCTCAAAAAGGCCGGGCGCGGTGGCTCACGCCTGTAAT\n\ +CCCAGCACTTTGGGAGGCCGAGGCGGGCGGATCACCTGAGGTCAGGAGTTCGAGACCAGC\n\ +CTGGCCAACATGGTGAAACCCCGTCTCTACTAAAAATACAAAAATTAGCCGGGCGTGGTG\n\ +GCGCGCGCCTGTAATCCCAGCTACTCGGGAGGCTGAGGCAGGAGAATCGCTTGAACCCGG\n\ +GAGGCGGAGGTTGCAGTGAGCCGAGATCGCGCCACTGCACTCCAGCCTGGGCGACAGAGC\n\ +GAGACTCCGTCTCAAAAAGGCCGGGCGCGGTGGCTCACGCCTGTAATCCCAGCACTTTGG\n\ +GAGGCCGAGGCGGGCGGATCACCTGAGGTCAGGAGTTCGAGACCAGCCTGGCCAACATGG\n\ +TGAAACCCCGTCTCTACTAAAAATACAAAAATTAGCCGGGCGTGGTGGCGCGCGCCTGTA\n\ +ATCCCAGCTACTCGGGAGGCTGAGGCAGGAGAATCGCTTGAACCCGGGAGGCGGAGGTTG\n\ +CAGTGAGCCGAGATCGCGCCACTGCACTCCAGCCTGGGCGACAGAGCGAGACTCCGTCTC\n\ +AAAAAGGCCGGGCGCGGTGGCTCACGCCTGTAATCCCAGCACTTTGGGAGGCCGAGGCGG\n\ +GCGGATCACCTGAGGTCAGGAGTTCGAGACCAGCCTGGCCAACATGGTGAAACCCCGTCT\n\ +CTACTAAAAATACAAAAATTAGCCGGGCGTGGTGGCGCGCGCCTGTAATCCCAGCTACTC\n\ +GGGAGGCTGAGGCAGGAGAATCGCTTGAACCCGGGAGGCGGAGGTTGCAGTGAGCCGAGA\n\ +TCGCGCCACTGCACTCCAGCCTGGGCGACAGAGCGAGACTCCGTCTCAAAAAGGCCGGGC\n\ +GCGGTGGCTCACGCCTGTAATCCCAGCACTTTGGGAGGCCGAGGCGGGCGGATCACCTGA\n\ +GGTCAGGAGTTCGAGACCAGCCTGGCCAACATGGTGAAACCCCGTCTCTACTAAAAATAC\n\ +AAAAATTAGCCGGGCGTGGTGGCGCGCGCCTGTAATCCCAGCTACTCGGGAGGCTGAGGC\n\ +AGGAGAATCGCTTGAACCCGGGAGGCGGAGGTTGCAGTGAGCCGAGATCGCGCCACTGCA\n\ +CTCCAGCCTGGGCGACAGAGCGAGACTCCGTCTCAAAAAGGCCGGGCGCGGTGGCTCACG\n\ +CCTGTAATCCCAGCACTTTGGGAGGCCGAGGCGGGCGGATCACCTGAGGTCAGGAGTTCG\n\ +AGACCAGCCTGGCCAACATGGTGAAACCCCGTCTCTACTAAAAATACAAAAATTAGCCGG\n\ +GCGTGGTGGCGCGCGCCTGTAATCCCAGCTACTCGGGAGGCTGAGGCAGGAGAATCGCTT\n\ +GAACCCGGGAGGCGGAGGTTGCAGTGAGCCGAGATCGCGCCACTGCACTCCAGCCTGGGC\n\ +GACAGAGCGAGACTCCGTCTCAAAAAGGCCGGGCGCGGTGGCTCACGCCTGTAATCCCAG\n\ +CACTTTGGGAGGCCGAGGCGGGCGGATCACCTGAGGTCAGGAGTTCGAGACCAGCCTGGC\n\ +CAACATGGTGAAACCCCGTCTCTACTAAAAATACAAAAATTAGCCGGGCGTGGTGGCGCG\n\ +CGCCTGTAATCCCAGCTACTCGGGAGGCTGAGGCAGGAGAATCGCTTGAACCCGGGAGGC\n\ +GGAGGTTGCAGTGAGCCGAGATCGCGCCACTGCACTCCAGCCTGGGCGACAGAGCGAGAC\n\ +TCCGTCTCAAAAAGGCCGGGCGCGGTGGCTCACGCCTGTAATCCCAGCACTTTGGGAGGC\n\ +CGAGGCGGGCGGATCACCTGAGGTCAGGAGTTCGAGACCAGCCTGGCCAACATGGTGAAA\n\ +CCCCGTCTCTACTAAAAATACAAAAATTAGCCGGGCGTGGTGGCGCGCGCCTGTAATCCC\n\ +AGCTACTCGGGAGGCTGAGGCAGGAGAATCGCTTGAACCCGGGAGGCGGAGGTTGCAGTG\n\ +AGCCGAGATCGCGCCACTGCACTCCAGCCTGGGCGACAGAGCGAGACTCCGTCTCAAAAA\n\ +GGCCGGGCGCGGTGGCTCACGCCTGTAATCCCAGCACTTTGGGAGGCCGAGGCGGGCGGA\n\ +TCACCTGAGGTCAGGAGTTCGAGACCAGCCTGGCCAACATGGTGAAACCCCGTCTCTACT\n\ +AAAAATACAAAAATTAGCCGGGCGTGGTGGCGCGCGCCTGTAATCCCAGCTACTCGGGAG\n\ +GCTGAGGCAGGAGAATCGCTTGAACCCGGGAGGCGGAGGTTGCAGTGAGCCGAGATCGCG\n\ +CCACTGCACTCCAGCCTGGGCGACAGAGCGAGACTCCGTCTCAAAAAGGCCGGGCGCGGT\n\ +GGCTCACGCCTGTAATCCCAGCACTTTGGGAGGCCGAGGCGGGCGGATCACCTGAGGTCA\n\ +GGAGTTCGAGACCAGCCTGGCCAACATGGTGAAACCCCGTCTCTACTAAAAATACAAAAA\n\ +TTAGCCGGGCGTGGTGGCGCGCGCCTGTAATCCCAGCTACTCGGGAGGCTGAGGCAGGAG\n\ +AATCGCTTGAACCCGGGAGGCGGAGGTTGCAGTGAGCCGAGATCGCGCCACTGCACTCCA\n\ +GCCTGGGCGACAGAGCGAGACTCCGTCTCAAAAAGGCCGGGCGCGGTGGCTCACGCCTGT\n\ +AATCCCAGCACTTTGGGAGGCCGAGGCGGGCGGATCACCTGAGGTCAGGAGTTCGAGACC\n\ +AGCCTGGCCAACATGGTGAAACCCCGTCTCTACTAAAAATACAAAAATTAGCCGGGCGTG\n\ +GTGGCGCGCGCCTGTAATCCCAGCTACTCGGGAGGCTGAGGCAGGAGAATCGCTTGAACC\n\ +CGGGAGGCGGAGGTTGCAGTGAGCCGAGATCGCGCCACTGCACTCCAGCCTGGGCGACAG\n\ +AGCGAGACTCCGTCTCAAAAAGGCCGGGCGCGGTGGCTCACGCCTGTAATCCCAGCACTT\n\ +TGGGAGGCCGAGGCGGGCGGATCACCTGAGGTCAGGAGTTCGAGACCAGCCTGGCCAACA\n\ +TGGTGAAACCCCGTCTCTACTAAAAATACAAAAATTAGCCGGGCGTGGTGGCGCGCGCCT\n\ +GTAATCCCAGCTACTCGGGAGGCTGAGGCAGGAGAATCGCTTGAACCCGGGAGGCGGAGG\n\ +TTGCAGTGAGCCGAGATCGCGCCACTGCACTCCAGCCTGGGCGACAGAGCGAGACTCCGT\n\ +CTCAAAAAGGCCGGGCGCGGTGGCTCACGCCTGTAATCCCAGCACTTTGGGAGGCCGAGG\n\ +CGGGCGGATCACCTGAGGTCAGGAGTTCGAGACCAGCCTGGCCAACATGGTGAAACCCCG\n\ +TCTCTACTAAAAATACAAAAATTAGCCGGGCGTGGTGGCGCGCGCCTGTAATCCCAGCTA\n\ +CTCGGGAGGCTGAGGCAGGAGAATCGCTTGAACCCGGGAGGCGGAGGTTGCAGTGAGCCG\n\ +AGATCGCGCCACTGCACTCCAGCCTGGGCGACAGAGCGAGACTCCGTCTCAAAAAGGCCG\n\ +GGCGCGGTGGCTCACGCCTGTAATCCCAGCACTTTGGGAGGCCGAGGCGGGCGGATCACC\n\ +TGAGGTCAGGAGTTCGAGACCAGCCTGGCCAACATGGTGAAACCCCGTCTCTACTAAAAA\n\ +TACAAAAATTAGCCGGGCGTGGTGGCGCGCGCCTGTAATCCCAGCTACTCGGGAGGCTGA\n\ +GGCAGGAGAATCGCTTGAACCCGGGAGGCGGAGGTTGCAGTGAGCCGAGATCGCGCCACT\n\ +GCACTCCAGCCTGGGCGACAGAGCGAGACTCCGTCTCAAAAAGGCCGGGCGCGGTGGCTC\n\ +ACGCCTGTAATCCCAGCACTTTGGGAGGCCGAGGCGGGCGGATCACCTGAGGTCAGGAGT\n\ +TCGAGACCAGCCTGGCCAACATGGTGAAACCCCGTCTCTACTAAAAATACAAAAATTAGC\n\ +CGGGCGTGGTGGCGCGCGCCTGTAATCCCAGCTACTCGGGAGGCTGAGGCAGGAGAATCG\n\ +CTTGAACCCGGGAGGCGGAGGTTGCAGTGAGCCGAGATCGCGCCACTGCACTCCAGCCTG\n\ +GGCGACAGAGCGAGACTCCGTCTCAAAAAGGCCGGGCGCGGTGGCTCACGCCTGTAATCC\n\ +CAGCACTTTGGGAGGCCGAGGCGGGCGGATCACCTGAGGTCAGGAGTTCGAGACCAGCCT\n\ +GGCCAACATGGTGAAACCCCGTCTCTACTAAAAATACAAAAATTAGCCGGGCGTGGTGGC\n\ +GCGCGCCTGTAATCCCAGCTACTCGGGAGGCTGAGGCAGGAGAATCGCTTGAACCCGGGA\n\ +GGCGGAGGTTGCAGTGAGCCGAGATCGCGCCACTGCACTCCAGCCTGGGCGACAGAGCGA\n\ +GACTCCGTCTCAAAAAGGCCGGGCGCGGTGGCTCACGCCTGTAATCCCAGCACTTTGGGA\n\ +GGCCGAGGCGGGCGGATCACCTGAGGTCAGGAGTTCGAGACCAGCCTGGCCAACATGGTG\n\ +AAACCCCGTCTCTACTAAAAATACAAAAATTAGCCGGGCGTGGTGGCGCGCGCCTGTAAT\n\ +CCCAGCTACTCGGGAGGCTGAGGCAGGAGAATCGCTTGAACCCGGGAGGCGGAGGTTGCA\n\ +GTGAGCCGAGATCGCGCCACTGCACTCCAGCCTGGGCGACAGAGCGAGACTCCGTCTCAA\n\ +AAAGGCCGGGCGCGGTGGCTCACGCCTGTAATCCCAGCACTTTGGGAGGCCGAGGCGGGC\n\ +GGATCACCTGAGGTCAGGAGTTCGAGACCAGCCTGGCCAACATGGTGAAACCCCGTCTCT\n\ +ACTAAAAATACAAAAATTAGCCGGGCGTGGTGGCGCGCGCCTGTAATCCCAGCTACTCGG\n\ +GAGGCTGAGGCAGGAGAATC\n\ +>TWO IUB ambiguity codes\n\ +cttBtatcatatgctaKggNcataaaSatgtaaaDcDRtBggDtctttataattcBgtcg\n\ +tactDtDagcctatttSVHtHttKtgtHMaSattgWaHKHttttagacatWatgtRgaaa\n\ +NtactMcSMtYtcMgRtacttctWBacgaaatatagScDtttgaagacacatagtVgYgt\n\ +cattHWtMMWcStgttaggKtSgaYaaccWStcgBttgcgaMttBYatcWtgacaYcaga\n\ +gtaBDtRacttttcWatMttDBcatWtatcttactaBgaYtcttgttttttttYaaScYa\n\ +HgtgttNtSatcMtcVaaaStccRcctDaataataStcYtRDSaMtDttgttSagtRRca\n\ +tttHatSttMtWgtcgtatSSagactYaaattcaMtWatttaSgYttaRgKaRtccactt\n\ +tattRggaMcDaWaWagttttgacatgttctacaaaRaatataataaMttcgDacgaSSt\n\ +acaStYRctVaNMtMgtaggcKatcttttattaaaaagVWaHKYagtttttatttaacct\n\ +tacgtVtcVaattVMBcttaMtttaStgacttagattWWacVtgWYagWVRctDattBYt\n\ +gtttaagaagattattgacVatMaacattVctgtBSgaVtgWWggaKHaatKWcBScSWa\n\ +accRVacacaaactaccScattRatatKVtactatatttHttaagtttSKtRtacaaagt\n\ +RDttcaaaaWgcacatWaDgtDKacgaacaattacaRNWaatHtttStgttattaaMtgt\n\ +tgDcgtMgcatBtgcttcgcgaDWgagctgcgaggggVtaaScNatttacttaatgacag\n\ +cccccacatYScaMgtaggtYaNgttctgaMaacNaMRaacaaacaKctacatagYWctg\n\ +ttWaaataaaataRattagHacacaagcgKatacBttRttaagtatttccgatctHSaat\n\ +actcNttMaagtattMtgRtgaMgcataatHcMtaBSaRattagttgatHtMttaaKagg\n\ +YtaaBataSaVatactWtataVWgKgttaaaacagtgcgRatatacatVtHRtVYataSa\n\ +KtWaStVcNKHKttactatccctcatgWHatWaRcttactaggatctataDtDHBttata\n\ +aaaHgtacVtagaYttYaKcctattcttcttaataNDaaggaaaDYgcggctaaWSctBa\n\ +aNtgctggMBaKctaMVKagBaactaWaDaMaccYVtNtaHtVWtKgRtcaaNtYaNacg\n\ +gtttNattgVtttctgtBaWgtaattcaagtcaVWtactNggattctttaYtaaagccgc\n\ +tcttagHVggaYtgtNcDaVagctctctKgacgtatagYcctRYHDtgBattDaaDgccK\n\ +tcHaaStttMcctagtattgcRgWBaVatHaaaataYtgtttagMDMRtaataaggatMt\n\ +ttctWgtNtgtgaaaaMaatatRtttMtDgHHtgtcattttcWattRSHcVagaagtacg\n\ +ggtaKVattKYagactNaatgtttgKMMgYNtcccgSKttctaStatatNVataYHgtNa\n\ +BKRgNacaactgatttcctttaNcgatttctctataScaHtataRagtcRVttacDSDtt\n\ +aRtSatacHgtSKacYagttMHtWataggatgactNtatSaNctataVtttRNKtgRacc\n\ +tttYtatgttactttttcctttaaacatacaHactMacacggtWataMtBVacRaSaatc\n\ +cgtaBVttccagccBcttaRKtgtgcctttttRtgtcagcRttKtaaacKtaaatctcac\n\ +aattgcaNtSBaaccgggttattaaBcKatDagttactcttcattVtttHaaggctKKga\n\ +tacatcBggScagtVcacattttgaHaDSgHatRMaHWggtatatRgccDttcgtatcga\n\ +aacaHtaagttaRatgaVacttagattVKtaaYttaaatcaNatccRttRRaMScNaaaD\n\ +gttVHWgtcHaaHgacVaWtgttScactaagSgttatcttagggDtaccagWattWtRtg\n\ +ttHWHacgattBtgVcaYatcggttgagKcWtKKcaVtgaYgWctgYggVctgtHgaNcV\n\ +taBtWaaYatcDRaaRtSctgaHaYRttagatMatgcatttNattaDttaattgttctaa\n\ +ccctcccctagaWBtttHtBccttagaVaatMcBHagaVcWcagBVttcBtaYMccagat\n\ +gaaaaHctctaacgttagNWRtcggattNatcRaNHttcagtKttttgWatWttcSaNgg\n\ +gaWtactKKMaacatKatacNattgctWtatctaVgagctatgtRaHtYcWcttagccaa\n\ +tYttWttaWSSttaHcaaaaagVacVgtaVaRMgattaVcDactttcHHggHRtgNcctt\n\ +tYatcatKgctcctctatVcaaaaKaaaagtatatctgMtWtaaaacaStttMtcgactt\n\ +taSatcgDataaactaaacaagtaaVctaggaSccaatMVtaaSKNVattttgHccatca\n\ +cBVctgcaVatVttRtactgtVcaattHgtaaattaaattttYtatattaaRSgYtgBag\n\ +aHSBDgtagcacRHtYcBgtcacttacactaYcgctWtattgSHtSatcataaatataHt\n\ +cgtYaaMNgBaatttaRgaMaatatttBtttaaaHHKaatctgatWatYaacttMctctt\n\ +ttVctagctDaaagtaVaKaKRtaacBgtatccaaccactHHaagaagaaggaNaaatBW\n\ +attccgStaMSaMatBttgcatgRSacgttVVtaaDMtcSgVatWcaSatcttttVatag\n\ +ttactttacgatcaccNtaDVgSRcgVcgtgaacgaNtaNatatagtHtMgtHcMtagaa\n\ +attBgtataRaaaacaYKgtRccYtatgaagtaataKgtaaMttgaaRVatgcagaKStc\n\ +tHNaaatctBBtcttaYaBWHgtVtgacagcaRcataWctcaBcYacYgatDgtDHccta\n\ +aagacYRcaggattHaYgtKtaatgcVcaataMYacccatatcacgWDBtgaatcBaata\n\ +cKcttRaRtgatgaBDacggtaattaaYtataStgVHDtDctgactcaaatKtacaatgc\n\ +gYatBtRaDatHaactgtttatatDttttaaaKVccYcaaccNcBcgHaaVcattHctcg\n\ +attaaatBtatgcaaaaatYMctSactHatacgaWacattacMBgHttcgaatVaaaaca\n\ +BatatVtctgaaaaWtctRacgBMaatSgRgtgtcgactatcRtattaScctaStagKga\n\ +DcWgtYtDDWKRgRtHatRtggtcgaHgggcgtattaMgtcagccaBggWVcWctVaaat\n\ +tcgNaatcKWagcNaHtgaaaSaaagctcYctttRVtaaaatNtataaccKtaRgtttaM\n\ +tgtKaBtRtNaggaSattHatatWactcagtgtactaKctatttgRYYatKatgtccgtR\n\ +tttttatttaatatVgKtttgtatgtNtataRatWYNgtRtHggtaaKaYtKSDcatcKg\n\ +taaYatcSRctaVtSMWtVtRWHatttagataDtVggacagVcgKWagBgatBtaaagNc\n\ +aRtagcataBggactaacacRctKgttaatcctHgDgttKHHagttgttaatgHBtatHc\n\ +DaagtVaBaRccctVgtgDtacRHSctaagagcggWYaBtSaKtHBtaaactYacgNKBa\n\ +VYgtaacttagtVttcttaatgtBtatMtMtttaattaatBWccatRtttcatagVgMMt\n\ +agctStKctaMactacDNYgKYHgaWcgaHgagattacVgtttgtRaSttaWaVgataat\n\ +gtgtYtaStattattMtNgWtgttKaccaatagNYttattcgtatHcWtctaaaNVYKKt\n\ +tWtggcDtcgaagtNcagatacgcattaagaccWctgcagcttggNSgaNcHggatgtVt\n\ +catNtRaaBNcHVagagaaBtaaSggDaatWaatRccaVgggStctDaacataKttKatt\n\ +tggacYtattcSatcttagcaatgaVBMcttDattctYaaRgatgcattttNgVHtKcYR\n\ +aatRKctgtaaacRatVSagctgtWacBtKVatctgttttKcgtctaaDcaagtatcSat\n\ +aWVgcKKataWaYttcccSaatgaaaacccWgcRctWatNcWtBRttYaattataaNgac\n\ +acaatagtttVNtataNaYtaatRaVWKtBatKagtaatataDaNaaaaataMtaagaaS\n\ +tccBcaatNgaataWtHaNactgtcDtRcYaaVaaaaaDgtttRatctatgHtgttKtga\n\ +aNSgatactttcgagWaaatctKaaDaRttgtggKKagcDgataaattgSaacWaVtaNM\n\ +acKtcaDaaatttctRaaVcagNacaScRBatatctRatcctaNatWgRtcDcSaWSgtt\n\ +RtKaRtMtKaatgttBHcYaaBtgatSgaSWaScMgatNtctcctatttctYtatMatMt\n\ +RRtSaattaMtagaaaaStcgVgRttSVaScagtgDtttatcatcatacRcatatDctta\n\ +tcatVRtttataaHtattcYtcaaaatactttgVctagtaaYttagatagtSYacKaaac\n\ +gaaKtaaatagataatSatatgaaatSgKtaatVtttatcctgKHaatHattagaaccgt\n\ +YaaHactRcggSBNgtgctaaBagBttgtRttaaattYtVRaaaattgtaatVatttctc\n\ +ttcatgBcVgtgKgaHaaatattYatagWacNctgaaMcgaattStagWaSgtaaKagtt\n\ +ttaagaDgatKcctgtaHtcatggKttVDatcaaggtYcgccagNgtgcVttttagagat\n\ +gctaccacggggtNttttaSHaNtatNcctcatSaaVgtactgBHtagcaYggYVKNgta\n\ +KBcRttgaWatgaatVtagtcgattYgatgtaatttacDacSctgctaaaStttaWMagD\n\ +aaatcaVYctccgggcgaVtaaWtStaKMgDtttcaaMtVgBaatccagNaaatcYRMBg\n\ +gttWtaaScKttMWtYataRaDBMaDataatHBcacDaaKDactaMgagttDattaHatH\n\ +taYatDtattDcRNStgaatattSDttggtattaaNSYacttcDMgYgBatWtaMagact\n\ +VWttctttgYMaYaacRgHWaattgRtaagcattctMKVStatactacHVtatgatcBtV\n\ +NataaBttYtSttacKgggWgYDtgaVtYgatDaacattYgatggtRDaVDttNactaSa\n\ +MtgNttaacaaSaBStcDctaccacagacgcaHatMataWKYtaYattMcaMtgSttDag\n\ +cHacgatcaHttYaKHggagttccgatYcaatgatRaVRcaagatcagtatggScctata\n\ +ttaNtagcgacgtgKaaWaactSgagtMYtcttccaKtStaacggMtaagNttattatcg\n\ +tctaRcactctctDtaacWYtgaYaSaagaWtNtatttRacatgNaatgttattgWDDcN\n\ +aHcctgaaHacSgaataaRaataMHttatMtgaSDSKatatHHaNtacagtccaYatWtc\n\ +actaactatKDacSaStcggataHgYatagKtaatKagStaNgtatactatggRHacttg\n\ +tattatgtDVagDVaRctacMYattDgtttYgtctatggtKaRSttRccRtaaccttaga\n\ +gRatagSaaMaacgcaNtatgaaatcaRaagataatagatactcHaaYKBctccaagaRa\n\ +BaStNagataggcgaatgaMtagaatgtcaKttaaatgtaWcaBttaatRcggtgNcaca\n\ +aKtttScRtWtgcatagtttWYaagBttDKgcctttatMggNttattBtctagVtacata\n\ +aaYttacacaaRttcYtWttgHcaYYtaMgBaBatctNgcDtNttacgacDcgataaSat\n\ +YaSttWtcctatKaatgcagHaVaacgctgcatDtgttaSataaaaYSNttatagtaNYt\n\ +aDaaaNtggggacttaBggcHgcgtNtaaMcctggtVtaKcgNacNtatVaSWctWtgaW\n\ +cggNaBagctctgaYataMgaagatBSttctatacttgtgtKtaattttRagtDtacata\n\ +tatatgatNHVgBMtKtaKaNttDHaagatactHaccHtcatttaaagttVaMcNgHata\n\ +tKtaNtgYMccttatcaaNagctggacStttcNtggcaVtattactHaSttatgNMVatt\n\ +MMDtMactattattgWMSgtHBttStStgatatRaDaagattttctatMtaaaaaggtac\n\ +taaVttaSacNaatactgMttgacHaHRttgMacaaaatagttaatatWKRgacDgaRta\n\ +tatttattatcYttaWtgtBRtWatgHaaattHataagtVaDtWaVaWtgStcgtMSgaS\n\ +RgMKtaaataVacataatgtaSaatttagtcgaaHtaKaatgcacatcggRaggSKctDc\n\ +agtcSttcccStYtccRtctctYtcaaKcgagtaMttttcRaYDttgttatctaatcata\n\ +NctctgctatcaMatactataggDaHaaSttMtaDtcNatataattctMcStaaBYtaNa\n\ +gatgtaatHagagSttgWHVcttatKaYgDctcttggtgttMcRaVgSgggtagacaata\n\ +aDtaattSaDaNaHaBctattgNtaccaaRgaVtKNtaaYggHtaKKgHcatctWtctDt\n\ +ttctttggSDtNtaStagttataaacaattgcaBaBWggHgcaaaBtYgctaatgaaatW\n\ +cDcttHtcMtWWattBHatcatcaaatctKMagtDNatttWaBtHaaaNgMttaaStagt\n\ +tctctaatDtcRVaYttgttMtRtgtcaSaaYVgSWDRtaatagctcagDgcWWaaaBaa\n\ +RaBctgVgggNgDWStNaNBKcBctaaKtttDcttBaaggBttgaccatgaaaNgttttt\n\ +tttatctatgttataccaaDRaaSagtaVtDtcaWatBtacattaWacttaSgtattggD\n\ +gKaaatScaattacgWcagKHaaccaYcRcaRttaDttRtttHgaHVggcttBaRgtccc\n\ +tDatKaVtKtcRgYtaKttacgtatBtStaagcaattaagaRgBagSaattccSWYttta\n\ +ttVaataNctgHgttaaNBgcVYgtRtcccagWNaaaacaDNaBcaaaaRVtcWMgBagM\n\ +tttattacgDacttBtactatcattggaaatVccggttRttcatagttVYcatYaSHaHc\n\ +ttaaagcNWaHataaaRWtctVtRYtagHtaaaYMataHYtNBctNtKaatattStgaMc\n\ +BtRgctaKtgcScSttDgYatcVtggaaKtaagatWccHccgKYctaNNctacaWctttt\n\ +gcRtgtVcgaKttcMRHgctaHtVaataaDtatgKDcttatBtDttggNtacttttMtga\n\ +acRattaaNagaactcaaaBBVtcDtcgaStaDctgaaaSgttMaDtcgttcaccaaaag\n\ +gWtcKcgSMtcDtatgtttStaaBtatagDcatYatWtaaaBacaKgcaDatgRggaaYc\n\ +taRtccagattDaWtttggacBaVcHtHtaacDacYgtaatataMagaatgHMatcttat\n\ +acgtatttttatattacHactgttataMgStYaattYaccaattgagtcaaattaYtgta\n\ +tcatgMcaDcgggtcttDtKgcatgWRtataatatRacacNRBttcHtBgcRttgtgcgt\n\ +catacMtttBctatctBaatcattMttMYgattaaVYatgDaatVagtattDacaacDMa\n\ +tcMtHcccataagatgBggaccattVWtRtSacatgctcaaggggYtttDtaaNgNtaaB\n\ +atggaatgtctRtaBgBtcNYatatNRtagaacMgagSaSDDSaDcctRagtVWSHtVSR\n\ +ggaacaBVaccgtttaStagaacaMtactccagtttVctaaRaaHttNcttagcaattta\n\ +ttaatRtaaaatctaacDaBttggSagagctacHtaaRWgattcaaBtctRtSHaNtgta\n\ +cattVcaHaNaagtataccacaWtaRtaaVKgMYaWgttaKggKMtKcgWatcaDatYtK\n\ +SttgtacgaccNctSaattcDcatcttcaaaDKttacHtggttHggRRaRcaWacaMtBW\n\ +VHSHgaaMcKattgtaRWttScNattBBatYtaNRgcggaagacHSaattRtttcYgacc\n\ +BRccMacccKgatgaacttcgDgHcaaaaaRtatatDtatYVtttttHgSHaSaatagct\n\ +NYtaHYaVYttattNtttgaaaYtaKttWtctaNtgagaaaNctNDctaaHgttagDcRt\n\ +tatagccBaacgcaRBtRctRtggtaMYYttWtgataatcgaataattattataVaaaaa\n\ +ttacNRVYcaaMacNatRttcKatMctgaagactaattataaYgcKcaSYaatMNctcaa\n\ +cgtgatttttBacNtgatDccaattattKWWcattttatatatgatBcDtaaaagttgaa\n\ +VtaHtaHHtBtataRBgtgDtaataMttRtDgDcttattNtggtctatctaaBcatctaR\n\ +atgNacWtaatgaagtcMNaacNgHttatactaWgcNtaStaRgttaaHacccgaYStac\n\ +aaaatWggaYaWgaattattcMaactcBKaaaRVNcaNRDcYcgaBctKaacaaaaaSgc\n\ +tccYBBHYaVagaatagaaaacagYtctVccaMtcgtttVatcaatttDRtgWctagtac\n\ +RttMctgtDctttcKtWttttataaatgVttgBKtgtKWDaWagMtaaagaaattDVtag\n\ +gttacatcatttatgtcgMHaVcttaBtVRtcgtaYgBRHatttHgaBcKaYWaatcNSc\n\ +tagtaaaaatttacaatcactSWacgtaatgKttWattagttttNaggtctcaagtcact\n\ +attcttctaagKggaataMgtttcataagataaaaatagattatDgcBVHWgaBKttDgc\n\ +atRHaagcaYcRaattattatgtMatatattgHDtcaDtcaaaHctStattaatHaccga\n\ +cNattgatatattttgtgtDtRatagSacaMtcRtcattcccgacacSattgttKaWatt\n\ +NHcaacttccgtttSRtgtctgDcgctcaaMagVtBctBMcMcWtgtaacgactctcttR\n\ +ggRKSttgYtYatDccagttDgaKccacgVatWcataVaaagaataMgtgataaKYaaat\n\ +cHDaacgataYctRtcYatcgcaMgtNttaBttttgatttaRtStgcaacaaaataccVg\n\ +aaDgtVgDcStctatatttattaaaaRKDatagaaagaKaaYYcaYSgKStctccSttac\n\ +agtcNactttDVttagaaagMHttRaNcSaRaMgBttattggtttaRMggatggcKDgWR\n\ +tNaataataWKKacttcKWaaagNaBttaBatMHtccattaacttccccYtcBcYRtaga\n\ +ttaagctaaYBDttaNtgaaaccHcaRMtKtaaHMcNBttaNaNcVcgVttWNtDaBatg\n\ +ataaVtcWKcttRggWatcattgaRagHgaattNtatttctctattaattaatgaDaaMa\n\ +tacgttgggcHaYVaaNaDDttHtcaaHtcVVDgBVagcMacgtgttaaBRNtatRtcag\n\ +taagaggtttaagacaVaaggttaWatctccgtVtaDtcDatttccVatgtacNtttccg\n\ +tHttatKgScBatgtVgHtYcWagcaKtaMYaaHgtaattaSaHcgcagtWNaatNccNN\n\ +YcacgVaagaRacttctcattcccRtgtgtaattagcSttaaStWaMtctNNcSMacatt\n\ +ataaactaDgtatWgtagtttaagaaaattgtagtNagtcaataaatttgatMMYactaa\n\ +tatcggBWDtVcYttcDHtVttatacYaRgaMaacaStaatcRttttVtagaDtcacWat\n\ +ttWtgaaaagaaagNRacDtttStVatBaDNtaactatatcBSMcccaSttccggaMatg\n\ +attaaWatKMaBaBatttgataNctgttKtVaagtcagScgaaaDggaWgtgttttKtWt\n\ +atttHaatgtagttcactaaKMagttSYBtKtaYgaactcagagRtatagtVtatcaaaW\n\ +YagcgNtaDagtacNSaaYDgatBgtcgataacYDtaaactacagWDcYKaagtttatta\n\ +gcatcgagttKcatDaattgattatDtcagRtWSKtcgNtMaaaaacaMttKcaWcaaSV\n\ +MaaaccagMVtaMaDtMaHaBgaacataBBVtaatVYaNSWcSgNtDNaaKacacBttta\n\ +tKtgtttcaaHaMctcagtaacgtcgYtactDcgcctaNgagagcYgatattttaaattt\n\ +ccattttacatttDaaRctattttWctttacgtDatYtttcagacgcaaVttagtaaKaa\n\ +aRtgVtccataBggacttatttgtttaWNtgttVWtaWNVDaattgtatttBaagcBtaa\n\ +BttaaVatcHcaVgacattccNggtcgacKttaaaRtagRtctWagaYggtgMtataatM\n\ +tgaaRttattttgWcttNtDRRgMDKacagaaaaggaaaRStcccagtYccVattaNaaK\n\ +StNWtgacaVtagaagcttSaaDtcacaacgDYacWDYtgtttKatcVtgcMaDaSKStV\n\ +cgtagaaWaKaagtttcHaHgMgMtctataagBtKaaaKKcactggagRRttaagaBaaN\n\ +atVVcgRcKSttDaactagtSttSattgttgaaRYatggttVttaataaHttccaagDtg\n\ +atNWtaagHtgcYtaactRgcaatgMgtgtRaatRaNaacHKtagactactggaatttcg\n\ +ccataacgMctRgatgttaccctaHgtgWaYcactcacYaattcttaBtgacttaaacct\n\ +gYgaWatgBttcttVttcgttWttMcNYgtaaaatctYgMgaaattacNgaHgaacDVVM\n\ +tttggtHtctaaRgtacagacgHtVtaBMNBgattagcttaRcttacaHcRctgttcaaD\n\ +BggttKaacatgKtttYataVaNattccgMcgcgtagtRaVVaattaKaatggttRgaMc\n\ +agtatcWBttNtHagctaatctagaaNaaacaYBctatcgcVctBtgcaaagDgttVtga\n\ +HtactSNYtaaNccatgtgDacgaVtDcgKaRtacDcttgctaagggcagMDagggtBWR\n\ +tttSgccttttttaacgtcHctaVtVDtagatcaNMaVtcVacatHctDWNaataRgcgt\n\ +aVHaggtaaaaSgtttMtattDgBtctgatSgtRagagYtctSaKWaataMgattRKtaa\n\ +catttYcgtaacacattRWtBtcggtaaatMtaaacBatttctKagtcDtttgcBtKYYB\n\ +aKttctVttgttaDtgattttcttccacttgSaaacggaaaNDaattcYNNaWcgaaYat\n\ +tttMgcBtcatRtgtaaagatgaWtgaccaYBHgaatagataVVtHtttVgYBtMctaMt\n\ +cctgaDcYttgtccaaaRNtacagcMctKaaaggatttacatgtttaaWSaYaKttBtag\n\ +DacactagctMtttNaKtctttcNcSattNacttggaacaatDagtattRtgSHaataat\n\ +gccVgacccgatactatccctgtRctttgagaSgatcatatcgDcagWaaHSgctYYWta\n\ +tHttggttctttatVattatcgactaagtgtagcatVgtgHMtttgtttcgttaKattcM\n\ +atttgtttWcaaStNatgtHcaaaDtaagBaKBtRgaBgDtSagtatMtaacYaatYtVc\n\ +KatgtgcaacVaaaatactKcRgtaYtgtNgBBNcKtcttaccttKgaRaYcaNKtactt\n\ +tgagSBtgtRagaNgcaaaNcacagtVtttHWatgttaNatBgtttaatNgVtctgaata\n\ +tcaRtattcttttttttRaaKcRStctcggDgKagattaMaaaKtcaHacttaataataK\n\ +taRgDtKVBttttcgtKaggHHcatgttagHggttNctcgtatKKagVagRaaaggaaBt\n\ +NatttVKcRttaHctaHtcaaatgtaggHccaBataNaNaggttgcWaatctgatYcaaa\n\ +HaatWtaVgaaBttagtaagaKKtaaaKtRHatMaDBtBctagcatWtatttgWttVaaa\n\ +ScMNattRactttgtYtttaaaagtaagtMtaMaSttMBtatgaBtttaKtgaatgagYg\n\ +tNNacMtcNRacMMHcttWtgtRtctttaacaacattattcYaMagBaacYttMatcttK\n\ +cRMtgMNccattaRttNatHaHNaSaaHMacacaVaatacaKaSttHatattMtVatWga\n\ +ttttttaYctttKttHgScWaacgHtttcaVaaMgaacagNatcgttaacaaaaagtaca\n\ +HBNaattgttKtcttVttaaBtctgctacgBgcWtttcaggacacatMgacatcccagcg\n\ +gMgaVKaBattgacttaatgacacacaaaaaatRKaaBctacgtRaDcgtagcVBaacDS\n\ +BHaaaaSacatatacagacRNatcttNaaVtaaaataHattagtaaaaSWccgtatWatg\n\ +gDttaactattgcccatcttHaSgYataBttBaactattBtcHtgatcaataSttaBtat\n\ +KSHYttWggtcYtttBttaataccRgVatStaHaKagaatNtagRMNgtcttYaaSaact\n\ +cagDSgagaaYtMttDtMRVgWKWtgMaKtKaDttttgactatacataatcNtatNaHat\n\ +tVagacgYgatatatttttgtStWaaatctWaMgagaRttRatacgStgattcttaagaD\n\ +taWccaaatRcagcagaaNKagtaaDggcgccBtYtagSBMtactaaataMataBSacRM\n\ +gDgattMMgtcHtcaYDtRaDaacggttDaggcMtttatgttaNctaattaVacgaaMMt\n\ +aatDccSgtattgaRtWWaccaccgagtactMcgVNgctDctaMScatagcgtcaactat\n\ +acRacgHRttgctatttaatgaattataYKttgtaagWgtYttgcHgMtaMattWaWVta\n\ +RgcttgYgttBHtYataSccStBtgtagMgtDtggcVaaSBaatagDttgBgtctttctc\n\ +attttaNagtHKtaMWcYactVcgcgtatMVtttRacVagDaatcttgctBBcRDgcaac\n\ +KttgatSKtYtagBMagaRtcgBattHcBWcaactgatttaatttWDccatttatcgagS\n\ +KaWttataHactaHMttaatHtggaHtHagaatgtKtaaRactgtttMatacgatcaagD\n\ +gatKaDctataMggtHDtggHacctttRtatcttYattttgacttgaaSaataaatYcgB\n\ +aaaaccgNatVBttMacHaKaataagtatKgtcaagactcttaHttcggaattgttDtct\n\ +aaccHttttWaaatgaaatataaaWattccYDtKtaaaacggtgaggWVtctattagtga\n\ +ctattaagtMgtttaagcatttgSgaaatatccHaaggMaaaattttcWtatKctagDtY\n\ +tMcctagagHcactttactatacaaacattaacttaHatcVMYattYgVgtMttaaRtga\n\ +aataaDatcaHgtHHatKcDYaatcttMtNcgatYatgSaMaNtcttKcWataScKggta\n\ +tcttacgcttWaaagNatgMgHtctttNtaacVtgttcMaaRatccggggactcMtttaY\n\ +MtcWRgNctgNccKatcttgYDcMgattNYaRagatHaaHgKctcataRDttacatBatc\n\ +cattgDWttatttaWgtcggagaaaaatacaatacSNtgggtttccttacSMaagBatta\n\ +caMaNcactMttatgaRBacYcYtcaaaWtagctSaacttWgDMHgaggatgBVgcHaDt\n\ +ggaactttggtcNatNgtaKaBcccaNtaagttBaacagtatacDYttcctNgWgcgSMc\n\ +acatStctHatgRcNcgtacacaatRttMggaNKKggataaaSaYcMVcMgtaMaHtgat\n\ +tYMatYcggtcttcctHtcDccgtgRatcattgcgccgatatMaaYaataaYSggatagc\n\ +gcBtNtaaaScaKgttBgagVagttaKagagtatVaactaSacWactSaKatWccaKaaa\n\ +atBKgaaKtDMattttgtaaatcRctMatcaaMagMttDgVatggMaaWgttcgaWatga\n\ +aatttgRtYtattaWHKcRgctacatKttctaccaaHttRatctaYattaaWatVNccat\n\ +NgagtcKttKataStRaatatattcctRWatDctVagttYDgSBaatYgttttgtVaatt\n\ +taatagcagMatRaacttBctattgtMagagattaaactaMatVtHtaaatctRgaaaaa\n\ +aaatttWacaacaYccYDSaattMatgaccKtaBKWBattgtcaagcHKaagttMMtaat\n\ +ttcKcMagNaaKagattggMagaggtaatttYacatcWaaDgatMgKHacMacgcVaaca\n\ +DtaDatatYggttBcgtatgWgaSatttgtagaHYRVacaRtctHaaRtatgaactaata\n\ +tctSSBgggaaHMWtcaagatKgagtDaSatagttgattVRatNtctMtcSaagaSHaat\n\ +aNataataRaaRgattctttaataaagWaRHcYgcatgtWRcttgaaggaMcaataBRaa\n\ +ccagStaaacNtttcaatataYtaatatgHaDgcStcWttaacctaRgtYaRtataKtgM\n\ +ttttatgactaaaatttacYatcccRWtttHRtattaaatgtttatatttgttYaatMca\n\ +RcSVaaDatcgtaYMcatgtagacatgaaattgRtcaaYaaYtRBatKacttataccaNa\n\ +aattVaBtctggacaagKaaYaaatatWtMtatcYaaVNtcgHaactBaagKcHgtctac\n\ +aatWtaDtSgtaHcataHtactgataNctRgttMtDcDttatHtcgtacatcccaggStt\n\ +aBgtcacacWtccNMcNatMVaVgtccDYStatMaccDatggYaRKaaagataRatttHK\n\ +tSaaatDgataaacttaHgttgVBtcttVttHgDacgaKatgtatatNYataactctSat\n\ +atatattgcHRRYttStggaactHgttttYtttaWtatMcttttctatctDtagVHYgMR\n\ +BgtHttcctaatYRttKtaagatggaVRataKDctaMtKBNtMtHNtWtttYcVtattMc\n\ +gRaacMcctNSctcatttaaagDcaHtYccSgatgcaatYaaaaDcttcgtaWtaattct\n\ +cgttttScttggtaatctttYgtctaactKataHacctMctcttacHtKataacacagcN\n\ +RatgKatttttSaaatRYcgDttaMRcgaaattactMtgcgtaagcgttatBtttttaat\n\ +taagtNacatHgttcRgacKcBBtVgatKttcgaBaatactDRgtRtgaNacWtcacYtt\n\ +aaKcgttctHaKttaNaMgWgWaggtctRgaKgWttSttBtDcNtgtttacaaatYcDRt\n\ +gVtgcctattcNtctaaaDMNttttNtggctgagaVctDaacVtWccaagtaacacaNct\n\ +gaScattccDHcVBatcgatgtMtaatBgHaatDctMYgagaatgYWKcctaatNaStHa\n\ +aaKccgHgcgtYaaYtattgtStgtgcaaRtattaKatattagaWVtcaMtBagttatta\n\ +gNaWHcVgcaattttDcMtgtaRHVYtHtctgtaaaaHVtMKacatcgNaatttMatatg\n\ +ttgttactagWYtaRacgataKagYNKcattataNaRtgaacKaYgcaaYYacaNccHat\n\ +MatDcNgtHttRaWttagaaDcaaaaaatagggtKDtStaDaRtaVtHWKNtgtattVct\n\ +SVgRgataDaRaWataBgaagaaKtaataaYgDcaStaNgtaDaaggtattHaRaWMYaY\n\ +aWtggttHYgagVtgtgcttttcaaDKcagVcgttagacNaaWtagtaataDttctggtt\n\ +VcatcataaagtgKaaaNaMtaBBaattaatWaattgctHaVKaSgDaaVKaHtatatat\n\ +HatcatSBagNgHtatcHYMHgttDgtaHtBttWatcgtttaRaattgStKgSKNWKatc\n\ +agDtctcagatttctRtYtBatBgHHtKaWtgYBgacVVWaKtacKcDttKMaKaVcggt\n\ +gttataagaataaHaatattagtataatMHgttYgaRttagtaRtcaaVatacggtcMcg\n\ +agtaaRttacWgactKRYataaaagSattYaWgagatYagKagatgSaagKgttaatMgg\n\ +tataatgttWYttatgagaaacctNVataatHcccKtDctcctaatactggctHggaSag\n\ +gRtKHaWaattcgSatMatttagaggcYtctaMcgctcataSatatgRagacNaaDagga\n\ +VBagaYttKtacNaKgtSYtagttggaWcatcWttaatctatgaVtcgtgtMtatcaYcg\n\ +tRccaaYgDctgcMgtgtWgacWtgataacacgcgctBtgttaKtYDtatDcatcagKaV\n\ +MctaatcttgVcaaRgcRMtDcgattaHttcaNatgaatMtactacVgtRgatggaWttt\n\ +actaaKatgagSaaKggtaNtactVaYtaaKRagaacccacaMtaaMtKtatBcttgtaa\n\ +WBtMctaataaVcDaaYtcRHBtcgttNtaaHatttBNgRStVDattBatVtaagttaYa\n\ +tVattaagaBcacggtSgtVtatttaRattgatgtaHDKgcaatattKtggcctatgaWD\n\ +KRYcggattgRctatNgatacaatMNttctgtcRBYRaaaHctNYattcHtaWcaattct\n\ +BtMKtVgYataatMgYtcagcttMDataVtggRtKtgaatgccNcRttcaMtRgattaac\n\ +attRcagcctHtWMtgtDRagaKaBtgDttYaaaaKatKgatctVaaYaacWcgcatagB\n\ +VtaNtRtYRaggBaaBtgKgttacataagagcatgtRattccacttaccatRaaatgWgD\n\ +aMHaYVgVtaSctatcgKaatatattaDgacccYagtgtaYNaaatKcagtBRgagtcca\n\ +tgKgaaaccBgaagBtgSttWtacgatWHaYatcgatttRaaNRgcaNaKVacaNtDgat\n\ +tgHVaatcDaagcgtatgcNttaDataatcSataaKcaataaHWataBtttatBtcaKtK\n\ +tatagttaDgSaYctacaRatNtaWctSaatatttYaKaKtaccWtatcRagacttaYtt\n\ +VcKgSDcgagaagatccHtaattctSttatggtKYgtMaHagVaBRatttctgtRgtcta\n\ +tgggtaHKgtHacHtSYacgtacacHatacKaaBaVaccaDtatcSaataaHaagagaat\n\ +ScagactataaRttagcaaVcaHataKgDacatWccccaagcaBgagWatctaYttgaaa\n\ +tctVNcYtttWagHcgcgcDcVaaatgttKcHtNtcaatagtgtNRaactttttcaatgg\n\ +WgBcgDtgVgtttctacMtaaataaaRggaaacWaHttaRtNtgctaaRRtVBctYtVta\n\ +tDcattDtgaccYatagatYRKatNYKttNgcctagtaWtgaactaMVaacctgaStttc\n\ +tgaKVtaaVaRKDttVtVctaDNtataaaDtccccaagtWtcgatcactDgYaBcatcct\n\ +MtVtacDaaBtYtMaKNatNtcaNacgDatYcatcgcaRatWBgaacWttKttagYtaat\n\ +tcggttgSWttttDWctttacYtatatWtcatDtMgtBttgRtVDggttaacYtacgtac\n\ +atgaattgaaWcttMStaDgtatattgaDtcRBcattSgaaVBRgagccaaKtttcDgcg\n\ +aSMtatgWattaKttWtgDBMaggBBttBaatWttRtgcNtHcgttttHtKtcWtagHSt\n\ +aacagttgatatBtaWSaWggtaataaMttaKacDaatactcBttcaatatHttcBaaSa\n\ +aatYggtaRtatNtHcaatcaHtagVtgtattataNggaMtcttHtNagctaaaggtaga\n\ +YctMattNaMVNtcKtactBKcaHHcBttaSagaKacataYgctaKaYgttYcgacWVtt\n\ +WtSagcaacatcccHaccKtcttaacgaKttcacKtNtacHtatatRtaaatacactaBt\n\ +ttgaHaRttggttWtatYagcatYDatcggagagcWBataagRtacctataRKgtBgatg\n\ +aDatataSttagBaHtaatNtaDWcWtgtaattacagKttcNtMagtattaNgtctcgtc\n\ +ctcttBaHaKcKccgtRcaaYagSattaagtKataDatatatagtcDtaacaWHcaKttD\n\ +gaaRcgtgYttgtcatatNtatttttatggccHtgDtYHtWgttatYaacaattcaWtat\n\ +NgctcaaaSttRgctaatcaaatNatcgtttaBtNNVtgttataagcaaagattBacgtD\n\ +atttNatttaaaDcBgtaSKgacgtagataatttcHMVNttgttBtDtgtaWKaaRMcKM\n\ +tHtaVtagataWctccNNaSWtVaHatctcMgggDgtNHtDaDttatatVWttgttattt\n\ +aacctttcacaaggaSaDcggttttttatatVtctgVtaacaStDVaKactaMtttaSNa\n\ +gtgaaattaNacttSKctattcctctaSagKcaVttaagNaVcttaVaaRNaHaaHttat\n\ +gtHttgtgatMccaggtaDcgaccgtWgtWMtttaHcRtattgScctatttKtaaccaag\n\ +tYagaHgtWcHaatgccKNRtttagtMYSgaDatctgtgaWDtccMNcgHgcaaacNDaa\n\ +aRaStDWtcaaaaHKtaNBctagBtgtattaactaattttVctagaatggcWSatMaccc\n\ +ttHttaSgSgtgMRcatRVKtatctgaaaccDNatYgaaVHNgatMgHRtacttaaaRta\n\ +tStRtDtatDttYatattHggaBcttHgcgattgaKcKtttcRataMtcgaVttWacatN\n\ +catacctRataDDatVaWNcggttgaHtgtMacVtttaBHtgagVttMaataattatgtt\n\ +cttagtttgtgcDtSatttgBtcaacHattaaBagVWcgcaSYttMgcttacYKtVtatc\n\ +aYaKctgBatgcgggcYcaaaaacgNtctagKBtattatctttKtaVttatagtaYtRag\n\ +NtaYataaVtgaatatcHgcaaRataHtacacatgtaNtgtcgYatWMatttgaactacR\n\ +ctaWtWtatacaatctBatatgYtaagtatgtgtatSttactVatcttYtaBcKgRaSgg\n\ +RaaaaatgcagtaaaWgtaRgcgataatcBaataccgtatttttccatcNHtatWYgatH\n\ +SaaaDHttgctgtccHtggggcctaataatttttctatattYWtcattBtgBRcVttaVM\n\ +RSgctaatMagtYtttaaaaatBRtcBttcaaVtaacagctccSaaSttKNtHtKYcagc\n\ +agaaaccccRtttttaaDcDtaStatccaagcgctHtatcttaDRYgatDHtWcaaaBcW\n\ +gKWHttHataagHacgMNKttMKHccaYcatMVaacgttaKgYcaVaaBtacgcaacttt\n\ +MctaaHaatgtBatgagaSatgtatgSRgHgWaVWgataaatatttccKagVgataattW\n\ +aHNcYggaaatgctHtKtaDtctaaagtMaatVDVactWtSaaWaaMtaHtaSKtcBRaN\n\ +cttStggtBttacNagcatagRgtKtgcgaacaacBcgKaatgataagatgaaaattgta\n\ +ctgcgggtccHHWHaaNacaBttNKtKtcaaBatatgctaHNgtKcDWgtttatNgVDHg\n\ +accaacWctKaaggHttgaRgYaatHcaBacaatgagcaaattactgtaVaaYaDtagat\n\ +tgagNKggtggtgKtWKaatacagDRtatRaMRtgattDggtcaaYRtatttNtagaDtc\n\ +acaaSDctDtataatcgtactaHttatacaatYaacaaHttHatHtgcgatRRttNgcat\n\ +SVtacWWgaaggagtatVMaVaaattScDDKNcaYBYaDatHgtctatBagcaacaagaa\n\ +tgagaaRcataaKNaRtBDatcaaacgcattttttaaBtcSgtacaRggatgtMNaattg\n\ +gatatWtgagtattaaaVctgcaYMtatgatttttYgaHtgtcttaagWBttHttgtctt\n\ +attDtcgtatWtataataSgctaHagcDVcNtaatcaagtaBDaWaDgtttagYctaNcc\n\ +DtaKtaHcttaataacccaRKtacaVaatNgcWRaMgaattatgaBaaagattVYaHMDc\n\ +aDHtcRcgYtcttaaaWaaaVKgatacRtttRRKYgaatacaWVacVcRtatMacaBtac\n\ +tggMataaattttHggNagSctacHgtBagcgtcgtgattNtttgatSaaggMttctttc\n\ +ttNtYNagBtaaacaaatttMgaccttacataattgYtcgacBtVMctgStgMDtagtaR\n\ +ctHtatgttcatatVRNWataDKatWcgaaaaagttaaaagcacgHNacgtaatctttMR\n\ +tgacttttDacctataaacgaaatatgattagaactccSYtaBctttaataacWgaaaYa\n\ +tagatgWttcatKtNgatttttcaagHtaYgaaRaDaagtaggagcttatVtagtctttc\n\ +attaaaatcgKtattaRttacagVaDatgcatVgattgggtctttHVtagKaaRBtaHta\n\ +aggccccaaaaKatggtttaMWgtBtaaacttcactttKHtcgatctccctaYaBacMgt\n\ +cttBaBaNgcgaaacaatctagtHccHtKttcRtRVttccVctttcatacYagMVtMcag\n\ +aMaaacaataBctgYtaatRaaagattaaccatVRatHtaRagcgcaBcgDttStttttc\n\ +VtttaDtKgcaaWaaaaatSccMcVatgtKgtaKgcgatatgtagtSaaaDttatacaaa\n\ +catYaRRcVRHctKtcgacKttaaVctaDaatgttMggRcWaacttttHaDaKaDaBctg\n\ +taggcgtttaHBccatccattcNHtDaYtaataMttacggctNVaacDattgatatttta\n\ +cVttSaattacaaRtataNDgacVtgaacataVRttttaDtcaaacataYDBtttaatBa\n\ +DtttYDaDaMccMttNBttatatgagaaMgaNtattHccNataattcaHagtgaaggDga\n\ +tgtatatatgYatgaStcataaBStWacgtcccataRMaaDattggttaaattcMKtctM\n\ +acaBSactcggaatDDgatDgcWctaacaccgggaVcacWKVacggtaNatatacctMta\n\ +tgatagtgcaKagggVaDtgtaacttggagtcKatatcgMcttRaMagcattaBRaStct\n\ +YSggaHYtacaactMBaagDcaBDRaaacMYacaHaattagcattaaaHgcgctaaggSc\n\ +cKtgaaKtNaBtatDDcKBSaVtgatVYaagVtctSgMctacgttaacWaaattctSgtD\n\ +actaaStaaattgcagBBRVctaatatacctNttMcRggctttMttagacRaHcaBaacV\n\ +KgaataHttttMgYgattcYaNRgttMgcVaaacaVVcDHaatttgKtMYgtatBtVVct\n\ +WgVtatHtacaaHttcacgatagcagtaaNattBatatatttcVgaDagcggttMaagtc\n\ +ScHagaaatgcYNggcgtttttMtStggtRatctacttaaatVVtBacttHNttttaRca\n\ +aatcacagHgagagtMgatcSWaNRacagDtatactaaDKaSRtgattctccatSaaRtt\n\ +aaYctacacNtaRtaactggatgaccYtacactttaattaattgattYgttcagDtNKtt\n\ +agDttaaaaaaaBtttaaNaYWKMBaaaacVcBMtatWtgBatatgaacVtattMtYatM\n\ +NYDKNcKgDttDaVtaaaatgggatttctgtaaatWtctcWgtVVagtcgRgacttcccc\n\ +taDcacagcRcagagtgtWSatgtacatgttaaSttgtaaHcgatgggMagtgaacttat\n\ +RtttaVcaccaWaMgtactaatSSaHtcMgaaYtatcgaaggYgggcgtgaNDtgttMNg\n\ +aNDMtaattcgVttttaacatgVatgtWVMatatcaKgaaattcaBcctccWcttgaaWH\n\ +tWgHtcgNWgaRgctcBgSgaattgcaaHtgattgtgNagtDttHHgBttaaWcaaWagc\n\ +aSaHHtaaaVctRaaMagtaDaatHtDMtcVaWMtagSagcttHSattaacaaagtRacM\n\ +tRtctgttagcMtcaBatVKtKtKacgagaSNatSactgtatatcBctgagVtYactgta\n\ +aattaaaggcYgDHgtaacatSRDatMMccHatKgttaacgactKtgKagtcttcaaHRV\n\ +tccttKgtSataatttacaactggatDNgaacttcaRtVaagDcaWatcBctctHYatHa\n\ +DaaatttagYatSatccaWtttagaaatVaacBatHcatcgtacaatatcgcNYRcaata\n\ +YaRaYtgattVttgaatgaVaactcRcaNStgtgtattMtgaggtNttBaDRcgaaaagc\n\ +tNgBcWaWgtSaDcVtgVaatMKBtttcgtttctaaHctaaagYactgMtatBDtcStga\n\ +ccgtSDattYaataHctgggaYYttcggttaWaatctggtRagWMaDagtaacBccacta\n\ +cgHWMKaatgatWatcctgHcaBaSctVtcMtgtDttacctaVgatYcWaDRaaaaRtag\n\ +atcgaMagtggaRaWctctgMgcWttaagKBRtaaDaaWtctgtaagYMttactaHtaat\n\ +cttcataacggcacBtSgcgttNHtgtHccatgttttaaagtatcgaKtMttVcataYBB\n\ +aKtaMVaVgtattNDSataHcagtWMtaggtaSaaKgttgBtVtttgttatcatKcgHac\n\ +acRtctHatNVagSBgatgHtgaRaSgttRcctaacaaattDNttgacctaaYtBgaaaa\n\ +tagttattactcttttgatgtNNtVtgtatMgtcttRttcatttgatgacacttcHSaaa\n\ +ccaWWDtWagtaRDDVNacVaRatgttBccttaatHtgtaaacStcVNtcacaSRttcYa\n\ +gacagaMMttttgMcNttBcgWBtactgVtaRttctccaaYHBtaaagaBattaYacgat\n\ +ttacatctgtaaMKaRYtttttactaaVatWgctBtttDVttctggcDaHaggDaagtcg\n\ +aWcaagtagtWttHtgKtVataStccaMcWcaagataagatcactctHatgtcYgaKcat\n\ +cagatactaagNSStHcctRRNtattgtccttagttagMVgtatagactaactctVcaat\n\ +MctgtttgtgttgccttatWgtaBVtttctggMcaaKgDWtcgtaaYStgSactatttHg\n\ +atctgKagtagBtVacRaagRtMctatgggcaaaKaaaatacttcHctaRtgtDcttDat\n\ +taggaaatttcYHaRaaBttaatggcacKtgctHVcaDcaaaVDaaaVcgMttgtNagcg\n\ +taDWgtcgttaatDgKgagcSatatcSHtagtagttggtgtHaWtaHKtatagctgtVga\n\ +ttaBVaatgaataagtaatVatSttaHctttKtttgtagttaccttaatcgtagtcctgB\n\ +cgactatttVcMacHaaaggaatgDatggKtaHtgStatattaaSagctWcctccRtata\n\ +BaDYcgttgcNaagaggatRaaaYtaWgNtSMcaatttactaacatttaaWttHtatBat\n\ +tgtcgacaatNgattgcNgtMaaaKaBDattHacttggtRtttaYaacgVactBtaBaKt\n\ +gBttatgVttgtVttcaatcWcNctDBaaBgaDHacBttattNtgtDtatttVSaaacag\n\ +gatgcRatSgtaSaNtgBatagttcHBgcBBaaattaHgtDattatDaKaatBaaYaaMa\n\ +ataaataKtttYtagtBgMatNcatgtttgaNagtgttgtgKaNaSagtttgaSMaYBca\n\ +aaacDStagttVacaaaaactaaWttBaagtctgtgcgtMgtaattctcctacctcaNtt\n\ +taaccaaaaVtBcacataacaccccBcWMtatVtggaatgaWtcaaWaaaaaaaaWtDta\n\ +atatRcctDWtcctaccMtVVatKttaWaaKaaatataaagScHBagaggBaSMtaWaVt\n\ +atattactSaaaKNaactatNatccttgaYctattcaaaVgatttYHcRagattttaSat\n\ +aggttattcVtaaagaKgtattattKtRttNcggcRgtgtgtWYtaacHgKatKgatYta\n\ +cYagDtWcHBDctctgRaYKaYagcactKcacSaRtBttttBHKcMtNtcBatttatttt\n\ +tgSatVgaaagaWtcDtagDatatgMacaacRgatatatgtttgtKtNRaatatNatgYc\n\ +aHtgHataacKtgagtagtaacYttaNccaaatHcacaacaVDtagtaYtccagcattNt\n\ +acKtBtactaaagaBatVtKaaHBctgStgtBgtatgaSNtgDataaccctgtagcaBgt\n\ +gatcttaDataStgaMaccaSBBgWagtacKcgattgaDgNNaaaacacagtSatBacKD\n\ +gcgtataBKcatacactaSaatYtYcDaactHttcatRtttaatcaattataRtttgtaa\n\ +gMcgNttcatcBtYBagtNWNMtSHcattcRctttttRWgaKacKttgggagBcgttcgc\n\ +MaWHtaatactgtctctatttataVgtttaBScttttaBMaNaatMacactYtBMggtHa\n\ +cMagtaRtctgcatttaHtcaaaatttgagKtgNtactBacaHtcgtatttctMaSRagc\n\ +agttaatgtNtaaattgagagWcKtaNttagVtacgatttgaatttcgRtgtWcVatcgt\n\ +taaDVctgtttBWgaccagaaagtcSgtVtatagaBccttttcctaaattgHtatcggRa\n\ +ttttcaaggcYSKaagWaWtRactaaaacccBatMtttBaatYtaagaactSttcgaaSc\n\ +aatagtattgaccaagtgttttctaacatgtttNVaatcaaagagaaaNattaaRtttta\n\ +VaaaccgcaggNMtatattVctcaagaggaacgBgtttaacaagttcKcYaatatactaa\n\ +ccBaaaSggttcNtattctagttRtBacgScVctcaatttaatYtaaaaaaatgSaatga\n\ +tagaMBRatgRcMcgttgaWHtcaVYgaatYtaatctttYttatRaWtctgBtDcgatNa\n\ +tcKaBaDgatgtaNatWKctccgatattaacattNaaacDatgBgttctgtDtaaaMggt\n\ +gaBaSHataacgccSctaBtttaRBtcNHcDatcDcctagagtcRtaBgWttDRVHagat\n\ +tYatgtatcWtaHtttYcattWtaaagtctNgtStggRNcgcggagSSaaagaaaatYcH\n\ +DtcgctttaatgYcKBVSgtattRaYBaDaaatBgtatgaHtaaRaRgcaSWNtagatHa\n\ +acttNctBtcaccatctMcatattccaSatttgcgaDagDgtatYtaaaVDtaagtttWV\n\ +aagtagYatRttaagDcNgacKBcScagHtattatcDaDactaaaaaYgHttBcgaDttg\n\ +gataaaKSRcBMaBcgaBSttcWtgNBatRaccgattcatttataacggHVtaattcaca\n\ +agagVttaaRaatVVRKcgWtVgacctgDgYaaHaWtctttcacMagggatVgactagMa\n\ +aataKaaNWagKatagNaaWtaaaatttgaattttatttgctaaVgaHatBatcaaBWcB\n\ +gttcMatcgBaaNgttcgSNaggSaRtttgHtRtattaNttcDcatSaVttttcgaaaaa\n\ +ttgHatctaRaggSaNatMDaaatDcacgattttagaHgHaWtYgattaatHNSttatMS\n\ +gggNtcKtYatRggtttgtMWVtttaYtagcagBagHaYagttatatggtBacYcattaR\n\ +SataBatMtttaaatctHcaaaSaaaagttNSaaWcWRccRtKaagtBWtcaaattSttM\n\ +tattggaaaccttaacgttBtWatttatatWcDaatagattcctScacctaagggRaaYt\n\ +aNaatgVtBcttaaBaacaMVaaattatStYgRcctgtactatcMcVKatttcgSgatRH\n\ +MaaaHtagtaaHtVgcaaataatatcgKKtgccaatBNgaaWcVttgagttaKatagttc\n\ +aggKDatDtattgaKaVcaKtaataDataataHSaHcattagttaatRVYcNaHtaRcaa\n\ +ggtNHcgtcaaccaBaaagYtHWaaaRcKgaYaaDttgcWYtataRgaatatgtYtgcKt\n\ +aNttWacatYHctRaDtYtattcBttttatcSataYaYgttWaRagcacHMgtttHtYtt\n\ +YaatcggtatStttcgtRSattaaDaKMaatatactaNBaWgctacacYtgaYVgtgHta\n\ +aaRaaRgHtagtWattataaaSDaaWtgMattatcgaaaagtaYRSaWtSgNtBgagcRY\n\ +aMDtactaacttaWgtatctagacaagNtattHggataatYttYatcataDcgHgttBtt\n\ +ctttVttgccgaaWtaaaacgKgtatctaaaaaNtccDtaDatBMaMggaatNKtatBaa\n\ +atVtccRaHtaSacataHattgtttKVYattcataVaattWtcgtgMttcttKtgtctaa\n\ +cVtatctatatBRataactcgKatStatattcatHHRttKtccaacgtgggtgRgtgaMt\n\ +attattggctatcgtgacMtRcBDtcttgtactaatRHttttaagatcgVMDStattatY\n\ +BtttDttgtBtNttgRcMtYtgBacHaWaBaatDKctaagtgaaactaatgRaaKgatcc\n\ +aagNaaaatattaggWNtaagtatacttttKcgtcggSYtcttgRctataYcttatataa\n\ +agtatattaatttataVaacacaDHatctatttttKYVatHRactttaBHccaWagtact\n\ +BtcacgaVgcgttRtttttttSVgtSagtBaaattctgaHgactcttgMcattttagVta\n\ +agaattHctHtcaDaaNtaacRggWatagttcgtSttgaDatcNgNagctagDgatcNtt\n\ +KgttgtaDtctttRaaYStRatDtgMggactSttaDtagSaVtBDttgtDgccatcacaM\n\ +attaaaMtNacaVcgSWcVaaDatcaHaatgaattaMtatccVtctBtaattgtWattat\n\ +BRcWcaatgNNtactWYtDaKttaaatcactcagtRaaRgatggtKgcgccaaHgaggat\n\ +StattYcaNMtcaBttacttatgagDaNtaMgaaWtgtttcttctaHtMNgttatctaWW\n\ +atMtBtaaatagDVatgtBYtatcggcttaagacMRtaHScgatatYgRDtcattatSDa\n\ +HggaaataNgaWSRRaaaBaatagBattaDctttgHWNttacaataaaaaaatacggttt\n\ +gHgVtaHtWMttNtBtctagtMcgKMgHgYtataHaNagWtcaacYattaataYRgtaWK\n\ +gaBctataaccgatttaHaNBRaRaMtccggtNgacMtctcatttgcaattcWgMactta\n\ +caaDaaNtactWatVtttagccttMaatcagVaagtctVaaDaBtattaattaYtNaYtg\n\ +gattaKtaKctYaMtattYgatattataatKtVgDcttatatNBtcgttgtStttttMag\n\ +aggttaHYSttcKgtcKtDNtataagttataagSgttatDtRttattgttttSNggRtca\n\ +aKMNatgaatattgtBWtaMacctgggYgaSgaagYataagattacgagaatBtggtRcV\n\ +HtgYggaDgaYaKagWagctatagacgaaHgtWaNgacttHRatVaWacKYtgRVNgVcS\n\ +gRWctacatcKSactctgWYtBggtataagcttNRttVtgRcaWaaatDMatYattaact\n\ +ttcgaagRatSctgccttgcRKaccHtttSNVagtagHagBagttagaccaRtataBcca\n\ +taatSHatRtcHagacBWatagcaMtacaRtgtgaaBatctKRtScttccaNaatcNgta\n\ +atatWtcaMgactctBtWtaaNactHaaaaRctcgcatggctMcaaNtcagaaaaacaca\n\ +gtggggWttRttagtaagaVctVMtcgaatcttcMaaaHcaHBttcgattatgtcaDagc\n\ +YRtBtYcgacMgtDcagcgaNgttaataatagcagKYYtcgtaBtYctMaRtaRtDagaa\n\ +aacacatgYaBttgattattcgaaNttBctSataaMataWRgaHtttccgtDgaYtatgg\n\ +tDgHKgMtatttVtMtVagttaRatMattRagataaccctKctMtSttgaHagtcStcta\n\ +tttccSagatgttccacgaggYNttHRacgattcDatatDcataaaatBBttatcgaHtN\n\ +HaaatatDNaggctgaNcaaggagttBttMgRagVatBcRtaWgatgBtSgaKtcgHttt\n\ +gaatcaaDaHttcSBgHcagtVaaSttDcagccgttNBtgttHagYtattctttRWaaVt\n\ +SttcatatKaaRaaaNacaVtVctMtSDtDtRHRcgtaatgctcttaaatSacacaatcg\n\ +HattcaWcttaaaatHaaatcNctWttaNMcMtaKctVtcctaagYgatgatcYaaaRac\n\ +tctaRDaYagtaacgtDgaggaaatctcaaacatcaScttcKttNtaccatNtaNataca\n\ +tttHaaDHgcaDatMWaaBttcRggctMaagctVYcacgatcaDttatYtaatcKatWat\n\ +caatVYtNagatttgattgaYttttYgacttVtcKaRagaaaHVgDtaMatKYagagttN\n\ +atWttaccNtYtcDWgSatgaRgtMatgKtcgacaagWtacttaagtcgKtgatccttNc\n\ +ttatagMatHVggtagcgHctatagccctYttggtaattKNaacgaaYatatVctaataM\n\ +aaaYtgVtcKaYtaataacagaatHcacVagatYWHttagaaSMaatWtYtgtaaagNaa\n\ +acaVgaWtcacNWgataNttcaSagctMDaRttgNactaccgataMaaatgtttattDtc\n\ +aagacgctDHYYatggttcaagccNctccttcMctttagacBtaaWtaWVHggaaaaNat\n\ +ttaDtDtgctaaHHtMtatNtMtagtcatttgcaaaRatacagRHtatDNtgtDgaatVg\n\ +tVNtcaaatYBMaaaagcaKgtgatgatMgWWMaHttttMgMagatDtataaattaacca\n\ +actMtacataaattgRataatacgBtKtaataattRgtatDagDtcRDacctatRcagag\n\ +cSHatNtcaScNtttggacNtaaggaccgtgKNttgttNcttgaaRgYgRtNtcagttBc\n\ +ttttcHtKtgcttYaaNgYagtaaatgaatggWaMattBHtatctatSgtcYtgcHtaat\n\ +tHgaaMtHcagaaSatggtatgccaHBtYtcNattWtgtNgctttaggtttgtWatNtgH\n\ +tgcDttactttttttgcNtactKtWRaVcttcatagtgSNKaNccgaataaBttataata\n\ +YtSagctttaaatSttggctaaKSaatRccgWHgagDttaaatcatgagMtcgagtVtaD\n\ +ggaBtatttgDacataaacgtagYRagBWtgDStKDgatgaagttcattatttaKWcata\n\ +aatWRgatataRgttRacaaNKttNtKagaaYaStaactScattattaacgatttaaatg\n\ +DtaattagatHgaYataaactatggggatVHtgccgtNgatNYcaStRtagaccacWcaM\n\ +tatRagHgVactYtWHtcttcatgatWgagaKggagtatgaWtDtVtNaNtcgYYgtaaa\n\ +ctttaDtBactagtaDctatagtaatatttatatataacgHaaaRagKattSagttYtSt\n\ +>THREE Homo sapiens frequency\n\ +agagagacgatgaaaattaatcgtcaatacgctggcgaacactgagggggacccaatgct\n\ +cttctcggtctaaaaaggaatgtgtcagaaattggtcagttcaaaagtagaccggatctt\n\ +tgcggagaacaattcacggaacgtagcgttgggaaatatcctttctaccacacatcggat\n\ +tttcgccctctcccattatttattgtgttctcacatagaattattgtttagacatccctc\n\ +gttgtatggagagttgcccgagcgtaaaggcataatccatataccgccgggtgagtgacc\n\ +tgaaattgtttttagttgggatttcgctatggattagcttacacgaagagattctaatgg\n\ +tactataggataattataatgctgcgtggcgcagtacaccgttacaaacgtcgttcgcat\n\ +atgtggctaacacggtgaaaatacctacatcgtatttgcaatttcggtcgtttcatagag\n\ +cgcattgaattactcaaaaattatatatgttgattatttgattagactgcgtggaaagaa\n\ +ggggtactcaagccatttgtaaaagctgcatctcgcttaagtttgagagcttacattagt\n\ +ctatttcagtcttctaggaaatgtctgtgtgagtggttgtcgtccataggtcactggcat\n\ +atgcgattcatgacatgctaaactaagaaagtagattactattaccggcatgcctaatgc\n\ +gattgcactgctatgaaggtgcggacgtcgcgcccatgtagccctgataataccaatact\n\ +tacatttggtcagcaattctgacattatacctagcacccataaatttactcagacttgag\n\ +gacaggctcttggagtcgatcttctgtttgtatgcatgtgatcatatagatgaataagcg\n\ +atgcgactagttagggcatagtatagatctgtgtatacagttcagctgaacgtccgcgag\n\ +tggaagtacagctgagatctatcctaaaatgcaaccatatcgttcacacatgatatgaac\n\ +ccagggggaaacattgagttcagttaaattggcagcgaatcccccaagaagaaggcggag\n\ +tgacgttgaacgggcttatggtttttcagtacttcctccgtataagttgagcgaaatgta\n\ +aacagaataatcgttgtgttaacaacattaaaatcgcggaatatgatgagaatacacagt\n\ +gtgagcatttcacttgtaaaatatctttggtagaacttactttgctttaaatatgttaaa\n\ +ccgatctaataatctacaaaacggtagattttgcctagcacattgcgtccttctctattc\n\ +agatagaggcaatactcagaaggttttatccaaagcactgtgttgactaacctaagtttt\n\ +agtctaataatcatgattgattataggtgccgtggactacatgactcgtccacaaataat\n\ +acttagcagatcagcaattggccaagcacccgacttttatttaatggttgtgcaatagtc\n\ +cagattcgtattcgggactctttcaaataatagtttcctggcatctaagtaagaaaagct\n\ +cataaggaagcgatattatgacacgctcttccgccgctgttttgaaacttgagtattgct\n\ +cgtccgaaattgagggtcacttcaaaatttactgagaagacgaagatcgactaaagttaa\n\ +aatgctagtccacagttggtcaagttgaattcatccacgagttatatagctattttaatt\n\ +tatagtcgagtgtacaaaaaacatccacaataagatttatcttagaataacaacccccgt\n\ +atcatcgaaatcctccgttatggcctgactcctcgagcttatagcatttgtgctggcgct\n\ +cttgccaggaacttgctcgcgaggtggtgacgagtgagatgatcagtttcattatgatga\n\ +tacgattttatcgcgactagttaatcatcatagcaagtaaaatttgaattatgtcattat\n\ +catgctccattaacaggttatttaattgatactgacgaaattttttcacaatgggttttc\n\ +tagaatttaatatcagtaattgaagccttcataggggtcctactagtatcctacacgacg\n\ +caggtccgcagtatcctggagggacgtgttactgattaaaagggtcaaaggaatgaaggc\n\ +tcacaatgttacctgcttcaccatagtgagccgatgagttttacattagtactaaatccc\n\ +aaatcatactttacgatgaggcttgctagcgctaaagagaatacatacaccaccacatag\n\ +aattgttagcgatgatatcaaatagactcctggaagtgtcagggggaaactgttcaatat\n\ +ttcgtccacaggactgaccaggcatggaaaagactgacgttggaaactataccatctcac\n\ +gcccgacgcttcactaattgatgatccaaaaaatatagcccggattcctgattagcaaag\n\ +ggttcacagagaaagatattatcgacgtatatcccaaaaaacagacgtaatgtgcatctt\n\ +cgaatcgggatgaatacttgtatcataaaaatgtgacctctagtatacaggttaatgtta\n\ +gtgatacacaatactcgtgggccatgggttctcaaataaaatgtaatattgcgtcgatca\n\ +ctcacccacgtatttggtctaattatgttttatttagtgacaatccaatagataaccggt\n\ +cctattaagggctatatttttagcgaccacgcgtttaaacaaaggattgtatgtagatgg\n\ +taccagtttaattgccagtgggcaatcctaagcaaaatgagattctatcctaaagtttgg\n\ +gcttgatataagatttcggatgtatgggttttataatcgttggagagctcaatcatgagc\n\ +taatacatggatttcgctacctcaccgagagaccttgcatgaagaattctaaccaaaagt\n\ +ttaataggccggattggattgagttaattaagaccttgttcagtcatagtaaaaaccctt\n\ +aaattttaccgattgacaaagtgagcagtcgcaataccctatgcgaaacgcctcgatagt\n\ +gactaggtatacaaggtttttgagttcctttgaaatagttaactaatttaaaattaatta\n\ +acgacatggaaatcacagaacctaatgctttgtaggagttatttatgctgtttactgcct\n\ +ctacaaccctaataaagcagtcctaagaatgaaacgcatcttttagttcagaaagtggta\n\ +tccagggtggtcaatttaataaattcaacatcgggtctcaggatattcggtcatataatt\n\ +tattaagggctcttcgagtcttactctgagtgaaattggaaacagtcatccttttcgttg\n\ +tgaggcatcttacaccgctatcgatatacaatgcattccaccgcggtgtcccgtacacaa\n\ +ggaaacttgttaccttggggatataagaaaactcacacgtctcattattaaactgagtac\n\ +aatttttgcacgagaaagtaatgcaatacaatatgatgaaagccagctaatgaaaaggga\n\ +tggaacgcacctcggatctgttgcactggattaaaatccgattatttttaaaaatattca\n\ +gtgctagagcatatcaggtctacttttttatctggtatgtaaagcccacggagcgatagt\n\ +gagatccttacgactcaacgaaaagttataacataactcccgttagccaaagcccaatcc\n\ +cgattactgccctaccctaacgtctgccatctaaatatcgaacttgttatgatcaatgtg\n\ +actacctcccaccctttccccttcatttgttccactggggataagctagcgttttcagaa\n\ +tcaatgcaataagaatagccaattgtctcacttcatcagagctcttggcaattccaggcg\n\ +ctacgtggttctggaatatattcatttttcaaatagtaatacgtttagtgttgctattgt\n\ +ctacacgtttggatattacgttatgtgagcggacatcaatagttgtctaactctttagta\n\ +agccagagatagcactcttagcgaatggataccatcttccataagtttagttaatagtcc\n\ +gaaacaactgcttcgagcatatttgaacctccttgtaggcaaatagcctcttcaaagcaa\n\ +tcttactaatagatagagtttgttttaagggactactagaaatgggacaatcttaatagt\n\ +atgacctaaactgacatttaaagatatatccaggtggcaagcataaagatcattgcgcca\n\ +cctccaccgtgggattacttatcagtcgatatcctatatgctaagtttgcgacggcagaa\n\ +tacaaactaagctgagttgatgctaaccttacctatgataccccattggaccggttaaca\n\ +gccctacttattccaaataaaagaacttttatgctgtagaagctattatagtgatgcctg\n\ +gtaacttcagtatattaaaatgacacacatacgccatatagagctcctggaactttgaat\n\ +aatgagcgaacttcgaagttgaagagcaagaaaccatatgtcacggttgcctaaagcccg\n\ +gtaaccagacatgtgctatcattgatcattatcgaggttttcataaccttgacccattat\n\ +cggctgtgcgcggacaagtacttaaatcactagtttcttcacctgcttatcggtaagaaa\n\ +taaggttggcaaagaatcgcataagacggacgtagagccgcagcgttgtgcgagtccagg\n\ +tgcatgcgcagcaataggattttaaattttgttccatttttaatttagccgtaaggatgt\n\ +ccgtaaatgattgaaaattggattcaatctttgggcctatgctactggaacctgatcgac\n\ +aaaatttcaaacatacgttaactccgaaagaccgtatttttgcggctagaatagtcagtc\n\ +gcttggagccatataccttaccacttaaacgacgtgctcctgtagttgaaatataaacag\n\ +aacacaaagactaccgatcatatcaactgaagatctttgtaactttgaggcgaagcaccc\n\ +tcttcgagacaactaagagtaaagtaccgggcgccgcaaggagtcgattgggaccctaaa\n\ +tcttgacgaattgctaagaggctcagagctaccactgtaatttctctagagcccataata\n\ +aatgaacgatacatccgtaggtagcacctaagggattataatggaagccaaatgcagtta\n\ +ataatattatatactggcgtacacgattcgacggatctctcacatagtgattcacgaccc\n\ +ccccctttgattgacacagcgtcagcattttgcaagaacgatcttctgcatagggtgcgc\n\ +caccgtaaggatgacgtcgaagctacaactgggtataatttaccatgcttccctgatgct\n\ +gagtgcaatacactaagaatgagtttttaccccatatcaccagtatttgttctgttattg\n\ +cgaagaaatggctatgctgagttggcgactaaagtcacccatcctttttattaggtaacc\n\ +ccctcccttaaactaactgatttgctggagctgccctgcatacatatactttatcattta\n\ +tggacgtccgtgacgcttattatccaccatagtcgatatgctacacggattcattaatgg\n\ +atcgtaggagtttaagttatatttactaagatcggtctcggctactatcccgccttaccc\n\ +ggcgctatttacggccatttttaatatattgacggtaattattcctatggtttcgaccgc\n\ +acgtccttggacaagaaagaatggcaaaaaaaatgtaaaagaaaaaaaatattgagtccc\n\ +taccatcatataaaaaatatgtgatgagtaacttgacgaaatgttagtggttattaaaga\n\ +ctatctattacaccttttgttttctgtcgtagtatattaaagtctagaagccttacagga\n\ +aaatcagggttatacagccgatactccgcagcatgaatcatcgaggaggtgtcctaccat\n\ +cgcgccttgtaatcttgtctgtgtatactgtatttagaccttttatacaaagtaaatatc\n\ +tcggctttatgtgattgggaggggcctactcaaacatgatgacttgacctaataatcact\n\ +gtgcgggcgtcttatgactagctattccttgaaatccaccaccaaatggttaatatgtaa\n\ +aaactttgacgatgaaacaaggtgaatgtgtagttactttgtgtaattagctgcgtcgag\n\ +cattgcttgtaaaaccgtcaatcgcacacgttacttccataaaatttctacgaatacacc\n\ +cttcttaaaaaaaacgtaggaattcacgagtttaacaaacgataactgtataaagtggaa\n\ +gtccgaagaaagcagatgcccgaactactcgaagatgtttcgttttcttaaccatagggg\n\ +cttcttaatggcccactacgcacattttgttcaagcccgagagggacatccccattacgg\n\ +gagtattactaaaactgttccgtaatacgttcagcaagggatgaaaaaggccactgctca\n\ +agttattgacgtgggagtattacatcggaagcctgaatcccacactatgatggtctgtac\n\ +aggcctagggactgcgtctagacggtattaccggcttctaatcatacgatcgtgagtctt\n\ +aacgggaagtaaggctcacacctaccccaaaccatttatctatgtaagtataaaattgtg\n\ +cgtaagtgttcaaagtggacaataaagacgtggcaaaaacccccgcacataagccgcttt\n\ +agatttcacaaataccaatgcggttaaaaacatccttgagtcgtacatacaccatactcg\n\ +cgttaaacggatataacagaagataataaatccggatgtggagtcggtgtaactatagaa\n\ +agccaagtgaaataatgcttaccagtcatttagctatacggctttcatttcatgtcaaga\n\ +gggtggagtttgacctgtacagttgatatatcaccgatacttagaactcacctaaagcta\n\ +aaattgctcgcagcgtgtaatccgcatattacaaacaatagatgggattcattatacata\n\ +agacacgatgatctgctttttcaggttgcgagatgttgcctatcgtcaatcgagtcctgc\n\ +cttacaccacttaaacaaaagtattgacagggaacctattttcgaggtattatatagtcc\n\ +agcttgaatatcaatttgacagttaacctagtgaaaatcagtaagaggaaatacgccaca\n\ +ttctccagtgaaattctacgggttatcgtctagtccaactatcaattataactcacgaga\n\ +tataagtaaattctcgtacttggcctgatttttattatactttggatccttagtaaacag\n\ +gaagggagaaaccttcaacgaaaaacactggattttgttttactctcaaagctcttatat\n\ +gacggaaataccctgtcaagtcttaactttattactagactaatgaaatgggcttggggt\n\ +ggccagaatcatagtacaatttagcggatacactattcggactttcctatcggctgtctg\n\ +gttggataagtatggggactaataggctagacatacctatacttaaactatacaggcgtc\n\ +atctatctctgcaactttggagttccctgatgttctcccgccctttgggttcacatcttc\n\ +tataccgacacccctaataacgattagtttgtgggttagagtaaattaatacggttaata\n\ +ttaatgtatcgttgaaaagctggtgtcgccaataaggtaaccggctaggcagagtatatg\n\ +tcacgaagtataactaccctaatgataagctgtaggaataaaattaatgctgtctctaag\n\ +cgaagagatatttccgactctgttttaatgacgaatctcattacttctgacttgcaaatg\n\ +ttcaatatggcacggtttcacggcacctttgtgacgcatataatgaacttagaagattat\n\ +aacgacggaactttatatgataatccgttacgattaaagaatctgttaaatatcataatg\n\ +gcattcagttctagaccgtgcatcatggtaaacttactttctctgcatggcgacatacat\n\ +ttcgctattcaaattcgcgtgtggttacacccactcgcacctttggaatattaagagaag\n\ +atgatcagaaaatccattcgctcaatttttctgacgtacgtctaatttatcctaggagac\n\ +aaatcgttttatgtctctcacatttttgaagaaaggttcgagagacaatactcaggtcct\n\ +gaactgctagaagatactcggtggagcgtggcaacaatgaaaaactcgtgacataaatga\n\ +atgatacttttccaagttcagttaagtgaatatgtttaacatacccggcttttcgatctt\n\ +aagctgacgctggacgtgcgagtaatgtcagtctcttacatacactagtgactccaagtt\n\ +tcgtcaaaaacgccccctcccttctcgagcccactcacgctatgtattgacgcgaacttg\n\ +ttcgggatcagacttttcaggagttcggtcgcgtgtccctatgtgctaatatataagtta\n\ +gatcgcattagatgctaatctgaatacttatagacgaccttcaacgagaacgggtaccac\n\ +cttgaggctagagttaggtgtgaaacgacaggtagggacatataaaatttgagtgcggct\n\ +ttagttaagggtttaattacctactcaaacatcacgctcgcgcccttcgtacgtaatcga\n\ +ccatctagaggctaaggggactgtactaggtagtgattaatgatatcctagacgcacgtg\n\ +ccttagatcttcagactctgatggtccgcgatcaccgtaattgtagtcctccaactcgat\n\ +cactttgttggcgtcaaagaaattacgatatctaaatacttataatacaataaccaagga\n\ +tgagaatgactcatcgcgttggagttatattgcttgaagttctatggaatgaaagcacgt\n\ +tatctgccgtcccaatatctccagtgagctaattcattggacggtccactttgatcaatc\n\ +cccgaggagatgttcggacactttagtctgtaacacttagcgttgagaccacgaacaatt\n\ +gattactcagtcttgaaggtgttttccaaagttcattttaaataagactacgataggcct\n\ +ttcctattgatataaactacccggctctgttgttcgtgtgagtcgtacttctctgtgttt\n\ +ttctgattatagcaagattcgattcttagtgtaaacagcgatttttatttgacccgtcaa\n\ +tgagaagcgcataggatctaagcaaaattatcaagttgtgccacaaggtaagatctttcc\n\ +agttattgcaggtaggatgtatcccacgttgatagtatgaggtctgacgtcaactgtcta\n\ +ggagagttgaccgcgtgcgggtacaccggatttgcatcgatgttgagaacgcagaactcc\n\ +cactgtcgtggcggcgttcctgatatttagcaagaggcgttgataaagccctcatcatct\n\ +agatctcgacctcatctgccctcttgctccatcattttctacacagactactttcctatc\n\ +tacgttagtataattgctttctatcttagtatcatttagagcttctccgtcaacaggttc\n\ +gtgctattaaagttagtacgaaagggacaacttgtagcaacgcatttaatcggttttcga\n\ +ctacttcgcacaaaatcagataaagaagtttgtcattctattagacattgaattgcgcaa\n\ +ttgacttgtaccacttatgatcgaacactgaatcaagactgtgattaactaaaatagaca\n\ +agccactatatcaactaataaaaacgcccctggtggtcgaacatagttgactacaggata\n\ +attaattggactggagccattacattctctacaatcgtatcacttcccaagtagacaact\n\ +ttgaccttgtagtttcatgtacaaaaaaatgctttcgcaggagcacattggtagttcaat\n\ +agtttcatgggaacctcttgagccgtcttctgtgggtgtgttcggatagtaggtactgat\n\ +aaagtcgtgtcgctttcgatgagagggaattcaccggaaaacaccttggttaacaggata\n\ +gtctatgtaaacttcgagacatgtttaagagttaccagcttaatccacggtgctctacta\n\ +gtatcatcagctgtcttgcctcgcctagaaatatgcattctatcgttatcctatcaacgg\n\ +ttgccgtactgagcagccttattgtggaagagtaatatataaatgtagtcttgtctttac\n\ +gaagcagacgtaagtaataatgacttggaataccaaaactaaacatagtggattatcata\n\ +ctcaagaactctccagataaataacagtttttacgatacgtcaccaatgagcttaaagat\n\ +taggatcctcaaaactgatacaaacgctaattcatttgttattggatccagtatcagtta\n\ +aactgaatggagtgaagattgtagaatgttgttctggcctcgcatggggtctaggtgata\n\ +tacaatttctcatacttacacggtagtggaaatctgattctagcttcgtagctgactata\n\ +ctcaaggaaccactgctcaaggtaggagactagttccgaccctacagtcaaagtggccga\n\ +agcttaaactatagactagttgttaaatgctgatttcaagatatcatctatatacagttt\n\ +ggacaattatgtgtgcgaaactaaaattcatgctattcagatggatttcacttatgcctt\n\ +agaaacagatattgcccgagctcaatcaacagttttagccggaaacaatcgaagcatagg\n\ +gacaatgtatcttttcctaaattgccatgtgcagatttctgagtgtcacgaagcgcataa\n\ +tagaatcttgtgttgcctcaactcgttgaaaagtttaaaacaatcgcagcagtctttttg\n\ +gggtctactgtgtgtttgcaaaataactgaaagaaacgcttgaacaactctgaagtagct\n\ +cgagtactcattaaagtgtaacacattagtgaatatcggccaatgaaccaaacgcttccc\n\ +ggtacgctatctctctcatcgggaggcgatgtgcaggttatctacgaaagcatcccttta\n\ +cgttgagagtgtcgatgcatgaacctcattgtaacaatagcccagcaaattctcatacgt\n\ +gcctcagggtccgggcgtactcctccatggaagggcgcgcatctagtgttataccaactc\n\ +gctttttaactactatgctgtagttctacaggcatagtggccagtattttctaacttctc\n\ +tggatagatgctctcactcctcatccatcacggcttcagtttacgtcttacttgcttgtt\n\ +cagcaacggatggaggcattaagtatcttcactgttccctaaaattgctgttcaatatca\n\ +aagtaaggacgatacagggaaagctcaagcacactcattgaatactgccccagttgcaac\n\ +ctcacttaatctgacaaaaataatgactactctaagtgttgcggaagcagtctcttccac\n\ +gagcttgtctgtatcacttcgtataggcatgtaactcgatagacacgaacaccgagtgag\n\ +aaactatattcttgcttccgtgtgtgtgacaccaggtaattgatgcggatataagctgga\n\ +gatcactcacgcccacacaaggcgctgctacctctttattccaatgtgtaagaatttgct\n\ +aacttcatttctagaccgcagctttgcggtcataatttcacggtacggacccttgggtta\n\ +gagacttgataacacacttcgcagtttccaccgcgcacatgttttagtggcttctaacat\n\ +agaatttttgttgtgacataaagagtgcgtgggagacttgcccgaccgttaagccataat\n\ +caattgaaagccccgtgagtcacatctaattggttgtactgcgcatttagctatccttta\n\ +gctgactcgaagagattcgattcctaatataggttaattagatggctgccgcgcgaagta\n\ +aaacgtgaaaaacgtagtgcgcagatctgcataactcgcgcttaattacttatgagtagt\n\ +tccaagttcgctacgttatgagagagattggaattaagcaaatatgttttatggtgattt\n\ +tgggatgagaaggactgctaagtacggctactaaacaaatttctaaaaccgccatctacc\n\ +ttatcttggagacatttaagttgtatatgtcactagtctagcttttgtctgtgggacgcg\n\ +ttctcggaatgagggaaatgcaagagccgattcatcaaatgcttatctaagaaagtagtg\n\ +gactattacaccaagcacgaatgccagggaactgctttcttgctcaggacctcgcgacaa\n\ +ggtaccccgcataagtcctagaattacatttggtcagcaatgctgacatttgaccgtgaa\n\ +aacataattttaatcagaaggcagctcacccgcttgctctagatcttatctttgtatgaa\n\ +tgtcagaatttactgcaatatccgttccgaatagtgagggcttagtatagttctctgtat\n\ +acaggtcacatcaaactccccctgtcctagtacagctctgagctttaattaattgcatac\n\ +atttccttcaatcatcagatgaaaacaccgcgaatcatgctcttctcgtatagggcaaga\n\ +gaagcaacaaacaactagcccgactcacgttcatccgccgtatccttgttcagttcttac\n\ +tccgtattaggtcagcgaaatctaatcagaataatcggtcgcgtatcaaaattaaaatcc\n\ +cgcttgaggttgacaattaaaacgctgagcagttatcggctattagatagtggggtgaaa\n\ +gtaattggctggaattatgttaaaacgtgatattaagctaaaatacgctacttgttgccg\n\ +acctaattcagtcattcgatattcagttagagccaagaataacaagcttgtataaattga\n\ +acggggtgcactaaacgatgtgttactctaatattcagcttggagtatacctgaaggcga\n\ +attcatgtatcggccaataataagacgttgaagatcacaatttggactagcaaaagaagg\n\ +tgatttatgcgtggggattgagtccactgtacgagtacggtctctggaaaattataggtt\n\ +cagggaatataaggaagtaaagataattaccaagagatttttggtatcgctatgacccag\n\ +aggtgttctaacgtctgttttgatccgcagaatttctgcctcaatgcatatttgacggac\n\ +ttgaactagagcctctaaagttaaatggcgacgcaactgttcctaaacttcaattattac\n\ +tactctttttttcctagggtattgtagaggccagtggacaaaataaatcaaatttaagat\n\ +gtttcggacattaacatcccccgtagcatagaaatcatcagttatccaatctctcatcga\n\ +gcttttacaatttctgctggcgctatggacagcatatgccgcgagacctccgcaagactc\n\ +acttgatcactgtaagtatcttcattagaggttagagcctatagttaagctgctgaccta\n\ +gtaaaattggtattttctaattttattgctcaagttaaaggttagtgaagggataatgac\n\ +gttatttttgaacaatgggttgtattcaattttatatcacgaatggaacccttcattccc\n\ +ggcataatactagacgacacgaacaagctccgatctatcagccaggcacgtgttaaggtt\n\ +taattccggcaaaccaatgaagcatcaaaaggtgacctgatgcaacttagggtcacgatg\n\ +agtttttcaggactacttattacctattaataagttaacatgagccttcataccccgtaa\n\ +gacaatacatactccaccaattagaattctgagccatcttatctttttgtatcatcgaag\n\ +ggtatggccgaataggttaattagttactcctaacgtctctacaggcatgcatttgacgc\n\ +accttcgaaaatagtcaatctctcgccacacgcgtctagtatgcagcatcaaaaatatag\n\ +tccacggtttccggattaccaaacgcggcaaagagaaacattgtatcgacggagataact\n\ +taatacagaaggaaggggcatcttcgaatacggatgaataattctatctgtttattctga\n\ +catcttgttttcaggttaatcttacgcattcaaatgacgcctgccccatgcgtgcgcaat\n\ +tattttctaatattgacgagagcaatctcactccttttgggtctatttatgttttattga\n\ +ggcacaagcctatacagaacaggtactattaaggccgtgagtgtgagactcaaaccgtgg\n\ +aaacaaaggatgggttgttcttggtacaagttttagtgcatgtgggcaatccttaccaaa\n\ +atcagatgctatccttaactttgggctgcatttaagatggcggttggaggcctgtgagaa\n\ +tcctgcgtgtcatctttaatgaccgaattcatccatgtagattcagatcacacactcatt\n\ +ccttgatgttgtctaaacaaaagttgttgtggacgcattggagggagttaagtaacaact\n\ +tgggatcgcatacttataaaaattatatgttaaactttcacaaacgctgaagtccaaagt\n\ +aactagcccaaacgcctcgagagtcactaggtattaatggtgtttgagttcctgtgaaat\n\ +agtgttcgaaggtaaaatttatgtaccaaatcgaaagaacacttaataaggcttgcttgc\n\ +acggaggtatgatgtttactgactctacaaccctaattttccagtacgtacattcattcc\n\ +aataggttagttctcaaagtgctatacaggctcctcaattgatgatatgcttcagccgct\n\ +ctatggatattagctcattttatttaggaagcccgcttagaggcttactatgagggaaat\n\ +gccaaaatgtcatacttttcggtgtgtcccatatgacaccgctttacatagaatttgaat\n\ +taaaacgcgctctcccgttcactaccatacttggtaccgtgcgcatattacatatagata\n\ +taggatcattttttaaagctgtactaggtttgatcgacaatcttatgctatactatatga\n\ +tgtaaccctcataatcaataccgatcgtacgatcctagcataggtggcaagcgattttat\n\ +gccgattattgtgttaaatagtctgtgagtgtgattatcagggctacgttggtagagggg\n\ +ttgtatagacctcgcacacattgtgacatacttaacaatatacgaaaactgatataataa\n\ +atccccttacccaaacaccaatcccgttgaatcaactaccataacgtctcccatataaat\n\ +tgcctacttgtttgcataaatctgaatacataacaccattgcaccttcttgtgttccaat\n\ +cccgttaagattgccttgtcagatgatatgcaagaacaatagcatttgctagcaattatt\n\ +aacagctcttcgaattgcctccacataacgcgggagggtatattttaatttggcaaatac\n\ +taagtactgttggcgtcatatgctattaacggttggatattaagttatgtcagccgtaag\n\ +caagagtgggcgaaatattttgttacccagtgagagcactcttagagtttggatacaata\n\ +ggccatatgttgacttaagaggacgtaactacgccgtacaccattgttcaaccgacttct\n\ +tggcaaatagaatcgtattagcaatcttaagaatagagacacgttcgtgttagggtatac\n\ +tacaaatccgaaaatcttaagaggatcacctaaactgaaatttatacatatttcaacgtg\n\ +gatagatttaacataattcagccacctccaacctgggagtaattttcagtagatttacta\n\ +gatgattagtggcccaacgcacttgactatataagatctggggatcctaacctgacctat\n\ +gagacaaaattggaaacgttaacagcccttatgtgtacaaagaaaagtaagttgttgctg\n\ +ttcaacagatgatagtcatgacgcgtaacttcactatagtaaattgaaacaaatacgcaa\n\ +tttagacagaatggtacggtcatgaatgacagtaattcgaagtgctagaccaacttaaaa\n\ +taggtaaacgtgcccgaaaccccccttaacagaaagctgctatcatggtgcagtatcgac\n\ +gtgttcagaaacttgtaacttttgagcaggtccgagcacatggaagtatatcacgtgttt\n\ +ctgaaccggcttatccctaagatatatccgtcgcaaactttcgatttagtcccacgtaga\n\ +gcccaagcgttgtgcgactccacgtgcatgcccagaaatacgagtttaaatttggttaca\n\ +tggttaattttgaccgaagcatcgcactttatgattgataattggattcaatatgtcgcc\n\ +ctatgcgaatgcaacatgatccacaatttggctataagacgtttaatccgtatcacactt\n\ +tgtttgcggctagtatagtaacgcccgtgcaccaagagtcagtaacaattataagtactc\n\ +cgcaggtacttcaaatataaaaactaatcaaacacgacccatatgatcatctgaagatat\n\ +ttggaactttctcgacaaccaccctcgtactcaatacttacactaatcgacaggcacacg\n\ +caacgtgtacagtcgcaccatattgagtcaagatttgcttagtggcgatgagcgtacacg\n\ +cttatttctctagtcacaattagttatctacgagacatcacgagggagcaaataagcgat\n\ +gttatggctacacataggcacgtatgaatatgatataagccagttaaacagtcgaaccat\n\ +cgagcaaattctcatgcaccaacccacacgttgaggcacaaagagtaagctgtttgaatg\n\ +taacttcttctgctgagcgggccccaacgtaaggatcaactagaagagaaaactcggtat\n\ +tagtttaaatgcgtcacggagcatgagtgcatttcactaagaatgtctgtgtaaccaata\n\ +taacatctatttgttatctgattgcctacttatggctttgcggtcgtggcgactaatgtc\n\ +tccaatccttttgaggtcggtaccaactccctttaaattacgctgtgcaggctcatgcac\n\ +tgcatacatatacggtagcaggtagggacctcacgcacccttattataatcaatagtagt\n\ +tatcagtcaacgaggcaggaatgctgaggtcgaggtgttggtatattttctatgtgccgt\n\ +ctaggcgactatcacgcattaccaggcgagatttaagccaattttgaatatagtcaacgt\n\ +aatttttactatgggttccaccgaaacgccttgcacaactaagaatcccataaaatatcg\n\ +atatcaaataaaagattgtgtcaataccttcatatatattttttcggttgactaacgtga\n\ +actaaggttaggggttttgtatgtctatataggaaacagtttcttttctgtcctacttta\n\ +gtaaagtcttcaagccttactccaaaatcacggtgattaagccgttactcagcagcatga\n\ +ttctgcctgctcgggtcctaaaatccagccttgtaagagtcgctgtgtattagctaggga\n\ +gacctttgttaaaaaggatatatcgcggcgggatgtgagtgcgtggcgcatactcaatct\n\ +tcagctcgtgtcattataatatctctcccccacgcttttcactagatatgccgtgtaagc\n\ +aaacaccttatgcttaatttcgaaaatattggtacttgaaaaaagctgtaggggtactta\n\ +atgtctggtaggagatcaggagagaattgagtgtaaaaccgtaaagccctcacctgactt\n\ +catgtaaatggcttagaagactccatgatttaataaatactacgaaggaaagactggatc\n\ +taaagataactctagtaaggccaactcccttcaatgctgttgccagttataatccaagag\n\ +ctgtccttttctgaaccatagcggcttctgaagcgaactagaagcaaagttggttctagc\n\ +cagacagccacataccctgtacgggtgtattactaaaactggtccggtattagttcacca\n\ +agggaggaattaggcaaaggatctaggtatgcaagtcggagtattacatccctaccctga\n\ +atccatcaataggttcctctgtactggccttcgcaatgagtattcaaggttgtacagccg\n\ +tataataataagatagtgactatgaacgggaagtaacccgctcaccttccccaaaacatt\n\ +gttatatctaagtattaaagtctgccgtagtgttaatactcgaaaataaacaactggcaa\n\ +attacaccgcacttaagccgcttttgatttatatttttccaatgcgcttttaaaaataat\n\ +tcagtcctacatactaattaagacccttaaacggagatatcacaagttaagttttaacca\n\ +tctcgactaggtggaactatagatacccaactcaatttatcattacctgtaatgttccta\n\ +gaaggattgcatttcatgtcaagacggtggagtttcacagcgaaacttcagtgtgaacag\n\ +attctgagaaatcacctaaacctattagtcagagcacccggttagaaccagttgtcaaaa\n\ +aatagagcggttgcatgagacagaagtaacgatgagatccgttgtaacgttgagacatct\n\ +ggcctatcgtcaatacagtcctcccttaaaaatatttttaaatactaggcaaacccaaca\n\ +taggttagtcctatgtgatacgccacatggtatatcattttgtaacgttacctagggata\n\ +atcaggaagtggaattacgcaaaagtagacagtgaaatgcttagggttatagtctagtcc\n\ +aaagataaaggataaagcacgtcagagaactatattagccgaatgggaatcattgttagg\n\ +agactgtggatcatgtctaaaaagcaacgcagaaacagtcatcgaaaaaatctcgttttt\n\ +gtttgaatctaaaagagctttgatgaccgatagtacctgtatactagttactgtattacg\n\ +tgtctaatgatttcggattggggtccccagaatcagacgtcattgtagacgattcaagtt\n\ +taccaatttaatttcccagctctccttggagaactatcgccaataattgcagtcactttc\n\ +cttttctgaaacgataaagccgtcagagttctctgcaacgttggacttacctgaggttct\n\ +aacccactttcggttctaatagtagttaacgacacaacgaataacctttactgtggggct\n\ +ttcacgatattttttcgcttattattaatggttacgtcataagctggtgtccaaattaag\n\ +gttaccggcttcgcagagtagttgtatccaagtataacttccctaatcataagatcgagg\n\ +tagaaaattaatgctgtctctaaccgaacagatatgtcccactatgtggtatggacgttg\n\ +ctaattacttctgaagggaaattggtcattatggatacgtgtctaccatcaggtcggacg\n\ +cagatatggttctgtcttcagttgatccaccgttctttataggataataactgacgatta\n\ +aagattatggtaaatagattaagccaattctcttcttgtcagtgaagcatccttaactga\n\ +cttgctctgcagcccctcatacatttagctattcaaagtaccggctcgtttcaaactctc\n\ +ccacctttggaagaggttgtcaacttgataagtatatcatttacagcattttttcggacg\n\ +tacctctaatgtttcattgcagaaaattagttttttctatcgcacattttgcaagtaacg\n\ +ttagagacacaattatctgcgaatgaactgctagatctgacgaccgggagcctcgcaaat\n\ +atcaaaaaagactgacatatatcaaggagtcgttgacaagtgctggtaagtcaattggtt\n\ +tatctgtcccggcgtttcgatcttaagctgaccatgcacggcagagtaatgtcactctcg\n\ +ttcttacaagtctgtctccaagggtcggcaaaaaagacccctccattctcgagcccactc\n\ +acgatatgtagggacgacaacttgtgcggcttatgaattgtctggactgcgggcgagggt\n\ +ccatatctccgaagttagaagggacatacctttagatgataagatcaattcttattgacg\n\ +aaattcatccacaacggggaacaacttcaccctagacttacgtctgaaaagacacctagc\n\ +gtcttataaaaggtcagtgccccgtttcgtaaggctggaattacctacgcaaacttaaac\n\ +ctcgcgcccttccttacgtatcgacaagatagaggctatcgcgaatgtactacggaggca\n\ +tgaatcatatactagaaccaagtgcctgtgatattaacaagatgatccgacgcgagcacc\n\ +gtaattctaggcataaaactccagcaatttgggggccgaaaacaaatgacgttagctaat\n\ +taattatatgacatgatcaaaggaggtcaatcacgcatcgagttcgacgtatattcattg\n\ +aacttcgtgcgtttgaaagaaacttttatgaaggcaaaattgatcctgtctcctatttca\n\ +tgcgtacctcctagttgataattccccgagcagtggttaggacacttttgtcggtatcaa\n\ +gttccggtctcaaaacgtaaaattctgtaatctgtatggatggtctgtgaattagttaat\n\ +ttttatgaagtcgtcgagacgcagttcctattgatttattctaaacggagatgtgcttcg\n\ +tgggactcggaagtagatctgtgtttatgattattgctactttagatgctgactgttaac\n\ +tccgtgttgtttttcaaccgtatatcacaaccgaattggatagaacctatagtttcaagt\n\ +tctgccacaaggtatcatatttacagttagtgctggttgcttctttcaaacgtggtgagt\n\ +ttgtgctatcacgtcaacggtagagctcagtggaccgagtgcgcgttcaaccctgttcca\n\ +gagagggtgtgatagcacatataccacgctcgtcgaggcgttcatgatagtttgcaagag\n\ +ccggtgttaaacacatattattattgttatccaactaatcggacctatgcataaagcatt\n\ +gtctaaacagaataattgcctatatacggtagttttagtgatttatatcttagtatcagt\n\ +tagagcttcgaactcttcaggttcctcatatttaacgttcttcgaaagcgaaaacttcta\n\ +caaacgaatgtaagcggttttccaagtagtacctataaatcacagaaagatctgtctcag\n\ +tatagttgaaatggtattcagctagtgacgtgtaccaattatcatagttcactcaagcaa\n\ +gacgctcattaacgaatatagacaagacactatatcatataataaaaaagaacatggtgc\n\ +tcgaacatagttgaattcaccatattgaaggggaatgctgacatgtaattcgctactaga\n\ +cgatcaattccctacttgtcaaagttgaactggtacgttcttggaattaaatatgattgc\n\ +gctggaccaaattgcgacttcttgagtttcagggcaaacgattgagccggaggatgtccg\n\ +tctcttacctttcttgcttatgataaacgacggtccctgtacatcactgggaattctcag\n\ +caaaaataattgggtaaatcgagactcgatgtattcggccacaaaggtgttagacgttaa\n\ +agattattcaacggggcgataataggatcataaccggtatgcaagcgcattgaaagagcc\n\ +atgagatccttatccgataaacgctgcacggtatgtgcagccttattgtcgatcacgaat\n\ +ttataaatgtagtctgggctgtaagttgaagacctaagttataatgaagtgcaataccaa\n\ +atcgattcatagtggattatcagactcaagatatctcctgataaattacagttgttaaga\n\ +tacggataaaatgagatttaagattagcagcctctaatctgtttcaatcccgttggaatg\n\ +tggtatgcgatcaaggttaagttaaaatcaagcctgtcttcagtcttgattcttgttctg\n\ +ccatcgcatgcggtctacgtgagttaatatgtagcttacgttctagcttgtgctaatctg\n\ +agtatagattcgtagaggaatattatcaagcttccacgcctcaacgtacgtgtattggtc\n\ +acacaagacactaaaagtggaagtagcgtaaactatagtctagttgttaaatgctcagtt\n\ +cttgttatattcgatatactcttggctaatttatgtctgagtatataaaattaatgatat\n\ +taacttgcatttcacggatcccttagaaaaagattttgaccgagcgcattataaacggtt\n\ +acaccgaatcaatagaagcatacccaatagctttctttgaatttattgcctgcgcaactt\n\ +ggctgactctctagatccgaataattctatatggtcgtgacgaaactagttcattactgt\n\ +ttaaaatgccaacatgtcttttgggccgataatggctctttgcaaaattactcaatgata\n\ +cgattgatcaaagcggtagttgctagtggtagcatgtaagtctatcaaatgtctgattat\n\ +ccgaaaatcttccaaaagagtccacgtaccatatctatctcatagcgacgcgaggggaac\n\ +cttatctaactatcattccatttaccgggtgactctcgatgcaggatccgattgggataa\n\ +attgcccagaaatggctcattcctgactaagggtaaggccgttctcagcaagggaacccc\n\ +gcgaatctaggcttataccatctagattgttaactacttgcctgtagttctacagccata\n\ +ctggacagttgtttctaaatgatcgggattcatgctagcactcctctgaatgcaccgcgt\n\ +aagtttaactattacgtccgtgggcagataaggatggaggctgtatgtatcttaactgtt\n\ +acctaatatggctggtaattatcaaagtaaggaccttaatgccatagcgctagcaatcgc\n\ +tttgtatactgaccatgtgccaacctctcttaatctgtaaaatataatgtcttagctaac\n\ +tgtggacgatcatgtctctgcctagagcttcgctgtatcaattcctatagccagcgtact\n\ +agtgacacaacaacaccgtgtgagaaaagatattagtccttacgtctgtctctctacagc\n\ +ttattgatgaggattgaacatggacatatagctccccctcaaaagcagatgctacctctt\n\ +tattccattctcgaacatttgccgaacttaatttcgacaaacctgaggtcacgtcttaat\n\ +ttatcggtaacgtcacgtccctttgagactggataaatatattaccaggggccaacgagc\n\ +aattgttggaggcgcttctataatacaaggtgtcttgtcaaagaaagacggcgtgcgtct\n\ +cgtgcaactcacttaaccaatattaatgtgaaacccccctctctcacatcttatgcggtg\n\ +tactgccctggtacatttcctgtacaggactccaacagtgtagattcctaagatagctgt\n\ +tggagttgcctcacgccagatcgaaaaactgaataaactagtgagctgagctgcagaaat\n\ +accgcttaattacttatgactagttcaaagggacctacgtgatgtcagacattgcaagga\n\ +agaaattaggtttgtgcgtcattttggctggactagcactccttacttcccctactattc\n\ +aaatgtcgtaaacagcatgagacaggatcgtgctgacatttaaggtctattgggaacgag\n\ +gctacctttggtcgcgcgctcgcgttctccgaatgaccgaaatgcatgagcacagtatgc\n\ +aattgcttatagatctaaggtctggtcgttgaaaccaagcacgtaggcctgggaaatcag\n\ +ttcttcctcagcaactacacaaaagcgtccaagcattagtacttgtagtaaatgtccgaa\n\ +cctatgcgctcatttgaaagtcaaaaaatatttttaagcagtaggcacctaacccgattc\n\ +ctctacttagtagctttctttgattctcagaattgactgcaatatcactgcacaattctg\n\ +tgccattactagacttctctgtattaacgtctcatcttactaacactcgcctaggacaca\n\ +tctgagagtgaagtatttcaatacatttactgaaatcttcagttctaaaatccccgaata\n\ +aggctcttatcggtttggccaacacaagaaaaaaacttcttgcaccactcaccttcatac\n\ +gcaggagcctggggaacttagtaataactatttcggcagacaaagcttataacaagttgc\n\ +cggcgcgtataatatttaaaagaccccttgagctgctcaattaaaacgctcacctggtat\n\ +aggctattagatagtgccgtcttagtaaggggcgggaattatcggataaactgatatttt\n\ +gataaaataaccgacttgttcacgacataagtcactaaggagattttatctttctccaaa\n\ +gtatatcttccttggataatttcaaagcgctgcaatttaagttctgttactagtttatgc\n\ +tgctgggaggtgaccggaaggcgtagtaatctagaggcaaattataagaagttcatcata\n\ +tcattttcgactacaaaaacaaggtgttgtatgccggcgcattgtgtaaactggacgagt\n\ +accctagatggaaaattatacgttaagccaagatttcgatgtaatgataattacctacac\n\ +atttttgctatccataggaacaagagctgttctataggctcgtggcatacgaacatttgc\n\ +tgccgctatgaatattggaagctcttcaactacagactctattcttaattgccgtcgaaa\n\ +atgggccgaatcggctattattaatactcggtttttccgaggggattgttgtcgacagtc\n\ +gtaattattattaatattgatgttggtgaggtcatttaaatacaaccttgcagacaatga\n\ +ataagggatccaatctctcatactccttttacaattgctcatgcccctatgcaaacctta\n\ +tgccgccacacctccgcaactctctcttctgaactgtaagtagcttcattactggtttga\n\ +gactatactgaagctgatgacattctaaaatggctattttcgaatgtgattcataatgtt\n\ +tatcgtttgggatggcagaatcacgttatttttgatatagcccgggtattctattgtata\n\ +gaacgtatgctacaagtcattccccgaagaagactagaagtaaacaacatgcgaccatcg\n\ +ttaagccacgcaaggctgtagctttatttcccgataacctatcttccataaatagcggac\n\ +agcaggatactgacgctcaacatcagtggttatggtctaatttttaacttttaataaggt\n\ +aacttcagcaggcatacacagtaactctttaatttataatcaaattagaagtctgacact\n\ +tcttatatttttctatcatccaacgcgatcgcccattagcttattgtgttactaataacg\n\ +tatctaaaccaatccttttcaagctactgcctatattgtcaatatatacaaacaacagga\n\ +tagtaggctgcttaaaaaatattgtcaaccgtgtacgctttacaatacccggaaatcaca\n\ +aactttgtagacaacgagtgaaatttatacactacgaagggccagcgtacaagacccatg\n\ +aattaggcgatatgtttattctgacatattggtttatccttaatctgtcgctgtaaaatg\n\ +aagccgcccccatccctgcgaattttttttcgaagattcacgactgaaatataaatacgt\n\ +ttggctatatttatgttggagggaggcaatagcctttactgttaaccgaagatttagcca\n\ +gtgagtgtgacactaaaacactggaataaatgcaggcgttcttctgggtaaaaggtttag\n\ +tcaatctcgcctataagttcatatagctctggatataattatctggcccatgcatttatc\n\ +atggcgcttggtgccctgtgtgaagccggcctctcatattgaaggtccgaagtattccat\n\ +gtacattaagatcactctctcattcatgcatcttggcttaacaaatctggttgtccaagc\n\ +tttccaggcacgtatggtacaaattcggatcgaatacttataaaaatgatatgttaaact\n\ +gtctaaaacgctcatctacaaagtaaagtgcactaaccaatagagtctcaagaccgtgta\n\ +atgctggtgcactgaatgtgtaatacggttagaagggattagttatgttacaaatccatt\n\ +gaaaacttaagaagcattgcgtgctcggagggtgcatcttttatcaagagactaacatta\n\ +ttttcaacgacgtacatgctttacaatagggtacttatcaaacgccgagaaacgcgccta\n\ +tagtgatgttatgattatgacccgatatccattggaccgaattttatgtaggttcccagc\n\ +gtactcgcgtaatatctcggtattgccataatgtaatacttgtcggtctctcccagatga\n\ +aaaagcgttacagagtatttcaatgaaaaacagcgcgcaacgtcaatacctttaggggta\n\ +acggccgctgatttcatatagatatacgataagttggtatagctctactaggtggcatcc\n\ +acaatcgttgcatttactatagctggttacaatcataatctataccgttccttacatact\n\ +accatagcgggatagcgtttttttgccgttgattgggtttaagaggatgtcagtctcatt\n\ +atatccgattcggtgggagagccgttgttttcaaatcgcacactttgtgacataatgtac\n\ +aagataacaaaactgatataagatataaactgtcaatatcaccttgacacttgaatcaaa\n\ +gtaaattaactcgcaaatataatttgactaattgggtgcagatttctcaattaataaaaa\n\ +aatggcaccggatgggcttacaagccccttatcattcacttgtatcatgatttccaagaa\n\ +caatagaatttgctagcaagtatgaacagagattcgaattgcatccacagtacgccggag\n\ +cgtttattttaatgtggatatgacgatgtactgttggcggcatttgctagtaaccggtcc\n\ +ttatttacgtagcgcacacgtaagcatgtctgggagaaatatggtggtacaatctcagag\n\ +aaagattacagtttggtttaaataggacttatcgggtcggaagtggaacttaataagcag\n\ +tacacaattgggcaacagacgtcttgcctattacaataggattacaatgcgttagatttc\n\ +agacacgttcgtgtttggctattcgtcaattccctaaatagttagacgatcaactattat\n\ +caaagtgattctttgttcatcctccattcatgtaacagatggcacactacgcataacgcc\n\ +gaggaattttaacgagatttaagagagcagttcgggcacaacccacttgactttataaca\n\ +gctcggcagcataaacggtaatatgtgacaaatttccaaacgttataagaacgtatgtgt\n\ +acttagaaaactaagtggttcatgttcaacagatgtgacgcagcaagcctaacttatcta\n\ +ttggttttgctataaaagaacaaagttacacagaatcctaagggcttgtttcacacttat\n\ +gcctagtgcttcaccatcttaaaatagcgaaaccggcacgaatcaaaccttaaaacaatg\n\ +cgcagatattggtgatggtgactccgggtatgataatggtaactgttgaccagcgcccac\n\ +ctcatcgaagtatagaaagtggttaggataaggatgagaccgaacttatttccggccata\n\ +actttagattttctacctagtacacaacatcagggcggacacgaaaccgccatcacatca\n\ +tataccaggtttaatttgcttaatgggggaagtgtcaacgaaccttcgaactttagcagg\n\ +catatggccattatatatggccccagagcagaatgctacagcagacaaaatttggattta\n\ +tgtagtttaatacctatcaaacttggtgtgaccatacttgtctaacgacagtgcacaaag\n\ +tgtaagttacaattattactactcagcagcttctgcaatgataaaatcttatcatacacg\n\ +tcacatatgataatatctacttagggggaacgggctccacaacctacatagtactcaata\n\ +cttacactattcgacaggcacaccaaacctgtacagtcccaaaagattgagtcaactttg\n\ +cagtactgcagatcacagtaatagcttagttagcgagtcaaaattagttttctacgagac\n\ +tgcacgaccgtgcaaatttccgatgtgttggctacaaatagcaacgtatgaatttgtttg\n\ +aagccacgtaaactgtacaaccttagagataagtctcaggctactaaaaacacgttgtgg\n\ +cactaacaggatcatggttgattcttacttattcggctgaccggcccaataagtaacctt\n\ +caactagaacagaataatcgggagtagtttaattcagtcaaggtgcaggtctcattgtaa\n\ +ctaacaagctctgtgtaaccaagttaaaatcgttttcttagcggattccctacttatgga\n\ +tttgagctcgtccacaatattcgatacaagaagtttgtggtccgtaacaacgaaatttta\n\ +attacgctgtgcagcctcatccaaggaattaatagaaggttgatggtaggctccgaacgc\n\ +tccatgattataatcaagtggactgtgcagtaaacgaggaaggtatcctgacgtcgtggt\n\ +gttcgtttttgttatttgtgccctatacgagtagataaaccatgaacagcacagtgtgaa\n\ +cccatggttgattttaggctaccttatttttaatttccgttacacagaaacgaattccac\n\ +aactaacatgccattaatttttcgatatcttataaaagatggtcgaaattcattcattta\n\ +ttttttttcggttctcgaaagtcaactaagctgtcgcgttttgtttctctttagaggtaa\n\ +aagtggctttgatctcctacgtttggatactagtcaaccattactccatttgatccgtga\n\ +gtatcacctgtctaacatccagcattatgactcctcggcgaagaaaagacacacttctta\n\ +gagtcgatgtgtattagctagggacacagttgtttaatacgatagtgagcccagggaggg\n\ +cagtgcgtcccccagtagatttattcagctagtgtaagtataagatatctcacccacgag\n\ +gttcaagtgatatgcagtcttagaataatacttatcctgaatttcgatattatgggtact\n\ +tcaataatccgctagcgctactttatgtctcgttggacagcaggacacatggcagtctta\n\ +aacactaaagacatcacctgaatgaatgtaatgggattacaagaatcaatgaggtattat\n\ +atacgacgtaggaaactctggatatatacagtaatctagttacgccatcgcacttcattc\n\ +ctctggaaacttagaagacatcagctgtacgtggaggaaccagacccccgtatgtagcca\n\ +aatagaaccaaagttgcttatacaaacacacccaatgacaatggaccgctggagttcgta\n\ +aactcggaacgtagtactgcacaaacccagcatttagcaataggagctacgtatgcaact\n\ +cccacgtggtaataccttcaagctatcaatatataggtgcctagctaatcgcattcgcaa\n\ +gcagtattcaagcttgtaaaccagtataataattacagaggctctatgaaacccaacttt\n\ +ccagctaaaagtcccaattaaatggttatttcgtacttttaaagtcgcccgttctgttat\n\ +tacgcgaattgattctactccaaaattaaacacaaattatcaaccgtttcatttatattt\n\ +gtcaatgcagctgtttaaaataaggctctactaaattataattaagacacttattaccag\n\ +atttctctagttaagtttgaaccagctcgactaccgcgaaagatacattcccttctctat\n\ +ttttcagttcatctatgggtcagagaagcattgaatttattctattcaccctcgtcgttc\n\ +acagcgaatcgtcagtgtgatcagtgtatgagaaatatcctaaaccgtttagtcagacca\n\ +cacgcttagaacaagtggtctaaaaagactgccctggaaggagtaagaagtatacagctg\n\ +atccggtgtatccttcagtcatctgccctatactaattacacgacgcaaggaaaaatagg\n\ +tttattttctaggcaaacccttcataggtgactccgatgtgttacgaatcatgcttgaga\n\ +atgtgctatcgttaccgacggataataacgatctccaatgaaccaaatgtagaatgtcta\n\ +ttgattacccttttactattcgacttagagataggagatagaacctcagtgtactttttt\n\ +agccgaatgggaatctttgggaggtgaatggccataaggtcgtaaatccaaccctcttaa\n\ +agtcttccatattatatcgttgttcgtggaatcgataacagatttgttgacccatagtaa\n\ +atgtatactagtttatgttgtaagtgtagattgttttccgattgccgtccaaactttatg\n\ +tcgtaattgtagaccagtaaagttgaccaaggtaagtgcccagcgatcctgcgagatcga\n\ +tcgccaatttttccagtcactgtaagtgtaggtttagataaagccgtatgagttatatca\n\ +taagggcctcggaaagcagcttcgaaccaaagttcccttataatagtagtttaactataa\n\ +aagtatatactggtctgtcgccctttcacgatttgttttaccggtttatgaagcgttacg\n\ +tcattagagcggctccaatttaaggttaacggcttccatgtgtagttgtatacaaggata\n\ +acttaaagtatctgttcagcgagctagttaagttatcctcgatagaacacaactcagagg\n\ +tcccaagatcgggtttgcaacttgctaatttattctcaaggcaaattgggaattatcgat\n\ +acctgtataccataaggtcgctcgatgtgatgcttatgtcttctggtgatcctaccttag\n\ +ttagtgctgattaacggaacattaatgtttatcgttttgagatttagccaattctctgat\n\ +tctaactcaagatgccttatctgacgtgctatgcagcccctaagtattttacattgtaat\n\ +aggacacgctcctttaaaactcgccaaaaggtcgttgtggttctctactggttaactata\n\ +taatttacagctttgttgagctagttcctctttggtttaagtcctcaatattagttggtt\n\ +cgagcgataagttggctagttaccttagtcactatattagatccgaatgttatgcttcat\n\ +ctgaagaccgccaccctccaaaatttcttttaagactcacttattgcaaggtgtaggtga\n\ +attcggctcgtttctcaagtggtgtatctgtacacgagtttccatattttcatcaacagc\n\ +caccgcacacttatgtcactctaggtattaaaagtcgctctacaaggggacgcaattaag\n\ +aaacagacatgctagtcaaaaataaacatagcgaggcaccactaattcggccgcttatca\n\ +atgggatgctctgcgcgagacgcgccagagctcagtagttagttcggacatacatttact\n\ +tcagatgatcaattagttttctacaaatgcttactctaccccgaaaaaagtcaccagact\n\ +cttacgtctctttagtatccttccgtcttatataaggtcagtcccccgtttcggtaccct\n\ +ggaatttactaagaataatgaaacagcccccaaggacgtacgtttacaaatgatagacca\n\ +gatcgcctagcttattccgacgcatgttgcatagaattgaaccaacggaatgtgagagta\n\ +actagatgagccgaccacagcacccgtttgcgtcgcagaatacgcctgatagttcggcca\n\ +cgaaatcatatgtcctttgagtattaagtatttgtaatgatcaatcgagctcaagcaagc\n\ +ttacacttcctcggatattcagggaacttagtgcctttgaaagatacgttgatcaacgaa\n\ +aaattgataatggctcatatggaatgcctacctcatagtgctgaattaacacagcactgc\n\ +ggacctaacttttcgaggtttcaagttcacgtctcaaaacctaataggctggaatatgta\n\ +gggatcctcggtgaatttgtgattgggtttgttgtagtactgaccaagtgaatattcttt\n\ +ttttctaaaagcagatctgctgccgggcactacgaaggagatctctgtgtatcattattg\n\ +cttcttgacatgatgactcttaaatcactgtgggtgtgcaaaacgatagcacaacccaat\n\ +tcgatagtacatattgttgatacttcgcactaaaccgttcatatttaaaggttgtgctcc\n\ +ttccttcgttaaatactggtgacttggtcctatctactattagctagacctctggggaac\n\ +cacgcccccgtaaaacctgtgcaagagagggggtcatacatcttagacatcgcgcctcca\n\ +ccagggaagcattgggtgattgaccaggtgtgtaacaaatatgattattcttatactaat\n\ +attagcaaagatgcataatgatttgtattaaatgtataattgaattgataagggtctttt\n\ +agtcagtgatagagtagtataaggtagacattagaactcttaaccggacgcagatttttc\n\ +ggtcttagtaagccaattagtcgacaaaacaaggtaagagcggttactagtagtacctat\n\ +aatgcactgaatcttcggtcgaagtatagttctaatgctatgcagattgtgacggcgaca\n\ +aatgttcagacttatatcatgaaacaagctcttgtaagtattgacaaatgaaaagattga\n\ +atatttttaaatacaaaatgcgcctacttattaggggaattaaccagattgaaggccaat\n\ +cctcacatgtaatgagataatagacgataaatgaaattcttgtaatagttgaactgctac\n\ +gtgatgggtattatatatgattgagatcctccaattgccgacgtcttgtcttgatgccca\n\ +aaagattgtcaacgaggagctccctcgcgtacctgtcgtccgtatcataaacgacgcgac\n\ +atgtacagcactccgaagtataagcaataataatgcgggtaatccagactagatcttttc\n\ +ggactcaatgcggtttcacggtaaacatgattaataccggagagtagtcgagcttatcag\n\ +cgatgcaagcgaattcattgtgccaggagatacgttgcagataaaaccggcaacgtatgt\n\ +caacaagttttggcgatctcgttgtttgtattcgacgaggcgcgggaacttcaagaacta\n\ +tcgtatattcaagtccattaccttttagtttcagactggtggagctgactaaagttatat\n\ +catcattttgtacactggtttagttaacgataatttcagatttaacatgaccagacgata\n\ +atcgctgtatatccagttggaatgtggtttgccagaaaggttaacttataatcaagcctc\n\ +tcttcagtcttgattcgtcgtatcccatccattgcgctatacctcagtgtatttggagct\n\ +gtagttataccgtgtgctaagatcagtagacatgacgagagcaatattatctaccttaca\n\ +agcatcaacggacgtctagtcggaacaaaagactctaaaactcgaacttcaggttaatat\n\ +actatagttctgtattcagcagttattcttatattcgatattatcttgcctattggatgt\n\ +ctgactttagtatattaatcatagtatctgccatgtaaaggtgccagtactaaatctgtt\n\ +tcacagtgcgaattataaacggttacaaccattaaagacaacaagaccctatagctttat\n\ +ttgaattttgtcaatgcgcaacttggagctcgcgatacatcccaattagtctatagggtc\n\ +gggacgattctacggcatttctggttataatgacaacatggattgtggcccgagaatcgc\n\ +tctttcattaattaagcaatcattacagtcttataagcgctacttccgagtggtagcagg\n\ +taactcgatataaggtcgcatgagccgaatagcttaaaaaacaggccaccgaacattgat\n\ +agagaataccgaccacagcgcaacctttgattactttcattaaattgtacggctcactcg\n\ +acatcaagcttaagattgcgataatgtgaactcaaatggatcagtactgaagaaccgtaa\n\ +cccacttcgcagaaagcgtacccagagaagatacgctgttacaatatacagggtgaaatt\n\ +attgcctgttcttcgtaaccatttcgccaaacttggttagaaatgatagccattcatgat\n\ +agaaataagctgaatgataccagtatctttaactatgtagtcagggggaagataacgatg\n\ +gtccatgtatgtttctgatatgtgacagtattggccgcgtaatttgctaacgaagctact\n\ +taatgcctttgagcttcatatagatttctttaatcaaaatcggcaaaaagatagtatgag\n\ +ctataatatatgctagtagagaactctggaccatcatctatatgaatactgattcgagcg\n\ +tgcaattactttagcctgcgtactactgactctacaaaacactctgagataagtttgtag\n\ +tcagtaagtcgctctctataaaccttttggatgaccattgtacagccacttatagatccc\n\ +aataaatagcacaggagacagagtttttcaatgctcgatcatttgccgatagtattttcg\n\ +tctaacctcagggcacctattatttgatacctaacctaacggccctttcacaatggagaa\n\ +atatatgacatcgggacaaacacaaatggtgggtggccaggagatatgacatggtggcgt\n\ +ctctaagaaacacggactccctctaggcaaactcacgtaaccaattttaatgtcaaacaa\n\ +aacgctcgaaaagattttgccgtgtaatgacctggtacattgactggtcaggaatacatc\n\ +actgtagttgccgtagtgtcctgttggtgttccatcaagacacatcgtataacgcaattt\n\ +acgacggacatcagatcaagttatacagattatttaagtatcacgtgtgcattgggacat\n\ +aagggatctcacacatgccttggaacatttttgctttgtgccgctttttcgctgcactac\n\ +caatccttacttaccagtatattcaaaggtcgttaacagaatgagaaaggttagggctct\n\ +aagttatcgtcgattgggatagacgagacatttgcgagcgccctccacggatacgaatct\n\ +cccatatcaatgtgaactggatgctatgcagtttagttcttacgtctcctagtggtaaaa\n\ +atcaaagtagcactcgcatagcagttattcagaacctaatacacaaaaccgtcaaacatt\n\ +ttctaattctaggtatgggccgatcataggagctaaggtgaaactcataaatgttttgtt\n\ +agatctagcatcctaaaaagatgcatatactgagtagctggcgtgcattctctcaattgt\n\ +atcctttttaactgaactagtcggtcccatttcgtgactgagatctattaaccgataaga\n\ +ttaataacactcgcattcgtatcagctcagagtgaagtttttcaataatttgactgatat\n\ +attaacttctaaaataaccctttaagcctcggatccgtttcccaatcacatcaaaaattc\n\ +ttattccaactatctacggattaacaacgtgcatggggatcgtagtaagaacttgttccg\n\ +atcactttgagtatatcaagttgacggcccggttattattgaatagaaacattcacctgc\n\ +taaattaaataccgcacatcggatacccgatttcagagggccgtcttactaagggcaggc\n\ +tttgttcggtttaactgagatgttcattattttacagtatgcttcaactaatatgtaacg\n\ +aaggacagtggatctgtctccatagtagatcttcagtcgtgaatttcataccgctcctat\n\ +ttaagttcgcgttcgagttgttgatcatggcacgtgaaagcaacccctagtattctagac\n\ +gaaaattttttctagttcatctgataatttgccaattcaaaaacaaccgctggtttcccg\n\ +gcgcattctctaaaatggaagtcgaacctagagccattatttgtcggtaacccatgagtt\n\ +ccttcttttcagaagttaatacactgtggtcctatacagaggaaaaacagcggttatata\n\ +cgatcgtggcataacaacattggatcaagatagcaatttggctacctattctaattctca\n\ +ctagattcggtattccactacaatatcggcagattaggattggatgaataatcggtgttt\n\ +aagtccggttgcgtctccaatctcctaatttttattaatattgatcttggtgacctattg\n\ +taaataaaaacttcaagactttgaataacggtgaaaagatagaagactcatttgaaaatg\n\ +gatcatccacagatccaaacattagcaagacactaatccccaactagctattctgatcgc\n\ +gatcgtgctgcagtactcctgtcacaatagtctgttcatgatctaattctttttgggctt\n\ +tgttcgatggtgattcagaatctttatccggtcgcttccctgtagctactttgtggggat\n\ +attgcccggggattatagggttgagatcgtttcctaaaagtatttaaaccaagtagactt\n\ +caactaaactacatcagaacatcgtgaagacaccatacgcggtacctttatttaccgata\n\ +acatttcttcaagaaataccggtaagcagcataatgaccctaaacagctcggggtatcgt\n\ +cgtagttttaaattttatttaggttactgctcaaggaataaaaactaactatttaattta\n\ +taataatattacaaggctcacactgattagatttgtctataagacttcgcgatcccccat\n\ +taccggattgtcttaagaataaactagataaaccatgcattttctagataaggcctttag\n\ +tctaattagatacaaaaaacacgatagttgcatccttaatttattgtgtcaaacctggaa\n\ +ccttttaattacccgcaaatcactttatgtcgagactacctctgaaatttattatctacc\n\ +taccgcatgaggacttgaaccatcttgtaggagttatgtttattagctaagattcgttta\n\ +tcctgtagcggtccatgtatattcaacaagcaaaaagcactcagaattgtttttagttga\n\ +gtcaagactgatatataaataagtttccctagttttttcgtggtgggacgatattgaatt\n\ +gaatcttaaccgaagagtttcccactctgtcgcacaataatacacgccaatatttccagc\n\ +cctgcttatgccttaatcggttactcaatctcccattgaagttcattttgatctgcatag\n\ +aagtttcgggcccagccttttttctgccaccttcctccaagctctgtagacgcactctaa\n\ +gattgatgctcacatgtattaattctacattaacataaatatataagtcatgcatcttcg\n\ +agtaaaatatctggttctccaacatgtcctggcacgtatcgttataatgcccatacatgt\n\ +agtattaaaatgattgggttaactggatattaagatcatcgaaattgtaaagtcaaatta\n\ +acaatactgtctcaagaccgtgtattcctcgtgctcggaagggctattacgcttacttcc\n\ +gttttggtatcttaatatgactttcaaaaattaagttgcagtgagtcctacctgcgtgca\n\ +tcggttagcaagagtataaaagttgtttaaacgaactacttgctttacaataccggtcgt\n\ +atatatcgccgtgaatccagaagattgtcttctttggattatcaaccgagatcctgtgga\n\ +ccgatgttttgggaccttcacagaggactccaggtagagctcgcttttgcattaatctaa\n\ +gaattgtacctctctaaaagatctaaaacagtgaatgtgtatttcatggaaaaacacaga\n\ +gaaacgtaaattactttaggccgaaaggcacatgagttattatacatatacgagatggtg\n\ +gtatacatcgaattcggggcatacactatagttgcattgtatttagctgctttaaataat\n\ +atgatattaccttccttacataagacattaccggcataccctggttttcaacttgtgggg\n\ +ctttttgacgatcgcactctcatttgatccgagtagggcggtgacccctgcttttcaaat\n\ +acaaaaatttcgctatgaaggtaatagattacttttcgctgttatgatagaaacggtaaa\n\ +tttaaaattgaaacttctagaaaagtaaagtaacgagaaatgattttgtgaataatgcgg\n\ +tcatgattgcgcaagtaagaaaaaaaggcaaaaggatgcgcggaatagaaacttatcagt\n\ +cacgggtatcttgatttcattcttcttgtcaattgccgacataggatgaaatcagattcc\n\ +aatgcaatacacagtaacccccacccttgattgtaatgtcgatttgaagttgtacgcgtc\n\ +gacgaagtggatagtatacgggccttttgtacggtgcgatcaactatgaatctcggcgag\n\ +ttagatggtcgtacaatctcacacatagaggtcacttgcctgtaatgacgaattttcggc\n\ +taggtactcgaactttattagaagtaaaaatgtgggcaaaagaaggattccattttacaa\n\ +gacgattacaatgagttacatgtctctcaacgtagtctttccctagtagtctttgaacta\n\ +tttaggtactccagaaaattttagcaaagggtttctgtgtgaatccgccattcatgttta\n\ +tgatggaacaataagaataacgccctcgtatgttatcgacagtgaagtcagcagttcggc\n\ +caaaaacatattcaatttagtacagatccccagaagttaagctaagtgctctaaaatggc\n\ +ctaaacggttatcaaagtaggtctaattactatactaacgggtgcatcgtaataactgct\n\ +gtcgatgcaacactatatgatagtgtcgttttgctatatatgtacaatgtgacaaagaag\n\ +ccttagcgattcttgcaaacttaggacttcggattctcaatcttaaatgtccgaaaacgc\n\ +aaagattcaaaaatttaatctatgagcagatatgcctgatggtgactacgcgtatgttaa\n\ +ggctaaatgttgacaaccgcacacataatcgaactattgatagtcgggagcataaccagg\n\ +tgaacgtactttgttcacgacatttattgacatgttctaaatacgtctcaaaatcacggc\n\ +gcactagaaaacgcaatcaaatcattgtcctggtttaagggccgtaatgccggtagtgtc\n\ +aaacttcatgagaactttagctggcttttggccagtatttagggaccaagagcactagcc\n\ +ttaagctgaatattttgccatttatctactgttataactttaaaacttggtggcaccaga\n\ +cttgtcgatacacacgcatcaatctgtaacgtaaaaggtttactaagaacaagcgtagga\n\ +attgagtttatattatatttaaactaaaagatgatattagcttctgagggcgatagggct\n\ +ccaaatcataaagaggaatatattattacacgattagaaacccacaacatacctcgaatc\n\ +gcccaaaagtttgacgaaacttggcagtactccacatctcagtaatacagttgggagagt\n\ +ctcaaatgttgttttattactcaatgaaccaccctcataatttcactgctgttccattaa\n\ +atttgcaaacgatcatttgctttgaagaaacgtaaaatcgacaaaattacagataagtag\n\ +atgcataataaaaaaaactgctcgctataacacgatcatcgtgcattcttacttaggagc\n\ +atcacccgcacaataacgtaccttaaactacaacactattagaccgagtactgtaattca\n\ +cgaaagctcaagctcgcattgtaaagaacttgctctctcgtaaaatgtgataatagtttg\n\ +cggagaggattcaattattttccattgcacctactccactagattcgataaaagaaggtg\n\ +gtcctcccttaaaaagaaatgttaagtaacatcggaaccataagcaaagcatgtaagtga\n\ +accgtcatccttccctaagaaacataaaggtttttaataatgtcgactgtgaactataac\n\ +tgcatcctttcctgacctactccggttccttgttgttatttctgaacgagaccagtagat\n\ +aaacaatgtaaaccacagtgggtaccaatggtgcatgtgacgctaccgttgttttaagtg\n\ +cccgtacaaacataagaagtcataatcttacttgaaattaattttgccttttattttttt\n\ +tcaggctcgaaattaatgatttgttttttttgaccttctagttacgctaatatgcggtcg\n\ +cctgtggtttctattgagtcctataacgggatgggatctaatacgtttggttactagtaa\n\ +acaaggtataaatttgataccggagtatcaactgtataacatcaagctttatgactcata\n\ +cgcgaagtaatgacacaaggctttcaggagatcgcgagtacagagccactaaggggtgta\n\ +ttacgatagtgacaccaccgagcgcactcactccccaagtagatttatgatcctacgcta\n\ +agtattagatatataaccaaagaggttctagtcagtgcaactcttagaataataattagc\n\ +cggttttgcctttttaggcctaatgcaatattcagctagcccttatgtatctcgcgttcc\n\ +acagcaccactcatggcacgcgtttaaactaatcaaatataatctatgaatgttatgcca\n\ +gtacttgaataaatcaggttttttataagtccttgcatactctcgttatatactgttaga\n\ +gtcttaccccatagaaattctttcatctgcaaacttagaagaattctcagctacggggag\n\ +cataaagtccccaggatgttgacaaatacaacaaatgtggcttatacaaacactccatat\n\ +gaaaatcgaaccctcgtggtagttttagccgaaccttgtacggataaatccctccatttt\n\ +ccaatagcagatacctatcctactacctcgtggtattaaattaaagcttgaaatatagag\n\ +ctgcatagcttatccaattcccaagcacgagtctaccgtcgtaaccacgatttgatttac\n\ +agacgctagagcaaacccatctttaaacatataagtaaaaattaaagggtgagtgcgtac\n\ +gtgtttactagcaacttcgcttattaagacaattgtttataagccataattaaaaacata\n\ +tgttcaacaggttcattgatatttgtaattgcacaggtttttaataaggatctacgtaag\n\ +tataatgaacaaactttttaccagagttatattctgtactttgaaaatgctcctctaccg\n\ +ccttagagactttcaattagattttttgcagttaatctatgcgtaagtgaaccatgcaag\n\ +ggatgcgattcaaccgcctcgtgctaaccctatcgtctgtctcataactgtaggtctaat\n\ +ataattttcagttttcgaacacataaccctttgaaaatctgctatttaatgtctcacctg\n\ +catgcactatcttctatactgctcagaacggctatacgtcactatgctccaagtgacgat\n\ +ttaaacgaagcaaggaataataggtttattttagtgcaaaacaattaagtgcggactacg\n\ +tgctctttacaataagccttgtgattgggctataggttaagtcccatattaacgatctcc\n\ +aatgtacaaaatcgacaatcgctttgcattacccggttactagtcgaattacagatagct\n\ +gttagatactcactctaattttggacaacaatcccaatcttggggtcgtctatcgcctga\n\ +agctcgtaaatccttccatcttaaacgattacatattatagacttgttcggggtagagat\n\ +atcacagttgtgcaaacattgtaaatcgatactagtttatgttggtagtctagttgcttt\n\ +taccattccccgaaaaacttgatctactatttcgacaacagtaaacttgaactaggtaag\n\ +tgaaaacagagaatgcctcatagtgccactatttgtccactatatgtaagtgtagcttta\n\ +cataatccactatgactgagatcattacggcctaggaaagcagcgtagaaaaaaagggcc\n\ +cggatattacgactgtaactataaaactagttactggtagcgcgccatgtatagatttgt\n\ +tttaccggttgtggttgcgttaacgaatttcagccgcgaaaattgatccgttaaccagtc\n\ +catctcgacttctataaaacgataaagtaaagttgatgttcagcctccttcttatggttg\n\ +catcgagagtacactactcagtgggaaatagatcggggttcctacttcagattgtattat\n\ +ctaggcaattgccgattgtgccatacctggataaaataagctacctacatgtgatgctta\n\ +tctattatcgtcatactaccttagggtgtcctgttgaacgctacattaatctttagccgt\n\ +ttgagatgttccaatggataggagtctaacgcatgatgaagtttaggaaggcagagcatc\n\ +ccactaagtatgtgacagtgtatttcgaaacgagacgttataaatagaaaaaaggtcctt\n\ +ctggttctattctgctgaactattgaatggaaagattggttgacctacgtactatttgct\n\ +tgaagtcatcaatttgacggggtgagagacatatggtgcatactttacggactctatatt\n\ +ttagatcagaagcttagcagtcttctctacaccccctcacgacataattgcttttaagaa\n\ +tctatgtttgattcctctacgggaattcggatccgttcgcatgtgcggtttatctaaacc\n\ +aggggacatatgttcagctaaagcatacgaacactttgctaactagacgtatgtatagta\n\ +gctataaatcccgacgatatttacaaaaagaaatgagactcaaatatatacatagcgacc\n\ +ctacacttattcgcaccctgatctaggcgatcctagcacccacacccgaaagtgagcact\n\ +agtgtcttccgtattaaatttactgcagttgagattttagttgtctactaaggattactc\n\ +taacccgtaataaggatcaagactcggtactagctttactatcattccctatgtgttttc\n\ +ctaactcacaagggtacgtaccagcctatgtaattacaataatgataaagacacaaagga\n\ +agtaactttacaaatgagtctccagttacactagcttagtccctcccatcttgctttgaa\n\ +gtctaaatacgcaatctctgaggatatacagcagaagaacactcataacgttggagtcca\n\ +agaattagactcatagggcccccaacatttaatatgtactgtgagtttgaaggtgttcta\n\ +ttgttaattcctgctcttgatacatgacacgtactccgtgtttaaggcttcggactgact\n\ +ttctttcataagttgagcaacgaaaatttcagaatcgataagttggattcactaactaat\n\ +acggctgattgaaaactccactccggacctatatggtcgacctttatacgtaaccgatat\n\ +aaaacttataggctggtatatcgagccttcctagcgcaatttcggatggggtttcttcta\n\ +ctactcaacaacggaatagtctttgtttagtaaaccagagctcaggacgcccaatacgta\n\ +ggagagcgctgtggagcatgtgtcattatggactggagcactcttaaatcactctgcgtg\n\ +tgctaaacgatagatcataacatgtcctgagtaaattttcttgatacgtcgcaatatacc\n\ +gttattagttaaacgttctcatccgtcatgcgtgaaatacggctgtcgtgctcagatata\n\ +ctattagcgactcatctcgcctaacacgcacacgtataaactcggaatgactgccgctct\n\ +tacatattagaaatacagactacaccacggaagcattgggtcattctcaaccgctgtata\n\ +aaagatgattagtcttataataagattaccaaagaggcagaatcatgggtagtaaatcta\n\ +ttattcaagtgattaccgtcgtgtaggcagggagtgaggacgagatggtactcaggacaa\n\ +atattaaccggacgaagtggtttacgtcgtactttcactattagtagtaaatacaaggta\n\ +acaccggggaatagtactaaatataatgatatctatcttcgggagaacgagtcgtctatt\n\ +gctttgaacattctcaaggcgtaaaatgtgctgacttatagcatgatacaaccgattgtt\n\ +acttttgtctattcaaaagattgaatagttttttatacaaaagccgcatacttatgacgg\n\ +ctagtatacagtttcatcccctagcatcaatgctatggacagtattgaacttataggaaa\n\ +ttcttctaatagggcaaatccgtcgtgatgcctattttttttcagtcacatcctcaaatg\n\ +gcactagtattgtcgggatcccattaacaggctcaaccacgagctcacgcgaggacatgt\n\ +agtccgtatctttaacgaagcgacagcgacagaactcccatggataaccaattataaggc\n\ +ccgtaatcctctagacatcgtttaccaataaatccgctttctccgtaatcatgttgaata\n\ +ccccagagtagtccagatgataaccgatgaaacacaagtctttctcaatgcacttacggt\n\ +gaacttattaccgccaacgtagctcatcaaggttgcgacatctagttgtgtgtttgcgac\n\ +gagcccagcgaacttcatcaactttcgtatattcaacgccttgtaattttactttaagac\n\ +gcctggtgatgtagattcttagataatcagtttgttatcggctgtactttaccataattt\n\ +cacaggtttcaggtcaagaagattatagctgtatatacagttccatgctcggtgcacaga\n\ +aacgtgatcggataataatcaatcgcttatgtcgtctttaggcgtatccaatacatgccc\n\ +cgataccgcagtgtatttcgacatgtaggtataccgtcgcatttgagctcgagtcaggac\n\ +gtcagctagattagattccttaatagaatataccgacctctagtccgaactaaactatag\n\ +ataacgccaacttcaggttaattgtctagtcgtctgtttgcagatgggattcttagatga\n\ +gtgagtatcggccatattggttcgagcactttagtttttgatgcataggatatgcaatgt\n\ +atagctgaaagtactttatctgtttcaaactcacattgattaaaccggtaaacctttaaa\n\ +gactacaagaaaatattcagtgagggcaattttgtcaatcacaatcttccagctagagat\n\ +acttcacaatttgtcttgaggctacgcaacattagacggattttcgcgttttattgaaat\n\ +aatcgaggggcccaagagtatccatagttcattttgtaagatttctttacaggcttatta\n\ +cagcttcttcagactcctacatgcttacgagttatatgctagcatgtgaacaatagatta\n\ +atatacaggaaaacgtacattgagagagatgaccctacacagcgcaaccgttgagtactt\n\ +tcattaaagggtaacgctctcgagacagcatccttaagatggccttattgtcaaatcatt\n\ +tgcagaagtacgcaagatccctaaccaacgtagaagaatccctacaaacacatgagacgc\n\ +ggtgaaaatagacagggtgttagtattcaatcttcggagtatcaatttcgccaatcttgg\n\ +tgagaaagcataccctttcttcagagaaagaagatcaatcataacactatctttaacgag\n\ +gtacgcacgcgcatcattacctgcctccatggatctttaggatagcggaaagtattggca\n\ +gcgtattgtgatttcgttcctactttatcaatttcacattcatatacatgtcttttatca\n\ +aaatcgccaataagataggatgagctatattagatgctagtagagttcgcgccaacatca\n\ +tcgataggaatactcaggacagcgtgataggacttttcaatccctaatactctctataat\n\ +tataactctctcttaagtttggaggcagtaacgcgctctatataatcagtttgctgcacc\n\ +attcttcagcctctgatacatacaaataaattccacagcagtaagagggtttaattgaga\n\ +catcttgggaacttaggattttactctaacatcaccgaaacgattattggataccgtacc\n\ +taaacgaactttctcaaggcagtaatataggacatccgcaataacacaaatgctgcctcc\n\ +ccaggagttatgtcttcctggaggctatatcttacacccactcactataggcaaactaaa\n\ +gtttaaatgttgattgtctaaaaaaaagatagataagagttggccggcgtagcacatgcg\n\ +aaagtgaatcgtaagctataattctctggacttgaagttctgtcctgttcctctgcaaga\n\ +aacaaacttcctttaaagctatttacgacgcacatctcagcaagttataaacatgttgga\n\ +agtttctagtcggaattcccaaagaacggatctatctaatgcattcctacatttttcctg\n\ +tctgccgatggtgccatcctattcaaagaatttcttaaaagtagattaaatgggactttt\n\ +aacaatgagtaaccttacgcctctaagggttcctcgagtgccatacaccagtcaggtccg\n\ +agccacatacacggagaacattctaacatagcattctcaactcgatcatttgcaggttac\n\ +ttctttcctatcctagtgctaaaaatcatacttgcaatcccatagcacggattaagaacc\n\ +taagaaacaattcagtaaaacatgttcgaattcttggtatgggaacatcattgcagctat\n\ +ggtctaacgcattaatgtttgggtacatcttccatcatataaacaggaagagtctgacga\n\ +cagggagtgcttgcgatcatgtctatcattgtgaaatcaaattgtagctcacatgtcgtc\n\ +tatgagagcgtgtatccgataagatttagaaaaatagaagtcgtataagatctcactgaa\n\ +cttttgaatgaatgtgaagcatatatgatctgctttaataaaactttatccataggatac\n\ +gtttccaaatcaattcaataattattagtcaaaatagataaggatgaacaacctgaaggc\n\ +cgatcggacgtagaaagtggtcccatcactttgagttgatattgttgaaccacacgttat\n\ +tatggttttcaaacagtctcaggatattgtatatacagataatccgataccagttgtctg\n\ +acgcccctcttacgtaccccaccctttgtgacgtttaaagcagttgttcagtattttaaa\n\ +ctaggcggcaactaatttggaaagaagcacagtggatatgtctaaattcttgttattcag\n\ +gcctgaatttaatacaccgcatagttaacttcgcggtagagttgttcatcatgcctcctc\n\ +taagctaccacttctatgatacaccaatagttgttctacggaatctgataattggccaag\n\ +tcataaacttccgctgcgttcaacccccttgctcgaatatccaactcgaaaagacagcct\n\ +tttggtgtccggaacaaatcagttacttcttttctgatgttaattctctgtggtcagata\n\ +cagaccaaaaactccgcggatttaccatcctccaagaacaaatttgcatcaacatagcat\n\ +tttggctacatattctaagtctcaatagtttaggttttcaactacattatcccaacatta\n\ +ggattggaggaataatagctgggtaagtccccttgcgtctacaatcgactattttttatg\n\ +aatatgcttctgccgcacctatggttattaaaaaagtcatgactttgaagaaccctgaaa\n\ +agatagatgaatcaggtgtaatggcagcagccaaagagcatataattagcaacactctaa\n\ +gaacattatagatatgatgatagcgatcgtcatgatgttatccggtcacaatagtagctt\n\ +catcagctaattcgttttgccagtggtgacttgcgctggaagaatcgttatacggtccct\n\ +tccctcttgatacggtgggggcttattcaaccgcgtggattgggttgtcatacttgcatt\n\ +aaacgatgtaaaccatctagtagtcaactatactaaatcacaaaatagtgatcaatacat\n\ +acccgcttcatggttttaaccatttaattgattaaagatattccgctaagaaccattatc\n\ +tacctaaactgatcgccgtatcctagtagtttgaaatttgatgtaccgtaatgatcaacg\n\ +aagtaaaacgttatattgtatgtagaataataggtcttggagctaaatgatgtgattggt\n\ +agtgaagacttacccttacaactttaccggtttctcggaagaatatactagagaatcaat\n\ +gcatgggctacataagcactttagtctaatgagataaaaaatacacgagtcttccatcat\n\ +gaattttttgtcgaaaaactcgaacctggtaatttaaaccatatatctttatgtcgtcaa\n\ +taactctcatatgttttatataacttcccaatcacgacttgtaactgcttgttcgactga\n\ +gctgtttgagctatgaggccgggatccggttgagctacatctatttgctacaagaaaaat\n\ +gaaagcacatttgttgggagttctggctacactcatagagaaataagtggcccgagtggg\n\ +tgcggcctgcctccatattcaagtgtatcttaaaccaagtggttccaacgctcgcgctaa\n\ +agaattaaagcctttatttcctccacggagtagcccgtaatccggttcgaaagagaccat\n\ +tgaagttaattttcatatccagtgaagtttaggcacaagcatgtgttctgccacatgcct\n\ +caaagcgctcttcaaccaagatatgattcatcctaacttcgatgaatgcgtctgtaacat\n\ +aaatatagaaggaatgattcggcgagttaattttcgccttctccaacatggcatccctac\n\ +gttcgttataaggaccatacatgtaggttttaaaggtttgcggttaatcgatatttacat\n\ +catagaaattctatagtcaaatttacaagactctagatactcactcgttgcagccggcta\n\ +ggaagcgctttgtaccttacttcccttttcgttgcgtaatatgaatttcatatagtaagt\n\ +tcaaggcactcatacctccgtgaagagggtagatagactattaaagttgtttaatagtac\n\ +gtattgatggaaatgacccgtaggagatttaccactcaatccacaagattcgctgctgtg\n\ +cattatcaaaacagtgcatgtcgaaacatgggttgggtccttcaaacacgaatccaggta\n\ +gagatacctttgcaattttt\n"; + +dnaInput = dnaInput + dnaInput + dnaInput; + +var ilen, clen, + seqs = [ + /agggtaaa|tttaccct/ig, + /[cgt]gggtaaa|tttaccc[acg]/ig, + /a[act]ggtaaa|tttacc[agt]t/ig, + /ag[act]gtaaa|tttac[agt]ct/ig, + /agg[act]taaa|ttta[agt]cct/ig, + /aggg[acg]aaa|ttt[cgt]ccct/ig, + /agggt[cgt]aa|tt[acg]accct/ig, + /agggta[cgt]a|t[acg]taccct/ig, + /agggtaa[cgt]|[acg]ttaccct/ig], + subs = { + B: '(c|g|t)', D: '(a|g|t)', H: '(a|c|t)', K: '(g|t)', + M: '(a|c)', N: '(a|c|g|t)', R: '(a|g)', S: '(c|t)', + V: '(a|c|g)', W: '(a|t)', Y: '(c|t)' } + +ilen = dnaInput.length; + +// There is no in-place substitution +dnaInput = dnaInput.replace(/>.*\n|\n/g,"") +clen = dnaInput.length + +var dnaOutputString; + +for(i in seqs) + dnaOutputString += seqs[i].source + " " + (dnaInput.match(seqs[i]) || []).length + "\n"; + // match returns null if no matches, so replace with empty + +for(k in subs) + dnaInput = dnaInput.replace(k, subs[k], "g") + // search string, replacement string, flags + + +var _sunSpiderInterval = new Date() - _sunSpiderStartDate; + +document.getElementById("console").innerHTML = _sunSpiderInterval; +</script> + + +</body> +</html> diff --git a/build/pgo/js-input/string-base64.html b/build/pgo/js-input/string-base64.html new file mode 100644 index 000000000..a09439b53 --- /dev/null +++ b/build/pgo/js-input/string-base64.html @@ -0,0 +1,185 @@ +<!DOCTYPE html> +<head> +<!-- + Copyright (C) 2007 Apple Inc. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--> + +<title>SunSpider string-base64</title> + +</head> + +<body> +<h3>string-base64</h3> +<div id="console"> +</div> + +<script> + +var _sunSpiderStartDate = new Date(); + +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla XML-RPC Client component. + * + * The Initial Developer of the Original Code is + * Digital Creations 2, Inc. + * Portions created by the Initial Developer are Copyright (C) 2000 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Martijn Pieters <mj@digicool.com> (original author) + * Samuel Sieb <samuel@sieb.net> + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +// From: http://lxr.mozilla.org/mozilla/source/extensions/xml-rpc/src/nsXmlRpcClient.js#956 + +/* Convert data (an array of integers) to a Base64 string. */ +var toBase64Table = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'; +var base64Pad = '='; + +function toBase64(data) { + var result = ''; + var length = data.length; + var i; + // Convert every three bytes to 4 ascii characters. + for (i = 0; i < (length - 2); i += 3) { + result += toBase64Table[data[i] >> 2]; + result += toBase64Table[((data[i] & 0x03) << 4) + (data[i+1] >> 4)]; + result += toBase64Table[((data[i+1] & 0x0f) << 2) + (data[i+2] >> 6)]; + result += toBase64Table[data[i+2] & 0x3f]; + } + + // Convert the remaining 1 or 2 bytes, pad out to 4 characters. + if (length%3) { + i = length - (length%3); + result += toBase64Table[data[i] >> 2]; + if ((length%3) == 2) { + result += toBase64Table[((data[i] & 0x03) << 4) + (data[i+1] >> 4)]; + result += toBase64Table[(data[i+1] & 0x0f) << 2]; + result += base64Pad; + } else { + result += toBase64Table[(data[i] & 0x03) << 4]; + result += base64Pad + base64Pad; + } + } + + return result; +} + +/* Convert Base64 data to a string */ +var toBinaryTable = [ + -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, + -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, + -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,62, -1,-1,-1,63, + 52,53,54,55, 56,57,58,59, 60,61,-1,-1, -1, 0,-1,-1, + -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11,12,13,14, + 15,16,17,18, 19,20,21,22, 23,24,25,-1, -1,-1,-1,-1, + -1,26,27,28, 29,30,31,32, 33,34,35,36, 37,38,39,40, + 41,42,43,44, 45,46,47,48, 49,50,51,-1, -1,-1,-1,-1 +]; + +function base64ToString(data) { + var result = ''; + var leftbits = 0; // number of bits decoded, but yet to be appended + var leftdata = 0; // bits decoded, but yet to be appended + + // Convert one by one. + for (var i = 0; i < data.length; i++) { + var c = toBinaryTable[data.charCodeAt(i) & 0x7f]; + var padding = (data[i] == base64Pad); + // Skip illegal characters and whitespace + if (c == -1) continue; + + // Collect data into leftdata, update bitcount + leftdata = (leftdata << 6) | c; + leftbits += 6; + + // If we have 8 or more bits, append 8 bits to the result + if (leftbits >= 8) { + leftbits -= 8; + // Append if not padding. + if (!padding) + result += String.fromCharCode((leftdata >> leftbits) & 0xff); + leftdata &= (1 << leftbits) - 1; + } + } + + // If there are any bits left, the base64 string was corrupted + if (leftbits) + throw Components.Exception('Corrupted base64 string'); + + return result; +} + +var str = ""; + +for ( var i = 0; i < 8192; i++ ) + str += String.fromCharCode( (25 * Math.random()) + 97 ); + +for ( var i = 8192; i <= 16384; i *= 2 ) { + + var base64; + + base64 = toBase64(str); + base64ToString(base64); + + // Double the string + str += str; +} + +toBinaryTable = null; + + +var _sunSpiderInterval = new Date() - _sunSpiderStartDate; + +document.getElementById("console").innerHTML = _sunSpiderInterval; +</script> + + +</body> +</html> diff --git a/build/pgo/js-input/string-fasta.html b/build/pgo/js-input/string-fasta.html new file mode 100644 index 000000000..240e60147 --- /dev/null +++ b/build/pgo/js-input/string-fasta.html @@ -0,0 +1,135 @@ +<!DOCTYPE html> +<head> +<!-- + Copyright (C) 2007 Apple Inc. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--> + +<title>SunSpider string-fasta</title> + +</head> + +<body> +<h3>string-fasta</h3> +<div id="console"> +</div> + +<script> + +var _sunSpiderStartDate = new Date(); + +// The Great Computer Language Shootout +// http://shootout.alioth.debian.org +// +// Contributed by Ian Osgood + +var last = 42, A = 3877, C = 29573, M = 139968; + +function rand(max) { + last = (last * A + C) % M; + return max * last / M; +} + +var ALU = + "GGCCGGGCGCGGTGGCTCACGCCTGTAATCCCAGCACTTTGG" + + "GAGGCCGAGGCGGGCGGATCACCTGAGGTCAGGAGTTCGAGA" + + "CCAGCCTGGCCAACATGGTGAAACCCCGTCTCTACTAAAAAT" + + "ACAAAAATTAGCCGGGCGTGGTGGCGCGCGCCTGTAATCCCA" + + "GCTACTCGGGAGGCTGAGGCAGGAGAATCGCTTGAACCCGGG" + + "AGGCGGAGGTTGCAGTGAGCCGAGATCGCGCCACTGCACTCC" + + "AGCCTGGGCGACAGAGCGAGACTCCGTCTCAAAAA"; + +var IUB = { + a:0.27, c:0.12, g:0.12, t:0.27, + B:0.02, D:0.02, H:0.02, K:0.02, + M:0.02, N:0.02, R:0.02, S:0.02, + V:0.02, W:0.02, Y:0.02 +} + +var HomoSap = { + a: 0.3029549426680, + c: 0.1979883004921, + g: 0.1975473066391, + t: 0.3015094502008 +} + +function makeCumulative(table) { + var last = null; + for (var c in table) { + if (last) table[c] += table[last]; + last = c; + } +} + +function fastaRepeat(n, seq) { + var seqi = 0, lenOut = 60; + while (n>0) { + if (n<lenOut) lenOut = n; + if (seqi + lenOut < seq.length) { + ret = seq.substring(seqi, seqi+lenOut); + seqi += lenOut; + } else { + var s = seq.substring(seqi); + seqi = lenOut - s.length; + ret = s + seq.substring(0, seqi); + } + n -= lenOut; + } +} + +function fastaRandom(n, table) { + var line = new Array(60); + makeCumulative(table); + while (n>0) { + if (n<line.length) line = new Array(n); + for (var i=0; i<line.length; i++) { + var r = rand(1); + for (var c in table) { + if (r < table[c]) { + line[i] = c; + break; + } + } + } + ret = line.join(''); + n -= line.length; + } +} + +var ret; + +var count = 7; +ret = fastaRepeat(2*count*100000, ALU); +ret = fastaRandom(3*count*1000, IUB); +ret = fastaRandom(5*count*1000, HomoSap); + + + +var _sunSpiderInterval = new Date() - _sunSpiderStartDate; + +document.getElementById("console").innerHTML = _sunSpiderInterval; +</script> + + +</body> +</html> diff --git a/build/pgo/js-input/string-tagcloud.html b/build/pgo/js-input/string-tagcloud.html new file mode 100644 index 000000000..893a927ac --- /dev/null +++ b/build/pgo/js-input/string-tagcloud.html @@ -0,0 +1,315 @@ +<!DOCTYPE html> +<head> +<!-- + Copyright (C) 2007 Apple Inc. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--> + +<title>SunSpider string-tagcloud</title> + +</head> + +<body> +<h3>string-tagcloud</h3> +<div id="console"> +</div> + +<script> + +var _sunSpiderStartDate = new Date(); + + +/* + * Copyright (C) 2007 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + Portions from: + json.js + 2007-10-10 + + Public Domain +*/ + +// This test parses a JSON string giving tag names and popularity, and +// generates html markup for a "tagcloud" view. + +if (!Object.prototype.toJSONString) { + + Array.prototype.toJSONString = function (w) { + var a = [], // The array holding the partial texts. + i, // Loop counter. + l = this.length, + v; // The value to be stringified. + + for (i = 0; i < l; i += 1) { + v = this[i]; + switch (typeof v) { + case 'object': + + if (v && typeof v.toJSONString === 'function') { + a.push(v.toJSONString(w)); + } else { + a.push('null'); + } + break; + + case 'string': + case 'number': + case 'boolean': + a.push(v.toJSONString()); + break; + default: + a.push('null'); + } + } + + return '[' + a.join(',') + ']'; + }; + + + Boolean.prototype.toJSONString = function () { + return String(this); + }; + + + Date.prototype.toJSONString = function () { + + function f(n) { + + return n < 10 ? '0' + n : n; + } + + return '"' + this.getUTCFullYear() + '-' + + f(this.getUTCMonth() + 1) + '-' + + f(this.getUTCDate()) + 'T' + + f(this.getUTCHours()) + ':' + + f(this.getUTCMinutes()) + ':' + + f(this.getUTCSeconds()) + 'Z"'; + }; + + + Number.prototype.toJSONString = function () { + + return isFinite(this) ? String(this) : 'null'; + }; + + + Object.prototype.toJSONString = function (w) { + var a = [], // The array holding the partial texts. + k, // The current key. + i, // The loop counter. + v; // The current value. + + if (w) { + for (i = 0; i < w.length; i += 1) { + k = w[i]; + if (typeof k === 'string') { + v = this[k]; + switch (typeof v) { + case 'object': + + if (v) { + if (typeof v.toJSONString === 'function') { + a.push(k.toJSONString() + ':' + + v.toJSONString(w)); + } + } else { + a.push(k.toJSONString() + ':null'); + } + break; + + case 'string': + case 'number': + case 'boolean': + a.push(k.toJSONString() + ':' + v.toJSONString()); + + } + } + } + } else { + + for (k in this) { + if (typeof k === 'string' && + Object.prototype.hasOwnProperty.apply(this, [k])) { + v = this[k]; + switch (typeof v) { + case 'object': + + if (v) { + if (typeof v.toJSONString === 'function') { + a.push(k.toJSONString() + ':' + + v.toJSONString()); + } + } else { + a.push(k.toJSONString() + ':null'); + } + break; + + case 'string': + case 'number': + case 'boolean': + a.push(k.toJSONString() + ':' + v.toJSONString()); + + } + } + } + } + + return '{' + a.join(',') + '}'; + }; + + + (function (s) { + + var m = { + '\b': '\\b', + '\t': '\\t', + '\n': '\\n', + '\f': '\\f', + '\r': '\\r', + '"' : '\\"', + '\\': '\\\\' + }; + + + s.parseJSON = function (filter) { + var j; + + function walk(k, v) { + var i, n; + if (v && typeof v === 'object') { + for (i in v) { + if (Object.prototype.hasOwnProperty.apply(v, [i])) { + n = walk(i, v[i]); + if (n !== undefined) { + v[i] = n; + } + } + } + } + return filter(k, v); + } + + if (/^[\],:{}\s]*$/.test(this.replace(/\\./g, '@'). + replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(:?[eE][+\-]?\d+)?/g, ']'). + replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) { + + j = eval('(' + this + ')'); + + return typeof filter === 'function' ? walk('', j) : j; + } + + throw new SyntaxError('parseJSON'); + }; + + + s.toJSONString = function () { + + if (/["\\\x00-\x1f]/.test(this)) { + return '"' + this.replace(/[\x00-\x1f\\"]/g, function (a) { + var c = m[a]; + if (c) { + return c; + } + c = a.charCodeAt(); + return '\\u00' + Math.floor(c / 16).toString(16) + + (c % 16).toString(16); + }) + '"'; + } + return '"' + this + '"'; + }; + })(String.prototype); +} + +var tagInfoJSON = '[\n {\n \"tag\": "titillation",\n \"popularity\": 4294967296\n },\n {\n \"tag\": "foamless",\n \"popularity\": 1257718401\n },\n {\n \"tag\": "snarler",\n \"popularity\": 613166183\n },\n {\n \"tag\": "multangularness",\n \"popularity\": 368304452\n },\n {\n \"tag\": "Fesapo unventurous",\n \"popularity\": 248026512\n },\n {\n \"tag\": "esthesioblast",\n \"popularity\": 179556755\n },\n {\n \"tag\": "echeneidoid",\n \"popularity\": 136641578\n },\n {\n \"tag\": "embryoctony",\n \"popularity\": 107852576\n },\n {\n \"tag\": "undilatory",\n \"popularity\": 87537981\n },\n {\n \"tag\": "predisregard",\n \"popularity\": 72630939\n },\n {\n \"tag\": "allergenic",\n \"popularity\": 61345190\n },\n {\n \"tag\": "uncloudy",\n \"popularity\": 52580571\n },\n {\n \"tag\": "unforeseeably",\n \"popularity\": 45628109\n },\n {\n \"tag\": "sturniform",\n \"popularity\": 40013489\n },\n {\n \"tag\": "anesthetize",\n \"popularity\": 35409226\n },\n {\n \"tag\": "ametabolia",\n \"popularity\": 31583050\n },\n {\n \"tag\": "angiopathy",\n \"popularity\": 28366350\n },\n {\n \"tag\": "sultanaship",\n \"popularity\": 25634218\n },\n {\n \"tag\": "Frenchwise",\n \"popularity\": 23292461\n },\n {\n \"tag\": "cerviconasal",\n \"popularity\": 21268909\n },\n {\n \"tag\": "mercurialness",\n \"popularity\": 19507481\n },\n {\n \"tag\": "glutelin venditate",\n \"popularity\": 17964042\n },\n {\n \"tag\": "acred overblack",\n \"popularity\": 16603454\n },\n {\n \"tag\": "Atik",\n \"popularity\": 15397451\n },\n {\n \"tag\": "puncturer",\n \"popularity\": 14323077\n },\n {\n \"tag\": "pukatea",\n \"popularity\": 13361525\n },\n {\n \"tag\": "suberize",\n \"popularity\": 12497261\n },\n {\n \"tag\": "Godfrey",\n \"popularity\": 11717365\n },\n {\n \"tag\": "tetraptote",\n \"popularity\": 11011011\n },\n {\n \"tag\": "lucidness",\n \"popularity\": 10369074\n },\n {\n \"tag\": "tartness",\n \"popularity\": 9783815\n },\n {\n \"tag\": "axfetch",\n \"popularity\": 9248634\n },\n {\n \"tag\": "preacquittal",\n \"popularity\": 8757877\n },\n {\n \"tag\": "matris",\n \"popularity\": 8306671\n },\n {\n \"tag\": "hyphenate",\n \"popularity\": 7890801\n },\n {\n \"tag\": "semifabulous",\n \"popularity\": 7506606\n },\n {\n \"tag\": "oppressiveness",\n \"popularity\": 7150890\n },\n {\n \"tag\": "Protococcales",\n \"popularity\": 6820856\n },\n {\n \"tag\": "unpreventive",\n \"popularity\": 6514045\n },\n {\n \"tag\": "Cordia",\n \"popularity\": 6228289\n },\n {\n \"tag\": "Wakamba leaflike",\n \"popularity\": 5961668\n },\n {\n \"tag\": "dacryoma",\n \"popularity\": 5712480\n },\n {\n \"tag\": "inguinal",\n \"popularity\": 5479211\n },\n {\n \"tag\": "responseless",\n \"popularity\": 5260507\n },\n {\n \"tag\": "supplementarily",\n \"popularity\": 5055158\n },\n {\n \"tag\": "emu",\n \"popularity\": 4862079\n },\n {\n \"tag\": "countermeet",\n \"popularity\": 4680292\n },\n {\n \"tag\": "purrer",\n \"popularity\": 4508918\n },\n {\n \"tag\": "Corallinaceae",\n \"popularity\": 4347162\n },\n {\n \"tag\": "speculum",\n \"popularity\": 4194304\n },\n {\n \"tag\": "crimpness",\n \"popularity\": 4049690\n },\n {\n \"tag\": "antidetonant",\n \"popularity\": 3912727\n },\n {\n \"tag\": "topeewallah",\n \"popularity\": 3782875\n },\n {\n \"tag\": "fidalgo ballant",\n \"popularity\": 3659640\n },\n {\n \"tag\": "utriculose",\n \"popularity\": 3542572\n },\n {\n \"tag\": "testata",\n \"popularity\": 3431259\n },\n {\n \"tag\": "beltmaking",\n \"popularity\": 3325322\n },\n {\n \"tag\": "necrotype",\n \"popularity\": 3224413\n },\n {\n \"tag\": "ovistic",\n \"popularity\": 3128215\n },\n {\n \"tag\": "swindlership",\n \"popularity\": 3036431\n },\n {\n \"tag\": "augustal",\n \"popularity\": 2948792\n },\n {\n \"tag\": "Titoist",\n \"popularity\": 2865047\n },\n {\n \"tag\": "trisoctahedral",\n \"popularity\": 2784963\n },\n {\n \"tag\": "sequestrator",\n \"popularity\": 2708327\n },\n {\n \"tag\": "sideburns",\n \"popularity\": 2634939\n },\n {\n \"tag\": "paraphrasia",\n \"popularity\": 2564616\n },\n {\n \"tag\": "graminology unbay",\n \"popularity\": 2497185\n },\n {\n \"tag\": "acaridomatium emargination",\n \"popularity\": 2432487\n },\n {\n \"tag\": "roofward",\n \"popularity\": 2370373\n },\n {\n \"tag\": "lauder",\n \"popularity\": 2310705\n },\n {\n \"tag\": "subjunctive",\n \"popularity\": 2253354\n },\n {\n \"tag\": "subelongate",\n \"popularity\": 2198199\n },\n {\n \"tag\": "guacimo",\n \"popularity\": 2145128\n },\n {\n \"tag\": "cockade",\n \"popularity\": 2094033\n },\n {\n \"tag\": "misgauge",\n \"popularity\": 2044818\n },\n {\n \"tag\": "unexpensive",\n \"popularity\": 1997388\n },\n {\n \"tag\": "chebel",\n \"popularity\": 1951657\n },\n {\n \"tag\": "unpursuing",\n \"popularity\": 1907543\n },\n {\n \"tag\": "kilobar",\n \"popularity\": 1864969\n },\n {\n \"tag\": "obsecration",\n \"popularity\": 1823863\n },\n {\n \"tag\": "nacarine",\n \"popularity\": 1784157\n },\n {\n \"tag\": "spirituosity",\n \"popularity\": 1745787\n },\n {\n \"tag\": "movableness deity",\n \"popularity\": 1708692\n },\n {\n \"tag\": "exostracism",\n \"popularity\": 1672816\n },\n {\n \"tag\": "archipterygium",\n \"popularity\": 1638104\n },\n {\n \"tag\": "monostrophic",\n \"popularity\": 1604506\n },\n {\n \"tag\": "gynecide",\n \"popularity\": 1571974\n },\n {\n \"tag\": "gladden",\n \"popularity\": 1540462\n },\n {\n \"tag\": "throughbred",\n \"popularity\": 1509927\n },\n {\n \"tag\": "groper",\n \"popularity\": 1480329\n },\n {\n \"tag\": "Xenosaurus",\n \"popularity\": 1451628\n },\n {\n \"tag\": "photoetcher",\n \"popularity\": 1423788\n },\n {\n \"tag\": "glucosid",\n \"popularity\": 1396775\n },\n {\n \"tag\": "Galtonian",\n \"popularity\": 1370555\n },\n {\n \"tag\": "mesosporic",\n \"popularity\": 1345097\n },\n {\n \"tag\": "theody",\n \"popularity\": 1320370\n },\n {\n \"tag\": "zaffer",\n \"popularity\": 1296348\n },\n {\n \"tag\": "probiology",\n \"popularity\": 1273003\n },\n {\n \"tag\": "rhizomic",\n \"popularity\": 1250308\n },\n {\n \"tag\": "superphosphate",\n \"popularity\": 1228240\n },\n {\n \"tag\": "Hippolytan",\n \"popularity\": 1206776\n },\n {\n \"tag\": "garget",\n \"popularity\": 1185892\n },\n {\n \"tag\": "diploplacula",\n \"popularity\": 1165568\n },\n {\n \"tag\": "orohydrographical",\n \"popularity\": 1145785\n },\n {\n \"tag\": "enhypostatize",\n \"popularity\": 1126521\n },\n {\n \"tag\": "polisman",\n \"popularity\": 1107759\n },\n {\n \"tag\": "acetometer",\n \"popularity\": 1089482\n },\n {\n \"tag\": "unsnatched",\n \"popularity\": 1071672\n },\n {\n \"tag\": "yabber",\n \"popularity\": 1054313\n },\n {\n \"tag\": "demiwolf",\n \"popularity\": 1037390\n },\n {\n \"tag\": "chromascope",\n \"popularity\": 1020888\n },\n {\n \"tag\": "seamanship",\n \"popularity\": 1004794\n },\n {\n \"tag\": "nonfenestrated",\n \"popularity\": 989092\n },\n {\n \"tag\": "hydrophytism",\n \"popularity\": 973771\n },\n {\n \"tag\": "dotter",\n \"popularity\": 958819\n },\n {\n \"tag\": "thermoperiodism",\n \"popularity\": 944222\n },\n {\n \"tag\": "unlawyerlike",\n \"popularity\": 929970\n },\n {\n \"tag\": "enantiomeride citywards",\n \"popularity\": 916052\n },\n {\n \"tag\": "unmetallurgical",\n \"popularity\": 902456\n },\n {\n \"tag\": "prickled",\n \"popularity\": 889174\n },\n {\n \"tag\": "strangerwise manioc",\n \"popularity\": 876195\n },\n {\n \"tag\": "incisorial",\n \"popularity\": 863510\n },\n {\n \"tag\": "irrationalize",\n \"popularity\": 851110\n },\n {\n \"tag\": "nasology",\n \"popularity\": 838987\n },\n {\n \"tag\": "fatuism",\n \"popularity\": 827131\n },\n {\n \"tag\": "Huk",\n \"popularity\": 815535\n },\n {\n \"tag\": "properispomenon",\n \"popularity\": 804192\n },\n {\n \"tag\": "unpummelled",\n \"popularity\": 793094\n },\n {\n \"tag\": "technographically",\n \"popularity\": 782233\n },\n {\n \"tag\": "underfurnish",\n \"popularity\": 771603\n },\n {\n \"tag\": "sinter",\n \"popularity\": 761198\n },\n {\n \"tag\": "lateroanterior",\n \"popularity\": 751010\n },\n {\n \"tag\": "nonpersonification",\n \"popularity\": 741034\n },\n {\n \"tag\": "Sitophilus",\n \"popularity\": 731264\n },\n {\n \"tag\": "unstudded overexerted",\n \"popularity\": 721694\n },\n {\n \"tag\": "tracheation",\n \"popularity\": 712318\n },\n {\n \"tag\": "thirteenth begloze",\n \"popularity\": 703131\n },\n {\n \"tag\": "bespice",\n \"popularity\": 694129\n },\n {\n \"tag\": "doppia",\n \"popularity\": 685305\n },\n {\n \"tag\": "unadorned",\n \"popularity\": 676656\n },\n {\n \"tag\": "dovelet engraff",\n \"popularity\": 668176\n },\n {\n \"tag\": "diphyozooid",\n \"popularity\": 659862\n },\n {\n \"tag\": "mure",\n \"popularity\": 651708\n },\n {\n \"tag\": "Tripitaka",\n \"popularity\": 643710\n },\n {\n \"tag\": "Billjim",\n \"popularity\": 635865\n },\n {\n \"tag\": "pyramidical",\n \"popularity\": 628169\n },\n {\n \"tag\": "circumlocutionist",\n \"popularity\": 620617\n },\n {\n \"tag\": "slapstick",\n \"popularity\": 613207\n },\n {\n \"tag\": "preobedience",\n \"popularity\": 605934\n },\n {\n \"tag\": "unfriarlike",\n \"popularity\": 598795\n },\n {\n \"tag\": "microchromosome",\n \"popularity\": 591786\n },\n {\n \"tag\": "Orphicism",\n \"popularity\": 584905\n },\n {\n \"tag\": "peel",\n \"popularity\": 578149\n },\n {\n \"tag\": "obediential",\n \"popularity\": 571514\n },\n {\n \"tag\": "Peripatidea",\n \"popularity\": 564997\n },\n {\n \"tag\": "undoubtful",\n \"popularity\": 558596\n },\n {\n \"tag\": "lodgeable",\n \"popularity\": 552307\n },\n {\n \"tag\": "pustulated woodchat",\n \"popularity\": 546129\n },\n {\n \"tag\": "antepast",\n \"popularity\": 540057\n },\n {\n \"tag\": "sagittoid matrimoniously",\n \"popularity\": 534091\n },\n {\n \"tag\": "Albizzia",\n \"popularity\": 528228\n },\n {\n \"tag\": "Elateridae unnewness",\n \"popularity\": 522464\n },\n {\n \"tag\": "convertingness",\n \"popularity\": 516798\n },\n {\n \"tag\": "Pelew",\n \"popularity\": 511228\n },\n {\n \"tag\": "recapitulation",\n \"popularity\": 505751\n },\n {\n \"tag\": "shack",\n \"popularity\": 500365\n },\n {\n \"tag\": "unmellowed",\n \"popularity\": 495069\n },\n {\n \"tag\": "pavis capering",\n \"popularity\": 489859\n },\n {\n \"tag\": "fanfare",\n \"popularity\": 484735\n },\n {\n \"tag\": "sole",\n \"popularity\": 479695\n },\n {\n \"tag\": "subarcuate",\n \"popularity\": 474735\n },\n {\n \"tag\": "multivious",\n \"popularity\": 469856\n },\n {\n \"tag\": "squandermania",\n \"popularity\": 465054\n },\n {\n \"tag\": "scintle",\n \"popularity\": 460329\n },\n {\n \"tag\": "hash chirognomic",\n \"popularity\": 455679\n },\n {\n \"tag\": "linseed",\n \"popularity\": 451101\n },\n {\n \"tag\": "redoubtable",\n \"popularity\": 446596\n },\n {\n \"tag\": "poachy reimpact",\n \"popularity\": 442160\n },\n {\n \"tag\": "limestone",\n \"popularity\": 437792\n },\n {\n \"tag\": "serranid",\n \"popularity\": 433492\n },\n {\n \"tag\": "pohna",\n \"popularity\": 429258\n },\n {\n \"tag\": "warwolf",\n \"popularity\": 425088\n },\n {\n \"tag\": "ruthenous",\n \"popularity\": 420981\n },\n {\n \"tag\": "dover",\n \"popularity\": 416935\n },\n {\n \"tag\": "deuteroalbumose",\n \"popularity\": 412950\n },\n {\n \"tag\": "pseudoprophetic",\n \"popularity\": 409025\n },\n {\n \"tag\": "dissoluteness",\n \"popularity\": 405157\n },\n {\n \"tag\": "preinvention",\n \"popularity\": 401347\n },\n {\n \"tag\": "swagbellied",\n \"popularity\": 397592\n },\n {\n \"tag\": "Ophidia",\n \"popularity\": 393892\n },\n {\n \"tag\": "equanimity",\n \"popularity\": 390245\n },\n {\n \"tag\": "troutful",\n \"popularity\": 386651\n },\n {\n \"tag\": "uke",\n \"popularity\": 383108\n },\n {\n \"tag\": "preacquaint",\n \"popularity\": 379616\n },\n {\n \"tag\": "shoq",\n \"popularity\": 376174\n },\n {\n \"tag\": "yox",\n \"popularity\": 372780\n },\n {\n \"tag\": "unelemental",\n \"popularity\": 369434\n },\n {\n \"tag\": "Yavapai",\n \"popularity\": 366134\n },\n {\n \"tag\": "joulean",\n \"popularity\": 362880\n },\n {\n \"tag\": "dracontine",\n \"popularity\": 359672\n },\n {\n \"tag\": "hardmouth",\n \"popularity\": 356507\n },\n {\n \"tag\": "sylvanize",\n \"popularity\": 353386\n },\n {\n \"tag\": "intraparenchymatous meadowbur",\n \"popularity\": 350308\n },\n {\n \"tag\": "uncharily",\n \"popularity\": 347271\n },\n {\n \"tag\": "redtab flexibly",\n \"popularity\": 344275\n },\n {\n \"tag\": "centervelic",\n \"popularity\": 341319\n },\n {\n \"tag\": "unravellable",\n \"popularity\": 338403\n },\n {\n \"tag\": "infortunately",\n \"popularity\": 335526\n },\n {\n \"tag\": "cannel",\n \"popularity\": 332687\n },\n {\n \"tag\": "oxyblepsia",\n \"popularity\": 329885\n },\n {\n \"tag\": "Damon",\n \"popularity\": 327120\n },\n {\n \"tag\": "etherin",\n \"popularity\": 324391\n },\n {\n \"tag\": "luminal",\n \"popularity\": 321697\n },\n {\n \"tag\": "interrogatorily presbyte",\n \"popularity\": 319038\n },\n {\n \"tag\": "hemiclastic",\n \"popularity\": 316414\n },\n {\n \"tag\": "poh flush",\n \"popularity\": 313823\n },\n {\n \"tag\": "Psoroptes",\n \"popularity\": 311265\n },\n {\n \"tag\": "dispirit",\n \"popularity\": 308740\n },\n {\n \"tag\": "nashgab",\n \"popularity\": 306246\n },\n {\n \"tag\": "Aphidiinae",\n \"popularity\": 303784\n },\n {\n \"tag\": "rhapsody nonconstruction",\n \"popularity\": 301353\n },\n {\n \"tag\": "Osmond",\n \"popularity\": 298952\n },\n {\n \"tag\": "Leonis",\n \"popularity\": 296581\n },\n {\n \"tag\": "Lemnian",\n \"popularity\": 294239\n },\n {\n \"tag\": "acetonic gnathonic",\n \"popularity\": 291926\n },\n {\n \"tag\": "surculus",\n \"popularity\": 289641\n },\n {\n \"tag\": "diagonally",\n \"popularity\": 287384\n },\n {\n \"tag\": "counterpenalty",\n \"popularity\": 285154\n },\n {\n \"tag\": "Eugenie",\n \"popularity\": 282952\n },\n {\n \"tag\": "hornbook",\n \"popularity\": 280776\n },\n {\n \"tag\": "miscoin",\n \"popularity\": 278626\n },\n {\n \"tag\": "admi",\n \"popularity\": 276501\n },\n {\n \"tag\": "Tarmac",\n \"popularity\": 274402\n },\n {\n \"tag\": "inexplicable",\n \"popularity\": 272328\n },\n {\n \"tag\": "rascallion",\n \"popularity\": 270278\n },\n {\n \"tag\": "dusterman",\n \"popularity\": 268252\n },\n {\n \"tag\": "osteostomous unhoroscopic",\n \"popularity\": 266250\n },\n {\n \"tag\": "spinibulbar",\n \"popularity\": 264271\n },\n {\n \"tag\": "phototelegraphically",\n \"popularity\": 262315\n },\n {\n \"tag\": "Manihot",\n \"popularity\": 260381\n },\n {\n \"tag\": "neighborhood",\n \"popularity\": 258470\n },\n {\n \"tag\": "Vincetoxicum",\n \"popularity\": 256581\n },\n {\n \"tag\": "khirka",\n \"popularity\": 254713\n },\n {\n \"tag\": "conscriptive",\n \"popularity\": 252866\n },\n {\n \"tag\": "synechthran",\n \"popularity\": 251040\n },\n {\n \"tag\": "Guttiferales",\n \"popularity\": 249235\n },\n {\n \"tag\": "roomful",\n \"popularity\": 247450\n },\n {\n \"tag\": "germinal",\n \"popularity\": 245685\n },\n {\n \"tag\": "untraitorous",\n \"popularity\": 243939\n },\n {\n \"tag\": "nondissenting",\n \"popularity\": 242213\n },\n {\n \"tag\": "amotion",\n \"popularity\": 240506\n },\n {\n \"tag\": "badious",\n \"popularity\": 238817\n },\n {\n \"tag\": "sumpit",\n \"popularity\": 237147\n },\n {\n \"tag\": "ectozoic",\n \"popularity\": 235496\n },\n {\n \"tag\": "elvet",\n \"popularity\": 233862\n },\n {\n \"tag\": "underclerk",\n \"popularity\": 232246\n },\n {\n \"tag\": "reticency",\n \"popularity\": 230647\n },\n {\n \"tag\": "neutroclusion",\n \"popularity\": 229065\n },\n {\n \"tag\": "unbelieving",\n \"popularity\": 227500\n },\n {\n \"tag\": "histogenetic",\n \"popularity\": 225952\n },\n {\n \"tag\": "dermamyiasis",\n \"popularity\": 224421\n },\n {\n \"tag\": "telenergy",\n \"popularity\": 222905\n },\n {\n \"tag\": "axiomatic",\n \"popularity\": 221406\n },\n {\n \"tag\": "undominoed",\n \"popularity\": 219922\n },\n {\n \"tag\": "periosteoma",\n \"popularity\": 218454\n },\n {\n \"tag\": "justiciaryship",\n \"popularity\": 217001\n },\n {\n \"tag\": "autoluminescence",\n \"popularity\": 215563\n },\n {\n \"tag\": "osmous",\n \"popularity\": 214140\n },\n {\n \"tag\": "borgh",\n \"popularity\": 212731\n },\n {\n \"tag\": "bedebt",\n \"popularity\": 211337\n },\n {\n \"tag\": "considerableness adenoidism",\n \"popularity\": 209957\n },\n {\n \"tag\": "sailorizing",\n \"popularity\": 208592\n },\n {\n \"tag\": "Montauk",\n \"popularity\": 207240\n },\n {\n \"tag\": "Bridget",\n \"popularity\": 205901\n },\n {\n \"tag\": "Gekkota",\n \"popularity\": 204577\n },\n {\n \"tag\": "subcorymbose",\n \"popularity\": 203265\n },\n {\n \"tag\": "undersap",\n \"popularity\": 201967\n },\n {\n \"tag\": "poikilothermic",\n \"popularity\": 200681\n },\n {\n \"tag\": "enneatical",\n \"popularity\": 199409\n },\n {\n \"tag\": "martinetism",\n \"popularity\": 198148\n },\n {\n \"tag\": "sustanedly",\n \"popularity\": 196901\n },\n {\n \"tag\": "declaration",\n \"popularity\": 195665\n },\n {\n \"tag\": "myringoplasty",\n \"popularity\": 194442\n },\n {\n \"tag\": "Ginkgo",\n \"popularity\": 193230\n },\n {\n \"tag\": "unrecurrent",\n \"popularity\": 192031\n },\n {\n \"tag\": "proprecedent",\n \"popularity\": 190843\n },\n {\n \"tag\": "roadman",\n \"popularity\": 189666\n },\n {\n \"tag\": "elemin",\n \"popularity\": 188501\n },\n {\n \"tag\": "maggot",\n \"popularity\": 187347\n },\n {\n \"tag\": "alitrunk",\n \"popularity\": 186204\n },\n {\n \"tag\": "introspection",\n \"popularity\": 185071\n },\n {\n \"tag\": "batiker",\n \"popularity\": 183950\n },\n {\n \"tag\": "backhatch oversettle",\n \"popularity\": 182839\n },\n {\n \"tag\": "thresherman",\n \"popularity\": 181738\n },\n {\n \"tag\": "protemperance",\n \"popularity\": 180648\n },\n {\n \"tag\": "undern",\n \"popularity\": 179568\n },\n {\n \"tag\": "tweeg",\n \"popularity\": 178498\n },\n {\n \"tag\": "crosspath",\n \"popularity\": 177438\n },\n {\n \"tag\": "Tangaridae",\n \"popularity\": 176388\n },\n {\n \"tag\": "scrutation",\n \"popularity\": 175348\n },\n {\n \"tag\": "piecemaker",\n \"popularity\": 174317\n },\n {\n \"tag\": "paster",\n \"popularity\": 173296\n },\n {\n \"tag\": "unpretendingness",\n \"popularity\": 172284\n },\n {\n \"tag\": "inframundane",\n \"popularity\": 171281\n },\n {\n \"tag\": "kiblah",\n \"popularity\": 170287\n },\n {\n \"tag\": "playwrighting",\n \"popularity\": 169302\n },\n {\n \"tag\": "gonepoiesis snowslip",\n \"popularity\": 168326\n },\n {\n \"tag\": "hoodwise",\n \"popularity\": 167359\n },\n {\n \"tag\": "postseason",\n \"popularity\": 166401\n },\n {\n \"tag\": "equivocality",\n \"popularity\": 165451\n },\n {\n \"tag\": "Opiliaceae nuclease",\n \"popularity\": 164509\n },\n {\n \"tag\": "sextipara",\n \"popularity\": 163576\n },\n {\n \"tag\": "weeper",\n \"popularity\": 162651\n },\n {\n \"tag\": "frambesia",\n \"popularity\": 161735\n },\n {\n \"tag\": "answerable",\n \"popularity\": 160826\n },\n {\n \"tag\": "Trichosporum",\n \"popularity\": 159925\n },\n {\n \"tag\": "cajuputol",\n \"popularity\": 159033\n },\n {\n \"tag\": "pleomorphous",\n \"popularity\": 158148\n },\n {\n \"tag\": "aculeolate",\n \"popularity\": 157270\n },\n {\n \"tag\": "wherever",\n \"popularity\": 156400\n },\n {\n \"tag\": "collapse",\n \"popularity\": 155538\n },\n {\n \"tag\": "porky",\n \"popularity\": 154683\n },\n {\n \"tag\": "perule",\n \"popularity\": 153836\n },\n {\n \"tag\": "Nevada",\n \"popularity\": 152996\n },\n {\n \"tag\": "conalbumin",\n \"popularity\": 152162\n },\n {\n \"tag\": "tsunami",\n \"popularity\": 151336\n },\n {\n \"tag\": "Gulf",\n \"popularity\": 150517\n },\n {\n \"tag\": "hertz",\n \"popularity\": 149705\n },\n {\n \"tag\": "limmock",\n \"popularity\": 148900\n },\n {\n \"tag\": "Tartarize",\n \"popularity\": 148101\n },\n {\n \"tag\": "entosphenoid",\n \"popularity\": 147310\n },\n {\n \"tag\": "ibis",\n \"popularity\": 146524\n },\n {\n \"tag\": "unyeaned",\n \"popularity\": 145746\n },\n {\n \"tag\": "tritural",\n \"popularity\": 144973\n },\n {\n \"tag\": "hundredary",\n \"popularity\": 144207\n },\n {\n \"tag\": "stolonlike",\n \"popularity\": 143448\n },\n {\n \"tag\": "chorister",\n \"popularity\": 142694\n },\n {\n \"tag\": "mismove",\n \"popularity\": 141947\n },\n {\n \"tag\": "Andine",\n \"popularity\": 141206\n },\n {\n \"tag\": "Annette proneur escribe",\n \"popularity\": 140471\n },\n {\n \"tag\": "exoperidium",\n \"popularity\": 139742\n },\n {\n \"tag\": "disedge",\n \"popularity\": 139019\n },\n {\n \"tag\": "hypochloruria",\n \"popularity\": 138302\n },\n {\n \"tag\": "prepupa",\n \"popularity\": 137590\n },\n {\n \"tag\": "assent",\n \"popularity\": 136884\n },\n {\n \"tag\": "hydrazobenzene",\n \"popularity\": 136184\n },\n {\n \"tag\": "emballonurid",\n \"popularity\": 135489\n },\n {\n \"tag\": "roselle",\n \"popularity\": 134800\n },\n {\n \"tag\": "unifiedly",\n \"popularity\": 134117\n },\n {\n \"tag\": "clang",\n \"popularity\": 133439\n },\n {\n \"tag\": "acetolytic",\n \"popularity\": 132766\n },\n {\n \"tag\": "cladodont",\n \"popularity\": 132098\n },\n {\n \"tag\": "recoast",\n \"popularity\": 131436\n },\n {\n \"tag\": "celebrated tydie Eocarboniferous",\n \"popularity\": 130779\n },\n {\n \"tag\": "superconsciousness",\n \"popularity\": 130127\n },\n {\n \"tag\": "soberness",\n \"popularity\": 129480\n },\n {\n \"tag\": "panoramist",\n \"popularity\": 128838\n },\n {\n \"tag\": "Orbitolina",\n \"popularity\": 128201\n },\n {\n \"tag\": "overlewd",\n \"popularity\": 127569\n },\n {\n \"tag\": "demiquaver",\n \"popularity\": 126942\n },\n {\n \"tag\": "kamelaukion",\n \"popularity\": 126319\n },\n {\n \"tag\": "flancard",\n \"popularity\": 125702\n },\n {\n \"tag\": "tricuspid",\n \"popularity\": 125089\n },\n {\n \"tag\": "bepelt",\n \"popularity\": 124480\n },\n {\n \"tag\": "decuplet",\n \"popularity\": 123877\n },\n {\n \"tag\": "Rockies",\n \"popularity\": 123278\n },\n {\n \"tag\": "unforgeability",\n \"popularity\": 122683\n },\n {\n \"tag\": "mocha",\n \"popularity\": 122093\n },\n {\n \"tag\": "scrunge",\n \"popularity\": 121507\n },\n {\n \"tag\": "delighter",\n \"popularity\": 120926\n },\n {\n \"tag\": "willey Microtinae",\n \"popularity\": 120349\n },\n {\n \"tag\": "unhuntable",\n \"popularity\": 119777\n },\n {\n \"tag\": "historically",\n \"popularity\": 119208\n },\n {\n \"tag\": "vicegerentship",\n \"popularity\": 118644\n },\n {\n \"tag\": "hemangiosarcoma",\n \"popularity\": 118084\n },\n {\n \"tag\": "harpago",\n \"popularity\": 117528\n },\n {\n \"tag\": "unionoid",\n \"popularity\": 116976\n },\n {\n \"tag\": "wiseman",\n \"popularity\": 116429\n },\n {\n \"tag\": "diclinism",\n \"popularity\": 115885\n },\n {\n \"tag\": "Maud",\n \"popularity\": 115345\n },\n {\n \"tag\": "scaphocephalism",\n \"popularity\": 114809\n },\n {\n \"tag\": "obtenebration",\n \"popularity\": 114277\n },\n {\n \"tag\": "cymar predreadnought",\n \"popularity\": 113749\n },\n {\n \"tag\": "discommend",\n \"popularity\": 113225\n },\n {\n \"tag\": "crude",\n \"popularity\": 112704\n },\n {\n \"tag\": "upflash",\n \"popularity\": 112187\n },\n {\n \"tag\": "saltimbank",\n \"popularity\": 111674\n },\n {\n \"tag\": "posthysterical",\n \"popularity\": 111165\n },\n {\n \"tag\": "trample",\n \"popularity\": 110659\n },\n {\n \"tag\": "ungirthed",\n \"popularity\": 110157\n },\n {\n \"tag\": "unshakable",\n \"popularity\": 109658\n },\n {\n \"tag\": "hepatocystic",\n \"popularity\": 109163\n },\n {\n \"tag\": "psammophyte",\n \"popularity\": 108671\n },\n {\n \"tag\": "millionfold",\n \"popularity\": 108183\n },\n {\n \"tag\": "outtaste",\n \"popularity\": 107698\n },\n {\n \"tag\": "poppycockish",\n \"popularity\": 107217\n },\n {\n \"tag\": "viduine",\n \"popularity\": 106739\n },\n {\n \"tag\": "pleasureman",\n \"popularity\": 106264\n },\n {\n \"tag\": "cholesterolemia",\n \"popularity\": 105792\n },\n {\n \"tag\": "hostlerwife",\n \"popularity\": 105324\n },\n {\n \"tag\": "figure undergrass",\n \"popularity\": 104859\n },\n {\n \"tag\": "bedrape",\n \"popularity\": 104398\n },\n {\n \"tag\": "nuttishness",\n \"popularity\": 103939\n },\n {\n \"tag\": "fow",\n \"popularity\": 103484\n },\n {\n \"tag\": "rachianesthesia",\n \"popularity\": 103031\n },\n {\n \"tag\": "recruitable",\n \"popularity\": 102582\n },\n {\n \"tag\": "semianatomical Oenotheraceae",\n \"popularity\": 102136\n },\n {\n \"tag\": "extracapsular",\n \"popularity\": 101693\n },\n {\n \"tag\": "unsigneted",\n \"popularity\": 101253\n },\n {\n \"tag\": "fissural",\n \"popularity\": 100816\n },\n {\n \"tag\": "ayous",\n \"popularity\": 100381\n },\n {\n \"tag\": "crestfallenness odontograph",\n \"popularity\": 99950\n },\n {\n \"tag\": "monopodium",\n \"popularity\": 99522\n },\n {\n \"tag\": "germfree",\n \"popularity\": 99096\n },\n {\n \"tag\": "dauphin",\n \"popularity\": 98673\n },\n {\n \"tag\": "nonagesimal",\n \"popularity\": 98254\n },\n {\n \"tag\": "waterchat",\n \"popularity\": 97836\n },\n {\n \"tag\": "Entelodon",\n \"popularity\": 97422\n },\n {\n \"tag\": "semischolastic",\n \"popularity\": 97010\n },\n {\n \"tag\": "somata",\n \"popularity\": 96602\n },\n {\n \"tag\": "expositorily",\n \"popularity\": 96195\n },\n {\n \"tag\": "bass",\n \"popularity\": 95792\n },\n {\n \"tag\": "calorimetry",\n \"popularity\": 95391\n },\n {\n \"tag\": "entireness",\n \"popularity\": 94993\n },\n {\n \"tag\": "ratline soppiness",\n \"popularity\": 94597\n },\n {\n \"tag\": "shor",\n \"popularity\": 94204\n },\n {\n \"tag\": "coprecipitation",\n \"popularity\": 93813\n },\n {\n \"tag\": "unblushingly",\n \"popularity\": 93425\n },\n {\n \"tag\": "macarize",\n \"popularity\": 93040\n },\n {\n \"tag\": "scruplesomeness",\n \"popularity\": 92657\n },\n {\n \"tag\": "offsaddle",\n \"popularity\": 92276\n },\n {\n \"tag\": "hypertragical",\n \"popularity\": 91898\n },\n {\n \"tag\": "uncassock loined",\n \"popularity\": 91522\n },\n {\n \"tag\": "interlobate",\n \"popularity\": 91149\n },\n {\n \"tag\": "releasor orrisroot stoloniferously",\n \"popularity\": 90778\n },\n {\n \"tag\": "elementoid",\n \"popularity\": 90410\n },\n {\n \"tag\": "Lentilla",\n \"popularity\": 90043\n },\n {\n \"tag\": "distressing",\n \"popularity\": 89679\n },\n {\n \"tag\": "hydrodrome",\n \"popularity\": 89318\n },\n {\n \"tag\": "Jeannette",\n \"popularity\": 88958\n },\n {\n \"tag\": "Kuli",\n \"popularity\": 88601\n },\n {\n \"tag\": "taxinomist",\n \"popularity\": 88246\n },\n {\n \"tag\": "southwestwardly",\n \"popularity\": 87894\n },\n {\n \"tag\": "polyparia",\n \"popularity\": 87543\n },\n {\n \"tag\": "exmeridian",\n \"popularity\": 87195\n },\n {\n \"tag\": "splenius regimentaled",\n \"popularity\": 86849\n },\n {\n \"tag\": "Sphaeropsidaceae",\n \"popularity\": 86505\n },\n {\n \"tag\": "unbegun",\n \"popularity\": 86163\n },\n {\n \"tag\": "something",\n \"popularity\": 85823\n },\n {\n \"tag\": "contaminable nonexpulsion",\n \"popularity\": 85486\n },\n {\n \"tag\": "douser",\n \"popularity\": 85150\n },\n {\n \"tag\": "prostrike",\n \"popularity\": 84817\n },\n {\n \"tag\": "worky",\n \"popularity\": 84485\n },\n {\n \"tag\": "folliful",\n \"popularity\": 84156\n },\n {\n \"tag\": "prioracy",\n \"popularity\": 83828\n },\n {\n \"tag\": "undermentioned",\n \"popularity\": 83503\n },\n {\n \"tag\": "Judaica",\n \"popularity\": 83179\n },\n {\n \"tag\": "multifarious",\n \"popularity\": 82858\n },\n {\n \"tag\": "poogye",\n \"popularity\": 82538\n },\n {\n \"tag\": "Sparganium",\n \"popularity\": 82221\n },\n {\n \"tag\": "thurrock",\n \"popularity\": 81905\n },\n {\n \"tag\": "outblush",\n \"popularity\": 81591\n },\n {\n \"tag\": "Strophanthus supraordination",\n \"popularity\": 81279\n },\n {\n \"tag\": "gingerroot",\n \"popularity\": 80969\n },\n {\n \"tag\": "unconscient",\n \"popularity\": 80661\n },\n {\n \"tag\": "unconstitutionally",\n \"popularity\": 80354\n },\n {\n \"tag\": "plaguily",\n \"popularity\": 80050\n },\n {\n \"tag\": "waterily equatorwards",\n \"popularity\": 79747\n },\n {\n \"tag\": "nondeposition",\n \"popularity\": 79446\n },\n {\n \"tag\": "dronishly",\n \"popularity\": 79147\n },\n {\n \"tag\": "gateado",\n \"popularity\": 78849\n },\n {\n \"tag\": "dislink",\n \"popularity\": 78553\n },\n {\n \"tag\": "Joceline",\n \"popularity\": 78259\n },\n {\n \"tag\": "amphiboliferous",\n \"popularity\": 77967\n },\n {\n \"tag\": "bushrope",\n \"popularity\": 77676\n },\n {\n \"tag\": "plumicorn sulphosalicylic",\n \"popularity\": 77387\n },\n {\n \"tag\": "nonefficiency",\n \"popularity\": 77100\n },\n {\n \"tag\": "hieroscopy",\n \"popularity\": 76815\n },\n {\n \"tag\": "causativeness",\n \"popularity\": 76531\n },\n {\n \"tag\": "swird paleoeremology",\n \"popularity\": 76249\n },\n {\n \"tag\": "camphoric",\n \"popularity\": 75968\n },\n {\n \"tag\": "retaining",\n \"popularity\": 75689\n },\n {\n \"tag\": "thyreoprotein",\n \"popularity\": 75411\n },\n {\n \"tag\": "carbona",\n \"popularity\": 75136\n },\n {\n \"tag\": "protectively",\n \"popularity\": 74861\n },\n {\n \"tag\": "mosasaur",\n \"popularity\": 74589\n },\n {\n \"tag\": "reciprocator",\n \"popularity\": 74317\n },\n {\n \"tag\": "detentive",\n \"popularity\": 74048\n },\n {\n \"tag\": "supravital",\n \"popularity\": 73780\n },\n {\n \"tag\": "Vespertilionidae",\n \"popularity\": 73513\n },\n {\n \"tag\": "parka",\n \"popularity\": 73248\n },\n {\n \"tag\": "pickaway",\n \"popularity\": 72984\n },\n {\n \"tag\": "oleaceous",\n \"popularity\": 72722\n },\n {\n \"tag\": "anticogitative",\n \"popularity\": 72462\n },\n {\n \"tag\": "woe",\n \"popularity\": 72203\n },\n {\n \"tag\": "skeuomorph",\n \"popularity\": 71945\n },\n {\n \"tag\": "helpmeet",\n \"popularity\": 71689\n },\n {\n \"tag\": "Hexactinellida brickmaking",\n \"popularity\": 71434\n },\n {\n \"tag\": "resink",\n \"popularity\": 71180\n },\n {\n \"tag\": "diluter",\n \"popularity\": 70928\n },\n {\n \"tag\": "micromicron",\n \"popularity\": 70677\n },\n {\n \"tag\": "parentage",\n \"popularity\": 70428\n },\n {\n \"tag\": "galactorrhoea",\n \"popularity\": 70180\n },\n {\n \"tag\": "gey",\n \"popularity\": 69934\n },\n {\n \"tag\": "gesticulatory",\n \"popularity\": 69689\n },\n {\n \"tag\": "wergil",\n \"popularity\": 69445\n },\n {\n \"tag\": "Lecanora",\n \"popularity\": 69202\n },\n {\n \"tag\": "malanders karst",\n \"popularity\": 68961\n },\n {\n \"tag\": "vibetoite",\n \"popularity\": 68721\n },\n {\n \"tag\": "unrequitedness",\n \"popularity\": 68483\n },\n {\n \"tag\": "outwash",\n \"popularity\": 68245\n },\n {\n \"tag\": "unsacred",\n \"popularity\": 68009\n },\n {\n \"tag\": "unabetted dividend",\n \"popularity\": 67775\n },\n {\n \"tag\": "untraveling",\n \"popularity\": 67541\n },\n {\n \"tag\": "thermobattery",\n \"popularity\": 67309\n },\n {\n \"tag\": "polypragmist",\n \"popularity\": 67078\n },\n {\n \"tag\": "irrefutableness",\n \"popularity\": 66848\n },\n {\n \"tag\": "remiges",\n \"popularity\": 66620\n },\n {\n \"tag\": "implode",\n \"popularity\": 66393\n },\n {\n \"tag\": "superfluousness",\n \"popularity\": 66166\n },\n {\n \"tag\": "croakily unalleviated",\n \"popularity\": 65942\n },\n {\n \"tag\": "edicule",\n \"popularity\": 65718\n },\n {\n \"tag\": "entophytous",\n \"popularity\": 65495\n },\n {\n \"tag\": "benefactorship Toryish",\n \"popularity\": 65274\n },\n {\n \"tag\": "pseudoamateurish",\n \"popularity\": 65054\n },\n {\n \"tag\": "flueless Iguanodontoidea snipnose",\n \"popularity\": 64835\n },\n {\n \"tag\": "zealotical Zamicrus interpole",\n \"popularity\": 64617\n },\n {\n \"tag\": "whereabout",\n \"popularity\": 64401\n },\n {\n \"tag\": "benzazide",\n \"popularity\": 64185\n },\n {\n \"tag\": "pokeweed",\n \"popularity\": 63971\n },\n {\n \"tag\": "calamitoid",\n \"popularity\": 63757\n },\n {\n \"tag\": "sporozoal",\n \"popularity\": 63545\n },\n {\n \"tag\": "physcioid Welshwoman",\n \"popularity\": 63334\n },\n {\n \"tag\": "wanting",\n \"popularity\": 63124\n },\n {\n \"tag\": "unencumbering",\n \"popularity\": 62915\n },\n {\n \"tag\": "Tupi",\n \"popularity\": 62707\n },\n {\n \"tag\": "potbank",\n \"popularity\": 62501\n },\n {\n \"tag\": "bulked",\n \"popularity\": 62295\n },\n {\n \"tag\": "uparise",\n \"popularity\": 62090\n },\n {\n \"tag\": "Sudra",\n \"popularity\": 61887\n },\n {\n \"tag\": "hyperscrupulosity",\n \"popularity\": 61684\n },\n {\n \"tag\": "subterraneously unmaid",\n \"popularity\": 61483\n },\n {\n \"tag\": "poisonousness",\n \"popularity\": 61282\n },\n {\n \"tag\": "phare",\n \"popularity\": 61083\n },\n {\n \"tag\": "dicynodont",\n \"popularity\": 60884\n },\n {\n \"tag\": "chewer",\n \"popularity\": 60687\n },\n {\n \"tag\": "uliginous",\n \"popularity\": 60490\n },\n {\n \"tag\": "tinman",\n \"popularity\": 60295\n },\n {\n \"tag\": "coconut",\n \"popularity\": 60100\n },\n {\n \"tag\": "phryganeoid",\n \"popularity\": 59907\n },\n {\n \"tag\": "bismillah",\n \"popularity\": 59714\n },\n {\n \"tag\": "tautomeric",\n \"popularity\": 59523\n },\n {\n \"tag\": "jerquer",\n \"popularity\": 59332\n },\n {\n \"tag\": "Dryopithecinae",\n \"popularity\": 59143\n },\n {\n \"tag\": "ghizite",\n \"popularity\": 58954\n },\n {\n \"tag\": "unliveable",\n \"popularity\": 58766\n },\n {\n \"tag\": "craftsmaster",\n \"popularity\": 58579\n },\n {\n \"tag\": "semiscenic",\n \"popularity\": 58394\n },\n {\n \"tag\": "danaid",\n \"popularity\": 58209\n },\n {\n \"tag\": "flawful",\n \"popularity\": 58025\n },\n {\n \"tag\": "risibleness",\n \"popularity\": 57841\n },\n {\n \"tag\": "Muscovite",\n \"popularity\": 57659\n },\n {\n \"tag\": "snaringly",\n \"popularity\": 57478\n },\n {\n \"tag\": "brilliantwise",\n \"popularity\": 57297\n },\n {\n \"tag\": "plebeity",\n \"popularity\": 57118\n },\n {\n \"tag\": "historicalness",\n \"popularity\": 56939\n },\n {\n \"tag\": "piecemeal",\n \"popularity\": 56761\n },\n {\n \"tag\": "maxillipedary",\n \"popularity\": 56584\n },\n {\n \"tag\": "Hypenantron",\n \"popularity\": 56408\n },\n {\n \"tag\": "quaintness avigate",\n \"popularity\": 56233\n },\n {\n \"tag\": "ave",\n \"popularity\": 56059\n },\n {\n \"tag\": "mediaevally",\n \"popularity\": 55885\n },\n {\n \"tag\": "brucite",\n \"popularity\": 55712\n },\n {\n \"tag\": "Schwendenerian",\n \"popularity\": 55541\n },\n {\n \"tag\": "julole",\n \"popularity\": 55370\n },\n {\n \"tag\": "palaeolith",\n \"popularity\": 55199\n },\n {\n \"tag\": "cotyledonary",\n \"popularity\": 55030\n },\n {\n \"tag\": "rond",\n \"popularity\": 54861\n },\n {\n \"tag\": "boomster tassoo",\n \"popularity\": 54694\n },\n {\n \"tag\": "cattishly",\n \"popularity\": 54527\n },\n {\n \"tag\": "tonguefence",\n \"popularity\": 54360\n },\n {\n \"tag\": "hexastylar triskele",\n \"popularity\": 54195\n },\n {\n \"tag\": "ariot",\n \"popularity\": 54030\n },\n {\n \"tag\": "intarsist",\n \"popularity\": 53867\n },\n {\n \"tag\": "Oscines",\n \"popularity\": 53704\n },\n {\n \"tag\": "Spaniolize",\n \"popularity\": 53541\n },\n {\n \"tag\": "smellfungus",\n \"popularity\": 53380\n },\n {\n \"tag\": "redisplay",\n \"popularity\": 53219\n },\n {\n \"tag\": "phosphene",\n \"popularity\": 53059\n },\n {\n \"tag\": "phycomycete",\n \"popularity\": 52900\n },\n {\n \"tag\": "prophetic",\n \"popularity\": 52741\n },\n {\n \"tag\": "overtrustful",\n \"popularity\": 52584\n },\n {\n \"tag\": "pinitol",\n \"popularity\": 52427\n },\n {\n \"tag\": "asthmatic",\n \"popularity\": 52270\n },\n {\n \"tag\": "convulsive",\n \"popularity\": 52115\n },\n {\n \"tag\": "draughtswoman",\n \"popularity\": 51960\n },\n {\n \"tag\": "unetymologizable",\n \"popularity\": 51806\n },\n {\n \"tag\": "centrarchoid",\n \"popularity\": 51652\n },\n {\n \"tag\": "mesioincisal",\n \"popularity\": 51500\n },\n {\n \"tag\": "transbaikal",\n \"popularity\": 51348\n },\n {\n \"tag\": "silveriness",\n \"popularity\": 51196\n },\n {\n \"tag\": "costotomy",\n \"popularity\": 51046\n },\n {\n \"tag\": "caracore",\n \"popularity\": 50896\n },\n {\n \"tag\": "depotentiation",\n \"popularity\": 50747\n },\n {\n \"tag\": "glossoepiglottidean",\n \"popularity\": 50598\n },\n {\n \"tag\": "upswell",\n \"popularity\": 50450\n },\n {\n \"tag\": "flecnodal",\n \"popularity\": 50303\n },\n {\n \"tag\": "coventrate",\n \"popularity\": 50157\n },\n {\n \"tag\": "duchesse",\n \"popularity\": 50011\n },\n {\n \"tag\": "excisemanship trophied",\n \"popularity\": 49866\n },\n {\n \"tag\": "cytinaceous",\n \"popularity\": 49721\n },\n {\n \"tag\": "assuringly",\n \"popularity\": 49577\n },\n {\n \"tag\": "unconducted upliftitis",\n \"popularity\": 49434\n },\n {\n \"tag\": "rachicentesis",\n \"popularity\": 49292\n },\n {\n \"tag\": "antiangular",\n \"popularity\": 49150\n },\n {\n \"tag\": "advisal",\n \"popularity\": 49008\n },\n {\n \"tag\": "birdcatcher",\n \"popularity\": 48868\n },\n {\n \"tag\": "secularistic",\n \"popularity\": 48728\n },\n {\n \"tag\": "grandeeism superinformal",\n \"popularity\": 48588\n },\n {\n \"tag\": "unapprehension",\n \"popularity\": 48449\n },\n {\n \"tag\": "excipulum",\n \"popularity\": 48311\n },\n {\n \"tag\": "decimole",\n \"popularity\": 48174\n },\n {\n \"tag\": "semidrachm",\n \"popularity\": 48037\n },\n {\n \"tag\": "uvulotome",\n \"popularity\": 47901\n },\n {\n \"tag\": "Lemaneaceae",\n \"popularity\": 47765\n },\n {\n \"tag\": "corrade",\n \"popularity\": 47630\n },\n {\n \"tag\": "Kuroshio",\n \"popularity\": 47495\n },\n {\n \"tag\": "Araliophyllum",\n \"popularity\": 47361\n },\n {\n \"tag\": "victoriousness cardiosphygmograph",\n \"popularity\": 47228\n },\n {\n \"tag\": "reinvent",\n \"popularity\": 47095\n },\n {\n \"tag\": "Macrotolagus",\n \"popularity\": 46963\n },\n {\n \"tag\": "strenuousness",\n \"popularity\": 46831\n },\n {\n \"tag\": "deviability",\n \"popularity\": 46700\n },\n {\n \"tag\": "phyllospondylous",\n \"popularity\": 46570\n },\n {\n \"tag\": "bisect rudderhole",\n \"popularity\": 46440\n },\n {\n \"tag\": "crownwork",\n \"popularity\": 46311\n },\n {\n \"tag\": "Ascalabota",\n \"popularity\": 46182\n },\n {\n \"tag\": "prostatomyomectomy",\n \"popularity\": 46054\n },\n {\n \"tag\": "neurosyphilis",\n \"popularity\": 45926\n },\n {\n \"tag\": "tabloid scraplet",\n \"popularity\": 45799\n },\n {\n \"tag\": "nonmedullated servility",\n \"popularity\": 45673\n },\n {\n \"tag\": "melopoeic practicalization",\n \"popularity\": 45547\n },\n {\n \"tag\": "nonrhythmic",\n \"popularity\": 45421\n },\n {\n \"tag\": "deplorer",\n \"popularity\": 45296\n },\n {\n \"tag\": "Ophion",\n \"popularity\": 45172\n },\n {\n \"tag\": "subprioress",\n \"popularity\": 45048\n },\n {\n \"tag\": "semiregular",\n \"popularity\": 44925\n },\n {\n \"tag\": "praelection",\n \"popularity\": 44802\n },\n {\n \"tag\": "discinct",\n \"popularity\": 44680\n },\n {\n \"tag\": "preplace",\n \"popularity\": 44558\n },\n {\n \"tag\": "paternoster",\n \"popularity\": 44437\n },\n {\n \"tag\": "suboccipital",\n \"popularity\": 44316\n },\n {\n \"tag\": "Teutophil",\n \"popularity\": 44196\n },\n {\n \"tag\": "tracheole",\n \"popularity\": 44076\n },\n {\n \"tag\": "subsmile",\n \"popularity\": 43957\n },\n {\n \"tag\": "nonapostatizing",\n \"popularity\": 43839\n },\n {\n \"tag\": "cleidotomy",\n \"popularity\": 43720\n },\n {\n \"tag\": "hingle",\n \"popularity\": 43603\n },\n {\n \"tag\": "jocoque",\n \"popularity\": 43486\n },\n {\n \"tag\": "trundler notidanian",\n \"popularity\": 43369\n },\n {\n \"tag\": "strangling misdaub",\n \"popularity\": 43253\n },\n {\n \"tag\": "noncancellable",\n \"popularity\": 43137\n },\n {\n \"tag\": "lavabo",\n \"popularity\": 43022\n },\n {\n \"tag\": "lanterloo",\n \"popularity\": 42907\n },\n {\n \"tag\": "uncitizenly",\n \"popularity\": 42793\n },\n {\n \"tag\": "autoturning",\n \"popularity\": 42679\n },\n {\n \"tag\": "Haganah",\n \"popularity\": 42566\n },\n {\n \"tag\": "Glecoma",\n \"popularity\": 42453\n },\n {\n \"tag\": "membered",\n \"popularity\": 42341\n },\n {\n \"tag\": "consuetudinal",\n \"popularity\": 42229\n },\n {\n \"tag\": "gatehouse",\n \"popularity\": 42117\n },\n {\n \"tag\": "tetherball",\n \"popularity\": 42006\n },\n {\n \"tag\": "counterrevolutionist numismatical",\n \"popularity\": 41896\n },\n {\n \"tag\": "pagehood plateiasmus",\n \"popularity\": 41786\n },\n {\n \"tag\": "pelterer",\n \"popularity\": 41676\n },\n {\n \"tag\": "splenemphraxis",\n \"popularity\": 41567\n },\n {\n \"tag\": "Crypturidae",\n \"popularity\": 41458\n },\n {\n \"tag\": "caboodle",\n \"popularity\": 41350\n },\n {\n \"tag\": "Filaria",\n \"popularity\": 41242\n },\n {\n \"tag\": "noninvincibility",\n \"popularity\": 41135\n },\n {\n \"tag\": "preadvertisement",\n \"popularity\": 41028\n },\n {\n \"tag\": "bathrobe",\n \"popularity\": 40921\n },\n {\n \"tag\": "nitrifier",\n \"popularity\": 40815\n },\n {\n \"tag\": "furthermore",\n \"popularity\": 40709\n },\n {\n \"tag\": "recrate",\n \"popularity\": 40604\n },\n {\n \"tag\": "inexist",\n \"popularity\": 40499\n },\n {\n \"tag\": "Mocoan",\n \"popularity\": 40395\n },\n {\n \"tag\": "forint",\n \"popularity\": 40291\n },\n {\n \"tag\": "cardiomyoliposis",\n \"popularity\": 40187\n },\n {\n \"tag\": "channeling",\n \"popularity\": 40084\n },\n {\n \"tag\": "quebrachine",\n \"popularity\": 39981\n },\n {\n \"tag\": "magistery",\n \"popularity\": 39879\n },\n {\n \"tag\": "koko",\n \"popularity\": 39777\n },\n {\n \"tag\": "nobilify",\n \"popularity\": 39676\n },\n {\n \"tag\": "articulate taprooted",\n \"popularity\": 39575\n },\n {\n \"tag\": "cardiotonic Nicaragua",\n \"popularity\": 39474\n },\n {\n \"tag\": "assertiveness",\n \"popularity\": 39374\n },\n {\n \"tag\": "springtail",\n \"popularity\": 39274\n },\n {\n \"tag\": "spontoon",\n \"popularity\": 39174\n },\n {\n \"tag\": "plesiobiosis",\n \"popularity\": 39075\n },\n {\n \"tag\": "rooinek",\n \"popularity\": 38976\n },\n {\n \"tag\": "hairif falsehood",\n \"popularity\": 38878\n },\n {\n \"tag\": "synodally",\n \"popularity\": 38780\n },\n {\n \"tag\": "biodynamics",\n \"popularity\": 38683\n },\n {\n \"tag\": "trickling",\n \"popularity\": 38585\n },\n {\n \"tag\": "oxfly daystar",\n \"popularity\": 38489\n },\n {\n \"tag\": "epicycloidal",\n \"popularity\": 38392\n },\n {\n \"tag\": "shorthand",\n \"popularity\": 38296\n },\n {\n \"tag\": "herpolhode",\n \"popularity\": 38201\n },\n {\n \"tag\": "polysynthesism",\n \"popularity\": 38105\n },\n {\n \"tag\": "cany",\n \"popularity\": 38010\n },\n {\n \"tag\": "sideage",\n \"popularity\": 37916\n },\n {\n \"tag\": "strainableness",\n \"popularity\": 37822\n },\n {\n \"tag\": "superformidable",\n \"popularity\": 37728\n },\n {\n \"tag\": "slendang",\n \"popularity\": 37634\n },\n {\n \"tag\": "impropriation",\n \"popularity\": 37541\n },\n {\n \"tag\": "ficklehearted",\n \"popularity\": 37449\n },\n {\n \"tag\": "wintrify",\n \"popularity\": 37356\n },\n {\n \"tag\": "geomorphogenist",\n \"popularity\": 37264\n },\n {\n \"tag\": "smuggleable",\n \"popularity\": 37173\n },\n {\n \"tag\": "delapsion",\n \"popularity\": 37081\n },\n {\n \"tag\": "projective",\n \"popularity\": 36990\n },\n {\n \"tag\": "unglue exfoliation",\n \"popularity\": 36900\n },\n {\n \"tag\": "Acerae",\n \"popularity\": 36810\n },\n {\n \"tag\": "unstaged",\n \"popularity\": 36720\n },\n {\n \"tag\": "ranal",\n \"popularity\": 36630\n },\n {\n \"tag\": "worrier",\n \"popularity\": 36541\n },\n {\n \"tag\": "unhid",\n \"popularity\": 36452\n },\n {\n \"tag\": "adequation",\n \"popularity\": 36363\n },\n {\n \"tag\": "strongylid Sokotri",\n \"popularity\": 36275\n },\n {\n \"tag\": "fumingly",\n \"popularity\": 36187\n },\n {\n \"tag\": "gynosporangium phaenogenetic",\n \"popularity\": 36100\n },\n {\n \"tag\": "uniunguiculate",\n \"popularity\": 36012\n },\n {\n \"tag\": "prudelike",\n \"popularity\": 35926\n },\n {\n \"tag\": "seminomata",\n \"popularity\": 35839\n },\n {\n \"tag\": "trinklet",\n \"popularity\": 35753\n },\n {\n \"tag\": "risorial",\n \"popularity\": 35667\n },\n {\n \"tag\": "pericardiocentesis",\n \"popularity\": 35581\n },\n {\n \"tag\": "filmist",\n \"popularity\": 35496\n },\n {\n \"tag\": "Nana",\n \"popularity\": 35411\n },\n {\n \"tag\": "cynipoid",\n \"popularity\": 35326\n },\n {\n \"tag\": "cteniform",\n \"popularity\": 35242\n },\n {\n \"tag\": "semiflex",\n \"popularity\": 35158\n },\n {\n \"tag\": "solstitially",\n \"popularity\": 35074\n },\n {\n \"tag\": "Algarsife",\n \"popularity\": 34991\n },\n {\n \"tag\": "noncriminal",\n \"popularity\": 34908\n },\n {\n \"tag\": "compassion",\n \"popularity\": 34825\n },\n {\n \"tag\": "Buddhic",\n \"popularity\": 34743\n },\n {\n \"tag\": "vellicative dactylically hotfoot",\n \"popularity\": 34661\n },\n {\n \"tag\": "chicory",\n \"popularity\": 34579\n },\n {\n \"tag\": "transperitoneally",\n \"popularity\": 34497\n },\n {\n \"tag\": "pennae",\n \"popularity\": 34416\n },\n {\n \"tag\": "Flamandize",\n \"popularity\": 34335\n },\n {\n \"tag\": "underviewer",\n \"popularity\": 34254\n },\n {\n \"tag\": "assoil",\n \"popularity\": 34174\n },\n {\n \"tag\": "saccharobacillus",\n \"popularity\": 34094\n },\n {\n \"tag\": "biacetylene",\n \"popularity\": 34014\n },\n {\n \"tag\": "mouchardism",\n \"popularity\": 33935\n },\n {\n \"tag\": "anisomeric",\n \"popularity\": 33856\n },\n {\n \"tag\": "digestive",\n \"popularity\": 33777\n },\n {\n \"tag\": "darlingly",\n \"popularity\": 33698\n },\n {\n \"tag\": "liman",\n \"popularity\": 33620\n },\n {\n \"tag\": "soldanrie",\n \"popularity\": 33542\n },\n {\n \"tag\": "sully",\n \"popularity\": 33464\n },\n {\n \"tag\": "brightsmith",\n \"popularity\": 33387\n },\n {\n \"tag\": "inwrap antiliturgist ureterocervical",\n \"popularity\": 33309\n },\n {\n \"tag\": "discommodity",\n \"popularity\": 33232\n },\n {\n \"tag\": "typical aggrandizer",\n \"popularity\": 33156\n },\n {\n \"tag\": "xenogeny",\n \"popularity\": 33079\n },\n {\n \"tag\": "uncountrified",\n \"popularity\": 33003\n },\n {\n \"tag\": "Podarge",\n \"popularity\": 32928\n },\n {\n \"tag\": "uninterviewed",\n \"popularity\": 32852\n },\n {\n \"tag\": "underprior",\n \"popularity\": 32777\n },\n {\n \"tag\": "leiomyomatous",\n \"popularity\": 32702\n },\n {\n \"tag\": "postdysenteric",\n \"popularity\": 32627\n },\n {\n \"tag\": "Fusicladium",\n \"popularity\": 32553\n },\n {\n \"tag\": "Dulcinea",\n \"popularity\": 32478\n },\n {\n \"tag\": "interspersion",\n \"popularity\": 32404\n },\n {\n \"tag\": "preobligate",\n \"popularity\": 32331\n },\n {\n \"tag\": "subaggregate",\n \"popularity\": 32257\n },\n {\n \"tag\": "grammarianism",\n \"popularity\": 32184\n },\n {\n \"tag\": "palikar",\n \"popularity\": 32111\n },\n {\n \"tag\": "facileness",\n \"popularity\": 32039\n },\n {\n \"tag\": "deuterofibrinose",\n \"popularity\": 31966\n },\n {\n \"tag\": "pseudesthesia",\n \"popularity\": 31894\n },\n {\n \"tag\": "sedimentary",\n \"popularity\": 31822\n },\n {\n \"tag\": "typewrite",\n \"popularity\": 31751\n },\n {\n \"tag\": "immemorable",\n \"popularity\": 31679\n },\n {\n \"tag\": "Myrtus",\n \"popularity\": 31608\n },\n {\n \"tag\": "hauchecornite",\n \"popularity\": 31537\n },\n {\n \"tag\": "galleylike",\n \"popularity\": 31467\n },\n {\n \"tag\": "thimber",\n \"popularity\": 31396\n },\n {\n \"tag\": "Hegelianism",\n \"popularity\": 31326\n },\n {\n \"tag\": "strig",\n \"popularity\": 31256\n },\n {\n \"tag\": "skyre",\n \"popularity\": 31187\n },\n {\n \"tag\": "eupepticism",\n \"popularity\": 31117\n },\n {\n \"tag\": "eponymism",\n \"popularity\": 31048\n },\n {\n \"tag\": "flunkeyhood",\n \"popularity\": 30979\n },\n {\n \"tag\": "Abama",\n \"popularity\": 30911\n },\n {\n \"tag\": "adiadochokinesis",\n \"popularity\": 30842\n },\n {\n \"tag\": "spendthrifty",\n \"popularity\": 30774\n },\n {\n \"tag\": "chalcedony",\n \"popularity\": 30706\n },\n {\n \"tag\": "authorism",\n \"popularity\": 30638\n },\n {\n \"tag\": "nasturtium",\n \"popularity\": 30571\n },\n {\n \"tag\": "Acanthocereus",\n \"popularity\": 30504\n },\n {\n \"tag\": "uncollapsible",\n \"popularity\": 30437\n },\n {\n \"tag\": "excursionist",\n \"popularity\": 30370\n },\n {\n \"tag\": "fogbow",\n \"popularity\": 30303\n },\n {\n \"tag\": "overlie",\n \"popularity\": 30237\n },\n {\n \"tag\": "velours",\n \"popularity\": 30171\n },\n {\n \"tag\": "zoodendria madrigal stagbush",\n \"popularity\": 30105\n },\n {\n \"tag\": "imi",\n \"popularity\": 30039\n },\n {\n \"tag\": "cojudge",\n \"popularity\": 29974\n },\n {\n \"tag\": "depurate argal",\n \"popularity\": 29909\n },\n {\n \"tag\": "unrecognition",\n \"popularity\": 29844\n },\n {\n \"tag\": "paunchful",\n \"popularity\": 29779\n },\n {\n \"tag\": "invalued",\n \"popularity\": 29714\n },\n {\n \"tag\": "probang",\n \"popularity\": 29650\n },\n {\n \"tag\": "chetvert",\n \"popularity\": 29586\n },\n {\n \"tag\": "enactable",\n \"popularity\": 29522\n },\n {\n \"tag\": "detoxicate adhibit",\n \"popularity\": 29458\n },\n {\n \"tag\": "kullaite",\n \"popularity\": 29395\n },\n {\n \"tag\": "undazzling",\n \"popularity\": 29332\n },\n {\n \"tag\": "excalation",\n \"popularity\": 29269\n },\n {\n \"tag\": "sievings",\n \"popularity\": 29206\n },\n {\n \"tag\": "disenthral",\n \"popularity\": 29143\n },\n {\n \"tag\": "disinterestedly",\n \"popularity\": 29081\n },\n {\n \"tag\": "stanner",\n \"popularity\": 29018\n },\n {\n \"tag\": "recapitulative",\n \"popularity\": 28956\n },\n {\n \"tag\": "objectivist",\n \"popularity\": 28895\n },\n {\n \"tag\": "hypermetropia",\n \"popularity\": 28833\n },\n {\n \"tag\": "incumbency",\n \"popularity\": 28772\n },\n {\n \"tag\": "protegee",\n \"popularity\": 28711\n },\n {\n \"tag\": "zealotic",\n \"popularity\": 28650\n },\n {\n \"tag\": "predebit",\n \"popularity\": 28589\n },\n {\n \"tag\": "cupolar",\n \"popularity\": 28528\n },\n {\n \"tag\": "unattributed",\n \"popularity\": 28468\n },\n {\n \"tag\": "louisine",\n \"popularity\": 28408\n },\n {\n \"tag\": "illustrate",\n \"popularity\": 28348\n },\n {\n \"tag\": "inofficiousness",\n \"popularity\": 28288\n },\n {\n \"tag\": "Americawards",\n \"popularity\": 28228\n },\n {\n \"tag\": "foreflap",\n \"popularity\": 28169\n },\n {\n \"tag\": "eruditeness",\n \"popularity\": 28110\n },\n {\n \"tag\": "copiopsia",\n \"popularity\": 28051\n },\n {\n \"tag\": "sporuliferous",\n \"popularity\": 27992\n },\n {\n \"tag\": "muttering",\n \"popularity\": 27934\n },\n {\n \"tag\": "prepsychology adrip",\n \"popularity\": 27875\n },\n {\n \"tag\": "unfriendly",\n \"popularity\": 27817\n },\n {\n \"tag\": "sulphanilic",\n \"popularity\": 27759\n },\n {\n \"tag\": "Coelococcus",\n \"popularity\": 27701\n },\n {\n \"tag\": "undoubtfulness",\n \"popularity\": 27643\n },\n {\n \"tag\": "flaringly",\n \"popularity\": 27586\n },\n {\n \"tag\": "unordain",\n \"popularity\": 27529\n },\n {\n \"tag\": "fratchety",\n \"popularity\": 27472\n },\n {\n \"tag\": "decadentism dolefully",\n \"popularity\": 27415\n },\n {\n \"tag\": "synthronus",\n \"popularity\": 27358\n },\n {\n \"tag\": "maiid",\n \"popularity\": 27301\n },\n {\n \"tag\": "rhinobyon",\n \"popularity\": 27245\n },\n {\n \"tag\": "Didynamia",\n \"popularity\": 27189\n },\n {\n \"tag\": "millionairedom",\n \"popularity\": 27133\n },\n {\n \"tag\": "mulierine",\n \"popularity\": 27077\n },\n {\n \"tag\": "Mayo",\n \"popularity\": 27021\n },\n {\n \"tag\": "perceivedness",\n \"popularity\": 26966\n },\n {\n \"tag\": "unadoration",\n \"popularity\": 26911\n },\n {\n \"tag\": "regraft",\n \"popularity\": 26856\n },\n {\n \"tag\": "witch",\n \"popularity\": 26801\n },\n {\n \"tag\": "ungrow",\n \"popularity\": 26746\n },\n {\n \"tag\": "glossopharyngeus",\n \"popularity\": 26691\n },\n {\n \"tag\": "unstirrable",\n \"popularity\": 26637\n },\n {\n \"tag\": "synodsman",\n \"popularity\": 26583\n },\n {\n \"tag\": "placentalian",\n \"popularity\": 26529\n },\n {\n \"tag\": "corpulently",\n \"popularity\": 26475\n },\n {\n \"tag\": "photochromoscope",\n \"popularity\": 26421\n },\n {\n \"tag\": "indusiate retinasphaltum chokestrap",\n \"popularity\": 26368\n },\n {\n \"tag\": "murdrum",\n \"popularity\": 26314\n },\n {\n \"tag\": "belatedness",\n \"popularity\": 26261\n },\n {\n \"tag\": "Cochin",\n \"popularity\": 26208\n },\n {\n \"tag\": "Leonist",\n \"popularity\": 26155\n },\n {\n \"tag\": "keeker confined",\n \"popularity\": 26102\n },\n {\n \"tag\": "unintellectual",\n \"popularity\": 26050\n },\n {\n \"tag\": "nymphaline bait",\n \"popularity\": 25997\n },\n {\n \"tag\": "sarcosporidiosis",\n \"popularity\": 25945\n },\n {\n \"tag\": "catawamptiously",\n \"popularity\": 25893\n },\n {\n \"tag\": "outshame",\n \"popularity\": 25841\n },\n {\n \"tag\": "animalism",\n \"popularity\": 25790\n },\n {\n \"tag\": "epithalamial",\n \"popularity\": 25738\n },\n {\n \"tag\": "ganner",\n \"popularity\": 25687\n },\n {\n \"tag\": "desilicify",\n \"popularity\": 25635\n },\n {\n \"tag\": "dandyism",\n \"popularity\": 25584\n },\n {\n \"tag\": "hyleg",\n \"popularity\": 25533\n },\n {\n \"tag\": "photophysical",\n \"popularity\": 25483\n },\n {\n \"tag\": "underload",\n \"popularity\": 25432\n },\n {\n \"tag\": "unintrusive",\n \"popularity\": 25382\n },\n {\n \"tag\": "succinamic",\n \"popularity\": 25331\n },\n {\n \"tag\": "matchy",\n \"popularity\": 25281\n },\n {\n \"tag\": "concordal",\n \"popularity\": 25231\n },\n {\n \"tag\": "exteriority",\n \"popularity\": 25181\n },\n {\n \"tag\": "sterculiad",\n \"popularity\": 25132\n },\n {\n \"tag\": "sulfoxylic",\n \"popularity\": 25082\n },\n {\n \"tag\": "oversubscription",\n \"popularity\": 25033\n },\n {\n \"tag\": "chiasmic",\n \"popularity\": 24984\n },\n {\n \"tag\": "pseudoparthenogenesis",\n \"popularity\": 24935\n },\n {\n \"tag\": "indorse",\n \"popularity\": 24886\n },\n {\n \"tag\": "Krishnaite",\n \"popularity\": 24837\n },\n {\n \"tag\": "calcinize",\n \"popularity\": 24788\n },\n {\n \"tag\": "rhodium",\n \"popularity\": 24740\n },\n {\n \"tag\": "tragopan",\n \"popularity\": 24692\n },\n {\n \"tag\": "overwhelmingly",\n \"popularity\": 24643\n },\n {\n \"tag\": "procidence accorporate",\n \"popularity\": 24595\n },\n {\n \"tag\": "polemize speelless",\n \"popularity\": 24548\n },\n {\n \"tag\": "radiocarpal goran",\n \"popularity\": 24500\n },\n {\n \"tag\": "counteroffer Pelodytes",\n \"popularity\": 24452\n },\n {\n \"tag\": "lionhearted",\n \"popularity\": 24405\n },\n {\n \"tag\": "paramastoid",\n \"popularity\": 24358\n },\n {\n \"tag\": "murine",\n \"popularity\": 24310\n },\n {\n \"tag\": "woodbined",\n \"popularity\": 24263\n },\n {\n \"tag\": "packthread",\n \"popularity\": 24217\n },\n {\n \"tag\": "citreous",\n \"popularity\": 24170\n },\n {\n \"tag\": "unfallaciously",\n \"popularity\": 24123\n },\n {\n \"tag\": "tentwork reincarnadine",\n \"popularity\": 24077\n },\n {\n \"tag\": "verminousness",\n \"popularity\": 24030\n },\n {\n \"tag\": "sillometer",\n \"popularity\": 23984\n },\n {\n \"tag\": "jointy",\n \"popularity\": 23938\n },\n {\n \"tag\": "streptolysin",\n \"popularity\": 23892\n },\n {\n \"tag\": "Florentinism",\n \"popularity\": 23847\n },\n {\n \"tag\": "monosomatous",\n \"popularity\": 23801\n },\n {\n \"tag\": "capsulociliary",\n \"popularity\": 23756\n },\n {\n \"tag\": "organum",\n \"popularity\": 23710\n },\n {\n \"tag\": "overtly",\n \"popularity\": 23665\n },\n {\n \"tag\": "ophthalmoscopical",\n \"popularity\": 23620\n },\n {\n \"tag\": "supposititiously",\n \"popularity\": 23575\n },\n {\n \"tag\": "radiochemistry",\n \"popularity\": 23530\n },\n {\n \"tag\": "flaxtail",\n \"popularity\": 23486\n },\n {\n \"tag\": "pretympanic",\n \"popularity\": 23441\n },\n {\n \"tag\": "auscultation",\n \"popularity\": 23397\n },\n {\n \"tag\": "hairdresser",\n \"popularity\": 23352\n },\n {\n \"tag\": "chaffless",\n \"popularity\": 23308\n },\n {\n \"tag\": "polioencephalitis",\n \"popularity\": 23264\n },\n {\n \"tag\": "axolotl",\n \"popularity\": 23220\n },\n {\n \"tag\": "smous",\n \"popularity\": 23177\n },\n {\n \"tag\": "morgen disenamour toothed",\n \"popularity\": 23133\n },\n {\n \"tag\": "chaiseless",\n \"popularity\": 23089\n },\n {\n \"tag\": "frugally",\n \"popularity\": 23046\n },\n {\n \"tag\": "combustive antievolutionist cinenegative",\n \"popularity\": 23003\n },\n {\n \"tag\": "malacolite",\n \"popularity\": 22960\n },\n {\n \"tag\": "borne",\n \"popularity\": 22917\n },\n {\n \"tag\": "mercaptole",\n \"popularity\": 22874\n },\n {\n \"tag\": "judicatory",\n \"popularity\": 22831\n },\n {\n \"tag\": "noctivagation",\n \"popularity\": 22789\n },\n {\n \"tag\": "synthete",\n \"popularity\": 22746\n },\n {\n \"tag\": "tomboyism",\n \"popularity\": 22704\n },\n {\n \"tag\": "serranoid",\n \"popularity\": 22661\n },\n {\n \"tag\": "impostorism",\n \"popularity\": 22619\n },\n {\n \"tag\": "flagellosis Talitha",\n \"popularity\": 22577\n },\n {\n \"tag\": "pseudoviscous",\n \"popularity\": 22535\n },\n {\n \"tag\": "Galleriidae",\n \"popularity\": 22494\n },\n {\n \"tag\": "undulation didelph Comintern",\n \"popularity\": 22452\n },\n {\n \"tag\": "triangulopyramidal",\n \"popularity\": 22411\n },\n {\n \"tag\": "middlings",\n \"popularity\": 22369\n },\n {\n \"tag\": "piperazin",\n \"popularity\": 22328\n },\n {\n \"tag\": "endostitis",\n \"popularity\": 22287\n },\n {\n \"tag\": "swordlike",\n \"popularity\": 22246\n },\n {\n \"tag\": "forthwith",\n \"popularity\": 22205\n },\n {\n \"tag\": "menaceful",\n \"popularity\": 22164\n },\n {\n \"tag\": "explantation defective",\n \"popularity\": 22123\n },\n {\n \"tag\": "arrear",\n \"popularity\": 22083\n },\n {\n \"tag\": "engraft",\n \"popularity\": 22042\n },\n {\n \"tag\": "revolunteer",\n \"popularity\": 22002\n },\n {\n \"tag\": "foliaceous",\n \"popularity\": 21962\n },\n {\n \"tag\": "pseudograph",\n \"popularity\": 21922\n },\n {\n \"tag\": "maenaite",\n \"popularity\": 21882\n },\n {\n \"tag\": "interfinger",\n \"popularity\": 21842\n },\n {\n \"tag\": "macroscopically",\n \"popularity\": 21802\n },\n {\n \"tag\": "bluewood",\n \"popularity\": 21762\n },\n {\n \"tag\": "chikara",\n \"popularity\": 21723\n },\n {\n \"tag\": "reprehension diazeuxis nickelous",\n \"popularity\": 21683\n },\n {\n \"tag\": "vacuation",\n \"popularity\": 21644\n },\n {\n \"tag\": "Sartish",\n \"popularity\": 21605\n },\n {\n \"tag\": "pseudogyny",\n \"popularity\": 21566\n },\n {\n \"tag\": "friedcake",\n \"popularity\": 21527\n },\n {\n \"tag\": "thraw",\n \"popularity\": 21488\n },\n {\n \"tag\": "bifid",\n \"popularity\": 21449\n },\n {\n \"tag\": "truthlessly",\n \"popularity\": 21411\n },\n {\n \"tag\": "lungy",\n \"popularity\": 21372\n },\n {\n \"tag\": "fluoborite",\n \"popularity\": 21334\n },\n {\n \"tag\": "anthropolithic",\n \"popularity\": 21295\n },\n {\n \"tag\": "coachee straw",\n \"popularity\": 21257\n },\n {\n \"tag\": "dehorner Grecize",\n \"popularity\": 21219\n },\n {\n \"tag\": "spondylopyosis",\n \"popularity\": 21181\n },\n {\n \"tag\": "institutionary",\n \"popularity\": 21143\n },\n {\n \"tag\": "agentry",\n \"popularity\": 21105\n },\n {\n \"tag\": "musing bietle",\n \"popularity\": 21068\n },\n {\n \"tag\": "cormophyte",\n \"popularity\": 21030\n },\n {\n \"tag\": "semielliptic",\n \"popularity\": 20993\n },\n {\n \"tag\": "ependytes",\n \"popularity\": 20955\n },\n {\n \"tag\": "coachmaster",\n \"popularity\": 20918\n },\n {\n \"tag\": "overexuberant",\n \"popularity\": 20881\n },\n {\n \"tag\": "selectable",\n \"popularity\": 20844\n },\n {\n \"tag\": "saclike",\n \"popularity\": 20807\n },\n {\n \"tag\": "mullion",\n \"popularity\": 20770\n },\n {\n \"tag\": "pantheonize prevalency",\n \"popularity\": 20733\n },\n {\n \"tag\": "trophosperm",\n \"popularity\": 20697\n },\n {\n \"tag\": "paraphrasist",\n \"popularity\": 20660\n },\n {\n \"tag\": "undercarry",\n \"popularity\": 20624\n },\n {\n \"tag\": "thallogenic",\n \"popularity\": 20587\n },\n {\n \"tag\": "bulgy forbid",\n \"popularity\": 20551\n },\n {\n \"tag\": "proliquor gratulatory",\n \"popularity\": 20515\n },\n {\n \"tag\": "booker",\n \"popularity\": 20479\n },\n {\n \"tag\": "wizen",\n \"popularity\": 20443\n },\n {\n \"tag\": "synchondrosially",\n \"popularity\": 20407\n },\n {\n \"tag\": "herbless",\n \"popularity\": 20371\n },\n {\n \"tag\": "arfvedsonite",\n \"popularity\": 20336\n },\n {\n \"tag\": "Neuroptera",\n \"popularity\": 20300\n },\n {\n \"tag\": "fingerstone",\n \"popularity\": 20265\n },\n {\n \"tag\": "Odontoglossae",\n \"popularity\": 20229\n },\n {\n \"tag\": "transmigrator",\n \"popularity\": 20194\n },\n {\n \"tag\": "Dehaites",\n \"popularity\": 20159\n },\n {\n \"tag\": "Molinist",\n \"popularity\": 20124\n },\n {\n \"tag\": "novelistic",\n \"popularity\": 20089\n },\n {\n \"tag\": "astelic",\n \"popularity\": 20054\n },\n {\n \"tag\": "pyelometry",\n \"popularity\": 20019\n },\n {\n \"tag\": "pigmentation",\n \"popularity\": 19984\n },\n {\n \"tag\": "epinaos",\n \"popularity\": 19950\n },\n {\n \"tag\": "outdare",\n \"popularity\": 19915\n },\n {\n \"tag\": "Funje philaristocracy",\n \"popularity\": 19881\n },\n {\n \"tag\": "keddah",\n \"popularity\": 19846\n },\n {\n \"tag\": "axoidean",\n \"popularity\": 19812\n },\n {\n \"tag\": "ovule",\n \"popularity\": 19778\n },\n {\n \"tag\": "solidify",\n \"popularity\": 19744\n },\n {\n \"tag\": "noncelestial",\n \"popularity\": 19710\n },\n {\n \"tag\": "overmultiplication",\n \"popularity\": 19676\n },\n {\n \"tag\": "hexatetrahedron",\n \"popularity\": 19642\n },\n {\n \"tag\": "pliciform",\n \"popularity\": 19609\n },\n {\n \"tag\": "zimbalon",\n \"popularity\": 19575\n },\n {\n \"tag\": "annexational",\n \"popularity\": 19542\n },\n {\n \"tag\": "eurhodol",\n \"popularity\": 19508\n },\n {\n \"tag\": "yark",\n \"popularity\": 19475\n },\n {\n \"tag\": "illegality nitroalizarin",\n \"popularity\": 19442\n },\n {\n \"tag\": "quadratum",\n \"popularity\": 19409\n },\n {\n \"tag\": "saccharine",\n \"popularity\": 19376\n },\n {\n \"tag\": "unemploy",\n \"popularity\": 19343\n },\n {\n \"tag\": "uniclinal unipotent",\n \"popularity\": 19310\n },\n {\n \"tag\": "turbo",\n \"popularity\": 19277\n },\n {\n \"tag\": "sybarism",\n \"popularity\": 19244\n },\n {\n \"tag\": "motacilline",\n \"popularity\": 19212\n },\n {\n \"tag\": "weaselly",\n \"popularity\": 19179\n },\n {\n \"tag\": "plastid",\n \"popularity\": 19147\n },\n {\n \"tag\": "wasting",\n \"popularity\": 19114\n },\n {\n \"tag\": "begrime fluting",\n \"popularity\": 19082\n },\n {\n \"tag\": "Nephilinae",\n \"popularity\": 19050\n },\n {\n \"tag\": "disregardance",\n \"popularity\": 19018\n },\n {\n \"tag\": "Shakerlike",\n \"popularity\": 18986\n },\n {\n \"tag\": "uniped",\n \"popularity\": 18954\n },\n {\n \"tag\": "knap",\n \"popularity\": 18922\n },\n {\n \"tag\": "electivism undergardener",\n \"popularity\": 18890\n },\n {\n \"tag\": "hulverheaded",\n \"popularity\": 18858\n },\n {\n \"tag\": "unruptured",\n \"popularity\": 18827\n },\n {\n \"tag\": "solemnize credently",\n \"popularity\": 18795\n },\n {\n \"tag\": "pentastomoid possessingly",\n \"popularity\": 18764\n },\n {\n \"tag\": "octose",\n \"popularity\": 18733\n },\n {\n \"tag\": "psithurism indefensibility",\n \"popularity\": 18701\n },\n {\n \"tag\": "torrentuous cyanometer subcrenate",\n \"popularity\": 18670\n },\n {\n \"tag\": "photoplaywright tapaculo",\n \"popularity\": 18639\n },\n {\n \"tag\": "univalence",\n \"popularity\": 18608\n },\n {\n \"tag\": "Porthetria",\n \"popularity\": 18577\n },\n {\n \"tag\": "funambulo",\n \"popularity\": 18546\n },\n {\n \"tag\": "pedion",\n \"popularity\": 18515\n },\n {\n \"tag\": "horticulturally",\n \"popularity\": 18485\n },\n {\n \"tag\": "marennin",\n \"popularity\": 18454\n },\n {\n \"tag\": "horselaugh",\n \"popularity\": 18423\n },\n {\n \"tag\": "semiexecutive",\n \"popularity\": 18393\n },\n {\n \"tag\": "Monopteridae",\n \"popularity\": 18363\n },\n {\n \"tag\": "commonable",\n \"popularity\": 18332\n },\n {\n \"tag\": "dreariment",\n \"popularity\": 18302\n },\n {\n \"tag\": "disbud",\n \"popularity\": 18272\n },\n {\n \"tag\": "monocled",\n \"popularity\": 18242\n },\n {\n \"tag\": "hurlbarrow",\n \"popularity\": 18212\n },\n {\n \"tag\": "opiateproof",\n \"popularity\": 18182\n },\n {\n \"tag\": "Fahrenheit",\n \"popularity\": 18152\n },\n {\n \"tag\": "writhed",\n \"popularity\": 18122\n },\n {\n \"tag\": "Volstead",\n \"popularity\": 18093\n },\n {\n \"tag\": "yesternight",\n \"popularity\": 18063\n },\n {\n \"tag\": "readmittance",\n \"popularity\": 18033\n },\n {\n \"tag\": "reiterable",\n \"popularity\": 18004\n },\n {\n \"tag\": "triquetral",\n \"popularity\": 17975\n },\n {\n \"tag\": "guillotinement",\n \"popularity\": 17945\n },\n {\n \"tag\": "repermission",\n \"popularity\": 17916\n },\n {\n \"tag\": "assishly",\n \"popularity\": 17887\n },\n {\n \"tag\": "daidle",\n \"popularity\": 17858\n },\n {\n \"tag\": "prismatoid",\n \"popularity\": 17829\n },\n {\n \"tag\": "irreptitious",\n \"popularity\": 17800\n },\n {\n \"tag\": "sourdeline",\n \"popularity\": 17771\n },\n {\n \"tag\": "Austrian",\n \"popularity\": 17742\n },\n {\n \"tag\": "psychorrhagic",\n \"popularity\": 17713\n },\n {\n \"tag\": "Monumbo",\n \"popularity\": 17685\n },\n {\n \"tag\": "cloiochoanitic",\n \"popularity\": 17656\n },\n {\n \"tag\": "hant",\n \"popularity\": 17628\n },\n {\n \"tag\": "roily pulldown",\n \"popularity\": 17599\n },\n {\n \"tag\": "recongratulation",\n \"popularity\": 17571\n },\n {\n \"tag\": "Peking",\n \"popularity\": 17543\n },\n {\n \"tag\": "erdvark",\n \"popularity\": 17514\n },\n {\n \"tag\": "antimnemonic",\n \"popularity\": 17486\n },\n {\n \"tag\": "noncapillarity",\n \"popularity\": 17458\n },\n {\n \"tag\": "irrepressive",\n \"popularity\": 17430\n },\n {\n \"tag\": "Petromyzontes",\n \"popularity\": 17402\n },\n {\n \"tag\": "piscatorially",\n \"popularity\": 17374\n },\n {\n \"tag\": "cholesterosis",\n \"popularity\": 17346\n },\n {\n \"tag\": "denunciate",\n \"popularity\": 17319\n },\n {\n \"tag\": "unmetalled",\n \"popularity\": 17291\n },\n {\n \"tag\": "Tigris enruin",\n \"popularity\": 17263\n },\n {\n \"tag\": "anaspalin",\n \"popularity\": 17236\n },\n {\n \"tag\": "monodromy",\n \"popularity\": 17208\n },\n {\n \"tag\": "Canichanan",\n \"popularity\": 17181\n },\n {\n \"tag\": "mesolabe",\n \"popularity\": 17154\n },\n {\n \"tag\": "trichothallic overcunningness",\n \"popularity\": 17127\n },\n {\n \"tag\": "spinsterishly",\n \"popularity\": 17099\n },\n {\n \"tag\": "sensilla",\n \"popularity\": 17072\n },\n {\n \"tag\": "wifelkin",\n \"popularity\": 17045\n },\n {\n \"tag\": "suppositionless",\n \"popularity\": 17018\n },\n {\n \"tag\": "irksomeness",\n \"popularity\": 16991\n },\n {\n \"tag\": "sanbenito",\n \"popularity\": 16964\n },\n {\n \"tag\": "nonstatement",\n \"popularity\": 16938\n },\n {\n \"tag\": "phenoloid",\n \"popularity\": 16911\n },\n {\n \"tag\": "Steinberger",\n \"popularity\": 16884\n },\n {\n \"tag\": "replicated boom",\n \"popularity\": 16858\n },\n {\n \"tag\": "sciomachiology",\n \"popularity\": 16831\n },\n {\n \"tag\": "starwise",\n \"popularity\": 16805\n },\n {\n \"tag\": "prerich",\n \"popularity\": 16778\n },\n {\n \"tag\": "unspawned",\n \"popularity\": 16752\n },\n {\n \"tag\": "unindentable",\n \"popularity\": 16726\n },\n {\n \"tag\": "stromatic",\n \"popularity\": 16700\n },\n {\n \"tag\": "fetishize",\n \"popularity\": 16673\n },\n {\n \"tag\": "dihydroxy",\n \"popularity\": 16647\n },\n {\n \"tag\": "precaudal",\n \"popularity\": 16621\n },\n {\n \"tag\": "Madagascar",\n \"popularity\": 16595\n },\n {\n \"tag\": "repinement",\n \"popularity\": 16570\n },\n {\n \"tag\": "noncathedral wenzel",\n \"popularity\": 16544\n },\n {\n \"tag\": "corollike",\n \"popularity\": 16518\n },\n {\n \"tag\": "pubes unamortization",\n \"popularity\": 16492\n },\n {\n \"tag\": "brickcroft",\n \"popularity\": 16467\n },\n {\n \"tag\": "intertrabecular",\n \"popularity\": 16441\n },\n {\n \"tag\": "formulaic",\n \"popularity\": 16416\n },\n {\n \"tag\": "arienzo",\n \"popularity\": 16390\n },\n {\n \"tag\": "Mazzinian",\n \"popularity\": 16365\n },\n {\n \"tag\": "wallowishly",\n \"popularity\": 16339\n },\n {\n \"tag\": "sysselman",\n \"popularity\": 16314\n },\n {\n \"tag\": "seligmannite",\n \"popularity\": 16289\n },\n {\n \"tag\": "harlequinery",\n \"popularity\": 16264\n },\n {\n \"tag\": "zucchetto",\n \"popularity\": 16239\n },\n {\n \"tag\": "malonyl",\n \"popularity\": 16214\n },\n {\n \"tag\": "patwari",\n \"popularity\": 16189\n },\n {\n \"tag\": "neoholmia venturesomeness",\n \"popularity\": 16164\n },\n {\n \"tag\": "Dehwar",\n \"popularity\": 16139\n },\n {\n \"tag\": "fetiferous",\n \"popularity\": 16114\n },\n {\n \"tag\": "chromatophore",\n \"popularity\": 16090\n },\n {\n \"tag\": "reregistration",\n \"popularity\": 16065\n },\n {\n \"tag\": "alienor",\n \"popularity\": 16040\n },\n {\n \"tag\": "Hexagynia",\n \"popularity\": 16016\n },\n {\n \"tag\": "cerebrotonia",\n \"popularity\": 15991\n },\n {\n \"tag\": "deedbox",\n \"popularity\": 15967\n },\n {\n \"tag\": "staab",\n \"popularity\": 15943\n },\n {\n \"tag\": "uratemia",\n \"popularity\": 15918\n },\n {\n \"tag\": "flaunt",\n \"popularity\": 15894\n },\n {\n \"tag\": "bogy",\n \"popularity\": 15870\n },\n {\n \"tag\": "subcartilaginous",\n \"popularity\": 15846\n },\n {\n \"tag\": "protonephridial",\n \"popularity\": 15822\n },\n {\n \"tag\": "Boswellia",\n \"popularity\": 15798\n },\n {\n \"tag\": "relaxant untiaraed protoepiphyte",\n \"popularity\": 15774\n },\n {\n \"tag\": "nesslerization",\n \"popularity\": 15750\n },\n {\n \"tag\": "precession",\n \"popularity\": 15726\n },\n {\n \"tag\": "peat",\n \"popularity\": 15702\n },\n {\n \"tag\": "unbit",\n \"popularity\": 15678\n },\n {\n \"tag\": "snailish",\n \"popularity\": 15655\n },\n {\n \"tag\": "porismatical",\n \"popularity\": 15631\n },\n {\n \"tag\": "hooflike",\n \"popularity\": 15608\n },\n {\n \"tag\": "resuppose phene cranic",\n \"popularity\": 15584\n },\n {\n \"tag\": "peptonization kipskin",\n \"popularity\": 15561\n },\n {\n \"tag\": "birdstone",\n \"popularity\": 15537\n },\n {\n \"tag\": "empty inferoanterior",\n \"popularity\": 15514\n },\n {\n \"tag\": "androtauric",\n \"popularity\": 15491\n },\n {\n \"tag\": "triamide",\n \"popularity\": 15467\n },\n {\n \"tag\": "showmanry",\n \"popularity\": 15444\n },\n {\n \"tag\": "doing",\n \"popularity\": 15421\n },\n {\n \"tag\": "bouchaleen",\n \"popularity\": 15398\n },\n {\n \"tag\": "precollude",\n \"popularity\": 15375\n },\n {\n \"tag\": "finger",\n \"popularity\": 15352\n },\n {\n \"tag\": "limnetic intermessenger",\n \"popularity\": 15329\n },\n {\n \"tag\": "uncharitable picrotoxic",\n \"popularity\": 15306\n },\n {\n \"tag\": "nationalizer Phasmidae",\n \"popularity\": 15283\n },\n {\n \"tag\": "laughingstock",\n \"popularity\": 15261\n },\n {\n \"tag\": "nondeferential",\n \"popularity\": 15238\n },\n {\n \"tag\": "uproariously",\n \"popularity\": 15215\n },\n {\n \"tag\": "manzanilla",\n \"popularity\": 15193\n },\n {\n \"tag\": "khahoon",\n \"popularity\": 15170\n },\n {\n \"tag\": "olericulturally longshanks",\n \"popularity\": 15148\n },\n {\n \"tag\": "enthusiastically methionic",\n \"popularity\": 15125\n },\n {\n \"tag\": "pobs",\n \"popularity\": 15103\n },\n {\n \"tag\": "tricarpellate",\n \"popularity\": 15081\n },\n {\n \"tag\": "souterrain",\n \"popularity\": 15058\n },\n {\n \"tag\": "tethelin",\n \"popularity\": 15036\n },\n {\n \"tag\": "tartle",\n \"popularity\": 15014\n },\n {\n \"tag\": "tidelike",\n \"popularity\": 14992\n },\n {\n \"tag\": "cosmoramic",\n \"popularity\": 14970\n },\n {\n \"tag\": "pretardiness",\n \"popularity\": 14948\n },\n {\n \"tag\": "insoul",\n \"popularity\": 14926\n },\n {\n \"tag\": "anthroxan",\n \"popularity\": 14904\n },\n {\n \"tag\": "jilter",\n \"popularity\": 14882\n },\n {\n \"tag\": "pectinibranchian trematode",\n \"popularity\": 14860\n },\n {\n \"tag\": "Renaissancist",\n \"popularity\": 14838\n },\n {\n \"tag\": "imaginant",\n \"popularity\": 14817\n },\n {\n \"tag\": "supercensure",\n \"popularity\": 14795\n },\n {\n \"tag\": "festilogy",\n \"popularity\": 14773\n },\n {\n \"tag\": "regression",\n \"popularity\": 14752\n },\n {\n \"tag\": "mesobregmate languorously",\n \"popularity\": 14730\n },\n {\n \"tag\": "unsupernaturalized",\n \"popularity\": 14709\n },\n {\n \"tag\": "boobyish",\n \"popularity\": 14687\n },\n {\n \"tag\": "scopolamine",\n \"popularity\": 14666\n },\n {\n \"tag\": "reamputation unchristianly",\n \"popularity\": 14645\n },\n {\n \"tag\": "cuneatic",\n \"popularity\": 14623\n },\n {\n \"tag\": "heathberry",\n \"popularity\": 14602\n },\n {\n \"tag\": "hate",\n \"popularity\": 14581\n },\n {\n \"tag\": "redeemableness",\n \"popularity\": 14560\n },\n {\n \"tag\": "damasse",\n \"popularity\": 14539\n },\n {\n \"tag\": "thrillsome",\n \"popularity\": 14518\n },\n {\n \"tag\": "disseverment",\n \"popularity\": 14497\n },\n {\n \"tag\": "underbishopric Ostyak",\n \"popularity\": 14476\n },\n {\n \"tag\": "Exoascales",\n \"popularity\": 14455\n },\n {\n \"tag\": "soiled",\n \"popularity\": 14434\n },\n {\n \"tag\": "Cain",\n \"popularity\": 14413\n },\n {\n \"tag\": "mismanageable arenae",\n \"popularity\": 14392\n },\n {\n \"tag\": "manducate unhinderably",\n \"popularity\": 14372\n },\n {\n \"tag\": "peregrin",\n \"popularity\": 14351\n },\n {\n \"tag\": "musicianly",\n \"popularity\": 14330\n },\n {\n \"tag\": "aln",\n \"popularity\": 14310\n },\n {\n \"tag\": "intercentrum",\n \"popularity\": 14289\n },\n {\n \"tag\": "roothold",\n \"popularity\": 14269\n },\n {\n \"tag\": "jane aneurism",\n \"popularity\": 14248\n },\n {\n \"tag\": "insinuatively forefeel phytolatrous",\n \"popularity\": 14228\n },\n {\n \"tag\": "kanchil",\n \"popularity\": 14208\n },\n {\n \"tag\": "Austrophile",\n \"popularity\": 14187\n },\n {\n \"tag\": "unterrorized",\n \"popularity\": 14167\n },\n {\n \"tag\": "admeasure",\n \"popularity\": 14147\n },\n {\n \"tag\": "electrodissolution",\n \"popularity\": 14127\n },\n {\n \"tag\": "unweddedly",\n \"popularity\": 14107\n },\n {\n \"tag\": "unannoying",\n \"popularity\": 14087\n },\n {\n \"tag\": "uningenuous",\n \"popularity\": 14067\n },\n {\n \"tag\": "omnibenevolent",\n \"popularity\": 14047\n },\n {\n \"tag\": "commissure",\n \"popularity\": 14027\n },\n {\n \"tag\": "tellureted",\n \"popularity\": 14007\n },\n {\n \"tag\": "suffragan",\n \"popularity\": 13987\n },\n {\n \"tag\": "sphaeriaceous",\n \"popularity\": 13967\n },\n {\n \"tag\": "unfearing",\n \"popularity\": 13947\n },\n {\n \"tag\": "stentoriousness precounsellor",\n \"popularity\": 13928\n },\n {\n \"tag\": "haemaspectroscope",\n \"popularity\": 13908\n },\n {\n \"tag\": "teras",\n \"popularity\": 13888\n },\n {\n \"tag\": "pulicine",\n \"popularity\": 13869\n },\n {\n \"tag\": "colicystopyelitis",\n \"popularity\": 13849\n },\n {\n \"tag\": "Physalia",\n \"popularity\": 13830\n },\n {\n \"tag\": "Saxicolidae",\n \"popularity\": 13810\n },\n {\n \"tag\": "peritonital",\n \"popularity\": 13791\n },\n {\n \"tag\": "dysphotic",\n \"popularity\": 13771\n },\n {\n \"tag\": "unabandoned",\n \"popularity\": 13752\n },\n {\n \"tag\": "rashful",\n \"popularity\": 13733\n },\n {\n \"tag\": "goodyness Manobo",\n \"popularity\": 13714\n },\n {\n \"tag\": "glaring",\n \"popularity\": 13694\n },\n {\n \"tag\": "horrorful",\n \"popularity\": 13675\n },\n {\n \"tag\": "intercepting",\n \"popularity\": 13656\n },\n {\n \"tag\": "semifine",\n \"popularity\": 13637\n },\n {\n \"tag\": "Gaypoo",\n \"popularity\": 13618\n },\n {\n \"tag\": "Metrosideros",\n \"popularity\": 13599\n },\n {\n \"tag\": "thoracicolumbar",\n \"popularity\": 13580\n },\n {\n \"tag\": "unserried",\n \"popularity\": 13561\n },\n {\n \"tag\": "keeperess cauterization",\n \"popularity\": 13542\n },\n {\n \"tag\": "administrant",\n \"popularity\": 13523\n },\n {\n \"tag\": "unpropitiatedness",\n \"popularity\": 13505\n },\n {\n \"tag\": "pensileness",\n \"popularity\": 13486\n },\n {\n \"tag\": "quinaldic unreceivable",\n \"popularity\": 13467\n },\n {\n \"tag\": "Carnaria",\n \"popularity\": 13448\n },\n {\n \"tag\": "azothionium wurrus",\n \"popularity\": 13430\n },\n {\n \"tag\": "mistresshood",\n \"popularity\": 13411\n },\n {\n \"tag\": "Savara",\n \"popularity\": 13393\n },\n {\n \"tag\": "dasyurine",\n \"popularity\": 13374\n },\n {\n \"tag\": "superideal",\n \"popularity\": 13356\n },\n {\n \"tag\": "Parisianize",\n \"popularity\": 13337\n },\n {\n \"tag\": "underearth",\n \"popularity\": 13319\n },\n {\n \"tag\": "athrogenic",\n \"popularity\": 13301\n },\n {\n \"tag\": "communicate",\n \"popularity\": 13282\n },\n {\n \"tag\": "denervation enworthed",\n \"popularity\": 13264\n },\n {\n \"tag\": "subbromide",\n \"popularity\": 13246\n },\n {\n \"tag\": "stenocoriasis",\n \"popularity\": 13228\n },\n {\n \"tag\": "facetiousness",\n \"popularity\": 13209\n },\n {\n \"tag\": "twaddling",\n \"popularity\": 13191\n },\n {\n \"tag\": "tetartoconid",\n \"popularity\": 13173\n },\n {\n \"tag\": "audiophile",\n \"popularity\": 13155\n },\n {\n \"tag\": "fustigate",\n \"popularity\": 13137\n },\n {\n \"tag\": "Sorbian cacophonia",\n \"popularity\": 13119\n },\n {\n \"tag\": "fondish",\n \"popularity\": 13101\n },\n {\n \"tag\": "endomastoiditis",\n \"popularity\": 13084\n },\n {\n \"tag\": "sniptious",\n \"popularity\": 13066\n },\n {\n \"tag\": "glochidiate",\n \"popularity\": 13048\n },\n {\n \"tag\": "polycarboxylic",\n \"popularity\": 13030\n },\n {\n \"tag\": "stamp",\n \"popularity\": 13012\n },\n {\n \"tag\": "tritonymph endotoxoid",\n \"popularity\": 12995\n },\n {\n \"tag\": "wolfskin",\n \"popularity\": 12977\n },\n {\n \"tag\": "oncosimeter",\n \"popularity\": 12959\n },\n {\n \"tag\": "outward",\n \"popularity\": 12942\n },\n {\n \"tag\": "circumscribed",\n \"popularity\": 12924\n },\n {\n \"tag\": "autohemolytic",\n \"popularity\": 12907\n },\n {\n \"tag\": "isorhamnose",\n \"popularity\": 12889\n },\n {\n \"tag\": "monarchomachic",\n \"popularity\": 12872\n },\n {\n \"tag\": "phaenomenon",\n \"popularity\": 12855\n },\n {\n \"tag\": "angiopressure",\n \"popularity\": 12837\n },\n {\n \"tag\": "similarize",\n \"popularity\": 12820\n },\n {\n \"tag\": "unseeable",\n \"popularity\": 12803\n },\n {\n \"tag\": "Toryize",\n \"popularity\": 12785\n },\n {\n \"tag\": "fruitling",\n \"popularity\": 12768\n },\n {\n \"tag\": "axle",\n \"popularity\": 12751\n },\n {\n \"tag\": "priestal cocked",\n \"popularity\": 12734\n },\n {\n \"tag\": "serotoxin",\n \"popularity\": 12717\n },\n {\n \"tag\": "unmovably",\n \"popularity\": 12700\n },\n {\n \"tag\": "darbha",\n \"popularity\": 12683\n },\n {\n \"tag\": "Mongolize",\n \"popularity\": 12666\n },\n {\n \"tag\": "clusteringly",\n \"popularity\": 12649\n },\n {\n \"tag\": "tendence",\n \"popularity\": 12632\n },\n {\n \"tag\": "foziness",\n \"popularity\": 12615\n },\n {\n \"tag\": "brickkiln lithify",\n \"popularity\": 12598\n },\n {\n \"tag\": "unpriest",\n \"popularity\": 12581\n },\n {\n \"tag\": "convincer",\n \"popularity\": 12564\n },\n {\n \"tag\": "mornlike",\n \"popularity\": 12548\n },\n {\n \"tag\": "overaddiction ostentatiousness",\n \"popularity\": 12531\n },\n {\n \"tag\": "diffusively moccasin pendom",\n \"popularity\": 12514\n },\n {\n \"tag\": "boose",\n \"popularity\": 12498\n },\n {\n \"tag\": "myonosus",\n \"popularity\": 12481\n },\n {\n \"tag\": "handsome",\n \"popularity\": 12464\n },\n {\n \"tag\": "paroxysmic",\n \"popularity\": 12448\n },\n {\n \"tag\": "Ulidian",\n \"popularity\": 12431\n },\n {\n \"tag\": "heartache",\n \"popularity\": 12415\n },\n {\n \"tag\": "torporize",\n \"popularity\": 12398\n },\n {\n \"tag\": "hippish",\n \"popularity\": 12382\n },\n {\n \"tag\": "stigmal militation",\n \"popularity\": 12366\n },\n {\n \"tag\": "matmaker",\n \"popularity\": 12349\n },\n {\n \"tag\": "marantaceous bivoluminous",\n \"popularity\": 12333\n },\n {\n \"tag\": "Uraniidae",\n \"popularity\": 12317\n },\n {\n \"tag\": "risper",\n \"popularity\": 12301\n },\n {\n \"tag\": "tintinnabulation",\n \"popularity\": 12284\n },\n {\n \"tag\": "tributorian",\n \"popularity\": 12268\n },\n {\n \"tag\": "ashamedly",\n \"popularity\": 12252\n },\n {\n \"tag\": "Macrourus",\n \"popularity\": 12236\n },\n {\n \"tag\": "Chora",\n \"popularity\": 12220\n },\n {\n \"tag\": "caul",\n \"popularity\": 12204\n },\n {\n \"tag\": "exsector",\n \"popularity\": 12188\n },\n {\n \"tag\": "acutish",\n \"popularity\": 12172\n },\n {\n \"tag\": "amphichrome",\n \"popularity\": 12156\n },\n {\n \"tag\": "guarder",\n \"popularity\": 12140\n },\n {\n \"tag\": "sculpturally",\n \"popularity\": 12124\n },\n {\n \"tag\": "benightmare",\n \"popularity\": 12108\n },\n {\n \"tag\": "chucky",\n \"popularity\": 12093\n },\n {\n \"tag\": "Venetian",\n \"popularity\": 12077\n },\n {\n \"tag\": "autotheater",\n \"popularity\": 12061\n },\n {\n \"tag\": "planarioid",\n \"popularity\": 12045\n },\n {\n \"tag\": "handkerchiefful",\n \"popularity\": 12030\n },\n {\n \"tag\": "fuliginousness potentize",\n \"popularity\": 12014\n },\n {\n \"tag\": "pantheum",\n \"popularity\": 11998\n },\n {\n \"tag\": "heavyweight",\n \"popularity\": 11983\n },\n {\n \"tag\": "unbrick",\n \"popularity\": 11967\n },\n {\n \"tag\": "duomachy",\n \"popularity\": 11952\n },\n {\n \"tag\": "polyphyodont",\n \"popularity\": 11936\n },\n {\n \"tag\": "hibernacle",\n \"popularity\": 11921\n },\n {\n \"tag\": "undistend",\n \"popularity\": 11905\n },\n {\n \"tag\": "hystericky",\n \"popularity\": 11890\n },\n {\n \"tag\": "paleolimnology",\n \"popularity\": 11875\n },\n {\n \"tag\": "cedarware",\n \"popularity\": 11859\n },\n {\n \"tag\": "overwrested",\n \"popularity\": 11844\n },\n {\n \"tag\": "Syriacism",\n \"popularity\": 11829\n },\n {\n \"tag\": "pretan",\n \"popularity\": 11813\n },\n {\n \"tag\": "formant",\n \"popularity\": 11798\n },\n {\n \"tag\": "pharmacopoeist Fedia",\n \"popularity\": 11783\n },\n {\n \"tag\": "exorcist eerisome",\n \"popularity\": 11768\n },\n {\n \"tag\": "separation",\n \"popularity\": 11753\n },\n {\n \"tag\": "infancy",\n \"popularity\": 11738\n },\n {\n \"tag\": "ecrasite",\n \"popularity\": 11723\n },\n {\n \"tag\": "propolize",\n \"popularity\": 11708\n },\n {\n \"tag\": "uncram phyllin",\n \"popularity\": 11693\n },\n {\n \"tag\": "thymopathy",\n \"popularity\": 11678\n },\n {\n \"tag\": "omniscient",\n \"popularity\": 11663\n },\n {\n \"tag\": "coussinet hazer",\n \"popularity\": 11648\n },\n {\n \"tag\": "contributiveness",\n \"popularity\": 11633\n },\n {\n \"tag\": "septifluous",\n \"popularity\": 11618\n },\n {\n \"tag\": "halfness",\n \"popularity\": 11603\n },\n {\n \"tag\": "tocher",\n \"popularity\": 11589\n },\n {\n \"tag\": "monotonist",\n \"popularity\": 11574\n },\n {\n \"tag\": "headchair",\n \"popularity\": 11559\n },\n {\n \"tag\": "everywhence",\n \"popularity\": 11544\n },\n {\n \"tag\": "gerate",\n \"popularity\": 11530\n },\n {\n \"tag\": "unrepellent",\n \"popularity\": 11515\n },\n {\n \"tag\": "inidoneous",\n \"popularity\": 11500\n },\n {\n \"tag\": "Rifi",\n \"popularity\": 11486\n },\n {\n \"tag\": "unstop",\n \"popularity\": 11471\n },\n {\n \"tag\": "conformer",\n \"popularity\": 11457\n },\n {\n \"tag\": "vivisectionally",\n \"popularity\": 11442\n },\n {\n \"tag\": "nonfinishing",\n \"popularity\": 11428\n },\n {\n \"tag\": "tyranness",\n \"popularity\": 11413\n },\n {\n \"tag\": "shepherdage havoc",\n \"popularity\": 11399\n },\n {\n \"tag\": "coronale",\n \"popularity\": 11385\n },\n {\n \"tag\": "airmarker",\n \"popularity\": 11370\n },\n {\n \"tag\": "subpanel",\n \"popularity\": 11356\n },\n {\n \"tag\": "conciliation",\n \"popularity\": 11342\n },\n {\n \"tag\": "supergun",\n \"popularity\": 11327\n },\n {\n \"tag\": "photoheliography",\n \"popularity\": 11313\n },\n {\n \"tag\": "cacosmia",\n \"popularity\": 11299\n },\n {\n \"tag\": "caressant",\n \"popularity\": 11285\n },\n {\n \"tag\": "swivet",\n \"popularity\": 11270\n },\n {\n \"tag\": "coddler",\n \"popularity\": 11256\n },\n {\n \"tag\": "rakehellish",\n \"popularity\": 11242\n },\n {\n \"tag\": "recohabitation",\n \"popularity\": 11228\n },\n {\n \"tag\": "postillator",\n \"popularity\": 11214\n },\n {\n \"tag\": "receipt",\n \"popularity\": 11200\n },\n {\n \"tag\": "nonconformistical",\n \"popularity\": 11186\n },\n {\n \"tag\": "unglorified",\n \"popularity\": 11172\n },\n {\n \"tag\": "unordinariness",\n \"popularity\": 11158\n },\n {\n \"tag\": "tetrahydroxy",\n \"popularity\": 11144\n },\n {\n \"tag\": "haploperistomic corporeity",\n \"popularity\": 11130\n },\n {\n \"tag\": "varical",\n \"popularity\": 11117\n },\n {\n \"tag\": "pilferment",\n \"popularity\": 11103\n },\n {\n \"tag\": "reverentially playcraft",\n \"popularity\": 11089\n },\n {\n \"tag\": "unretentive",\n \"popularity\": 11075\n },\n {\n \"tag\": "readiness",\n \"popularity\": 11061\n },\n {\n \"tag\": "thermomagnetism",\n \"popularity\": 11048\n },\n {\n \"tag\": "spotless",\n \"popularity\": 11034\n },\n {\n \"tag\": "semishrubby",\n \"popularity\": 11020\n },\n {\n \"tag\": "metrotomy",\n \"popularity\": 11007\n },\n {\n \"tag\": "hocker",\n \"popularity\": 10993\n },\n {\n \"tag\": "anecdotal",\n \"popularity\": 10979\n },\n {\n \"tag\": "tetrabelodont",\n \"popularity\": 10966\n },\n {\n \"tag\": "Ramillied",\n \"popularity\": 10952\n },\n {\n \"tag\": "sympatheticism",\n \"popularity\": 10939\n },\n {\n \"tag\": "kiskatom",\n \"popularity\": 10925\n },\n {\n \"tag\": "concyclically",\n \"popularity\": 10912\n },\n {\n \"tag\": "tunicless",\n \"popularity\": 10899\n },\n {\n \"tag\": "formalistic",\n \"popularity\": 10885\n },\n {\n \"tag\": "thermacogenesis",\n \"popularity\": 10872\n },\n {\n \"tag\": "multimotored",\n \"popularity\": 10858\n },\n {\n \"tag\": "inversive",\n \"popularity\": 10845\n },\n {\n \"tag\": "Jatki",\n \"popularity\": 10832\n },\n {\n \"tag\": "highest",\n \"popularity\": 10818\n },\n {\n \"tag\": "rubidic",\n \"popularity\": 10805\n },\n {\n \"tag\": "acranial",\n \"popularity\": 10792\n },\n {\n \"tag\": "pulvinulus",\n \"popularity\": 10779\n },\n {\n \"tag\": "nattiness",\n \"popularity\": 10766\n },\n {\n \"tag\": "antisimoniacal",\n \"popularity\": 10752\n },\n {\n \"tag\": "tetanize",\n \"popularity\": 10739\n },\n {\n \"tag\": "spectrophobia",\n \"popularity\": 10726\n },\n {\n \"tag\": "monopolitical",\n \"popularity\": 10713\n },\n {\n \"tag\": "teallite",\n \"popularity\": 10700\n },\n {\n \"tag\": "alicyclic interpellator",\n \"popularity\": 10687\n },\n {\n \"tag\": "nonsynthesized",\n \"popularity\": 10674\n },\n {\n \"tag\": "wheelwrighting",\n \"popularity\": 10661\n },\n {\n \"tag\": "pelliculate",\n \"popularity\": 10648\n },\n {\n \"tag\": "Euphyllopoda",\n \"popularity\": 10635\n },\n {\n \"tag\": "graver",\n \"popularity\": 10622\n },\n {\n \"tag\": "automorph",\n \"popularity\": 10609\n },\n {\n \"tag\": "underhanded",\n \"popularity\": 10597\n },\n {\n \"tag\": "causal",\n \"popularity\": 10584\n },\n {\n \"tag\": "odoom",\n \"popularity\": 10571\n },\n {\n \"tag\": "apodictical",\n \"popularity\": 10558\n },\n {\n \"tag\": "foundery",\n \"popularity\": 10545\n },\n {\n \"tag\": "unneighbored",\n \"popularity\": 10533\n },\n {\n \"tag\": "woolshearing",\n \"popularity\": 10520\n },\n {\n \"tag\": "boschveld",\n \"popularity\": 10507\n },\n {\n \"tag\": "unhardened lipopod",\n \"popularity\": 10495\n },\n {\n \"tag\": "unenriching",\n \"popularity\": 10482\n },\n {\n \"tag\": "spak",\n \"popularity\": 10469\n },\n {\n \"tag\": "yogasana",\n \"popularity\": 10457\n },\n {\n \"tag\": "depoetize",\n \"popularity\": 10444\n },\n {\n \"tag\": "parousiamania",\n \"popularity\": 10432\n },\n {\n \"tag\": "longlegs",\n \"popularity\": 10419\n },\n {\n \"tag\": "gelatinizability",\n \"popularity\": 10407\n },\n {\n \"tag\": "edeology",\n \"popularity\": 10394\n },\n {\n \"tag\": "sodwork",\n \"popularity\": 10382\n },\n {\n \"tag\": "somnambule",\n \"popularity\": 10369\n },\n {\n \"tag\": "antiquing",\n \"popularity\": 10357\n },\n {\n \"tag\": "intaker",\n \"popularity\": 10344\n },\n {\n \"tag\": "Gerberia",\n \"popularity\": 10332\n },\n {\n \"tag\": "preadmit",\n \"popularity\": 10320\n },\n {\n \"tag\": "bullhorn",\n \"popularity\": 10307\n },\n {\n \"tag\": "sororal",\n \"popularity\": 10295\n },\n {\n \"tag\": "phaeophyceous",\n \"popularity\": 10283\n },\n {\n \"tag\": "omphalopsychite",\n \"popularity\": 10271\n },\n {\n \"tag\": "substantious",\n \"popularity\": 10258\n },\n {\n \"tag\": "undemonstratively",\n \"popularity\": 10246\n },\n {\n \"tag\": "corallike blackit",\n \"popularity\": 10234\n },\n {\n \"tag\": "amoebous",\n \"popularity\": 10222\n },\n {\n \"tag\": "Polypodium",\n \"popularity\": 10210\n },\n {\n \"tag\": "blodite",\n \"popularity\": 10198\n },\n {\n \"tag\": "hordarian",\n \"popularity\": 10186\n },\n {\n \"tag\": "nonmoral",\n \"popularity\": 10174\n },\n {\n \"tag\": "dredgeful",\n \"popularity\": 10162\n },\n {\n \"tag\": "nourishingly",\n \"popularity\": 10150\n },\n {\n \"tag\": "seamy",\n \"popularity\": 10138\n },\n {\n \"tag\": "vara",\n \"popularity\": 10126\n },\n {\n \"tag\": "incorruptibleness",\n \"popularity\": 10114\n },\n {\n \"tag\": "manipulator",\n \"popularity\": 10102\n },\n {\n \"tag\": "chromodiascope uncountably",\n \"popularity\": 10090\n },\n {\n \"tag\": "typhemia",\n \"popularity\": 10078\n },\n {\n \"tag\": "Smalcaldic",\n \"popularity\": 10066\n },\n {\n \"tag\": "precontrive",\n \"popularity\": 10054\n },\n {\n \"tag\": "sowarry",\n \"popularity\": 10042\n },\n {\n \"tag\": "monopodic",\n \"popularity\": 10031\n },\n {\n \"tag\": "recodify",\n \"popularity\": 10019\n },\n {\n \"tag\": "phosphowolframic rimple",\n \"popularity\": 10007\n },\n {\n \"tag\": "triconch",\n \"popularity\": 9995\n },\n {\n \"tag\": "pycnodontoid",\n \"popularity\": 9984\n },\n {\n \"tag\": "bradyspermatism",\n \"popularity\": 9972\n },\n {\n \"tag\": "extensionist",\n \"popularity\": 9960\n },\n {\n \"tag\": "characterize",\n \"popularity\": 9949\n },\n {\n \"tag\": "anatreptic proteolytic",\n \"popularity\": 9937\n },\n {\n \"tag\": "waterboard",\n \"popularity\": 9925\n },\n {\n \"tag\": "allopathically",\n \"popularity\": 9914\n },\n {\n \"tag\": "arithmetician",\n \"popularity\": 9902\n },\n {\n \"tag\": "subsist",\n \"popularity\": 9891\n },\n {\n \"tag\": "Islamitish",\n \"popularity\": 9879\n },\n {\n \"tag\": "biddy",\n \"popularity\": 9868\n },\n {\n \"tag\": "reverberation",\n \"popularity\": 9856\n },\n {\n \"tag\": "Zaporogue",\n \"popularity\": 9845\n },\n {\n \"tag\": "soapberry",\n \"popularity\": 9833\n },\n {\n \"tag\": "physiognomics",\n \"popularity\": 9822\n },\n {\n \"tag\": "hospitalization",\n \"popularity\": 9810\n },\n {\n \"tag\": "dissembler",\n \"popularity\": 9799\n },\n {\n \"tag\": "festinate",\n \"popularity\": 9788\n },\n {\n \"tag\": "angiectopia",\n \"popularity\": 9776\n },\n {\n \"tag\": "Pulicidae",\n \"popularity\": 9765\n },\n {\n \"tag\": "beslimer",\n \"popularity\": 9754\n },\n {\n \"tag\": "nontreaty",\n \"popularity\": 9743\n },\n {\n \"tag\": "unhaggled",\n \"popularity\": 9731\n },\n {\n \"tag\": "catfall",\n \"popularity\": 9720\n },\n {\n \"tag\": "stola",\n \"popularity\": 9709\n },\n {\n \"tag\": "pataco",\n \"popularity\": 9698\n },\n {\n \"tag\": "ontologistic",\n \"popularity\": 9686\n },\n {\n \"tag\": "aerosphere",\n \"popularity\": 9675\n },\n {\n \"tag\": "deobstruent",\n \"popularity\": 9664\n },\n {\n \"tag\": "threepence",\n \"popularity\": 9653\n },\n {\n \"tag\": "cyprinoid",\n \"popularity\": 9642\n },\n {\n \"tag\": "overbank",\n \"popularity\": 9631\n },\n {\n \"tag\": "prostyle",\n \"popularity\": 9620\n },\n {\n \"tag\": "photoactivation",\n \"popularity\": 9609\n },\n {\n \"tag\": "homothetic",\n \"popularity\": 9598\n },\n {\n \"tag\": "roguedom",\n \"popularity\": 9587\n },\n {\n \"tag\": "underschool",\n \"popularity\": 9576\n },\n {\n \"tag\": "tractility",\n \"popularity\": 9565\n },\n {\n \"tag\": "gardenin",\n \"popularity\": 9554\n },\n {\n \"tag\": "Micromastictora",\n \"popularity\": 9543\n },\n {\n \"tag\": "gossypine",\n \"popularity\": 9532\n },\n {\n \"tag\": "amylodyspepsia",\n \"popularity\": 9521\n },\n {\n \"tag\": "Luciana",\n \"popularity\": 9510\n },\n {\n \"tag\": "meetly nonfisherman",\n \"popularity\": 9500\n },\n {\n \"tag\": "backhanded",\n \"popularity\": 9489\n },\n {\n \"tag\": "decrustation",\n \"popularity\": 9478\n },\n {\n \"tag\": "pinrail",\n \"popularity\": 9467\n },\n {\n \"tag\": "Mahori",\n \"popularity\": 9456\n },\n {\n \"tag\": "unsizable",\n \"popularity\": 9446\n },\n {\n \"tag\": "disawa",\n \"popularity\": 9435\n },\n {\n \"tag\": "launderability inconsidered",\n \"popularity\": 9424\n },\n {\n \"tag\": "unclassical",\n \"popularity\": 9414\n },\n {\n \"tag\": "inobtrusiveness",\n \"popularity\": 9403\n },\n {\n \"tag\": "sialogenous",\n \"popularity\": 9392\n },\n {\n \"tag\": "sulphonamide",\n \"popularity\": 9382\n },\n {\n \"tag\": "diluvion",\n \"popularity\": 9371\n },\n {\n \"tag\": "deuteranope",\n \"popularity\": 9361\n },\n {\n \"tag\": "addition",\n \"popularity\": 9350\n },\n {\n \"tag\": "bockeret",\n \"popularity\": 9339\n },\n {\n \"tag\": "unidentified",\n \"popularity\": 9329\n },\n {\n \"tag\": "caryatic",\n \"popularity\": 9318\n },\n {\n \"tag\": "misattribution",\n \"popularity\": 9308\n },\n {\n \"tag\": "outray",\n \"popularity\": 9297\n },\n {\n \"tag\": "areometrical",\n \"popularity\": 9287\n },\n {\n \"tag\": "antilogism",\n \"popularity\": 9277\n },\n {\n \"tag\": "inadjustable",\n \"popularity\": 9266\n },\n {\n \"tag\": "byssus",\n \"popularity\": 9256\n },\n {\n \"tag\": "trun",\n \"popularity\": 9245\n },\n {\n \"tag\": "thereology",\n \"popularity\": 9235\n },\n {\n \"tag\": "extort",\n \"popularity\": 9225\n },\n {\n \"tag\": "bumpkin",\n \"popularity\": 9214\n },\n {\n \"tag\": "sulphobenzide",\n \"popularity\": 9204\n },\n {\n \"tag\": "hydrogeology",\n \"popularity\": 9194\n },\n {\n \"tag\": "nidulariaceous",\n \"popularity\": 9183\n },\n {\n \"tag\": "propodiale",\n \"popularity\": 9173\n },\n {\n \"tag\": "fierily",\n \"popularity\": 9163\n },\n {\n \"tag\": "aerotonometry",\n \"popularity\": 9153\n },\n {\n \"tag\": "pelobatid oversuperstitious",\n \"popularity\": 9142\n },\n {\n \"tag\": "restringent",\n \"popularity\": 9132\n },\n {\n \"tag\": "tetrapodic",\n \"popularity\": 9122\n },\n {\n \"tag\": "heroicness Vendidad",\n \"popularity\": 9112\n },\n {\n \"tag\": "Sphingurus",\n \"popularity\": 9102\n },\n {\n \"tag\": "sclerote",\n \"popularity\": 9092\n },\n {\n \"tag\": "unkeyed",\n \"popularity\": 9082\n },\n {\n \"tag\": "superparliamentary",\n \"popularity\": 9072\n },\n {\n \"tag\": "hetericism",\n \"popularity\": 9061\n },\n {\n \"tag\": "hucklebone",\n \"popularity\": 9051\n },\n {\n \"tag\": "yojan",\n \"popularity\": 9041\n },\n {\n \"tag\": "bossed",\n \"popularity\": 9031\n },\n {\n \"tag\": "spiderwork",\n \"popularity\": 9021\n },\n {\n \"tag\": "millfeed dullery",\n \"popularity\": 9011\n },\n {\n \"tag\": "adnoun",\n \"popularity\": 9001\n },\n {\n \"tag\": "mesometric",\n \"popularity\": 8992\n },\n {\n \"tag\": "doublehandedness",\n \"popularity\": 8982\n },\n {\n \"tag\": "suppurant",\n \"popularity\": 8972\n },\n {\n \"tag\": "Berlinize",\n \"popularity\": 8962\n },\n {\n \"tag\": "sontag",\n \"popularity\": 8952\n },\n {\n \"tag\": "biplane",\n \"popularity\": 8942\n },\n {\n \"tag\": "insula",\n \"popularity\": 8932\n },\n {\n \"tag\": "unbrand",\n \"popularity\": 8922\n },\n {\n \"tag\": "Basilosaurus",\n \"popularity\": 8913\n },\n {\n \"tag\": "prenomination",\n \"popularity\": 8903\n },\n {\n \"tag\": "untextual",\n \"popularity\": 8893\n },\n {\n \"tag\": "coleslaw",\n \"popularity\": 8883\n },\n {\n \"tag\": "langsyne",\n \"popularity\": 8874\n },\n {\n \"tag\": "impede",\n \"popularity\": 8864\n },\n {\n \"tag\": "irrigator",\n \"popularity\": 8854\n },\n {\n \"tag\": "deflocculation",\n \"popularity\": 8844\n },\n {\n \"tag\": "narghile",\n \"popularity\": 8835\n },\n {\n \"tag\": "unguardedly ebenaceous",\n \"popularity\": 8825\n },\n {\n \"tag\": "conversantly subocular",\n \"popularity\": 8815\n },\n {\n \"tag\": "hydroponic",\n \"popularity\": 8806\n },\n {\n \"tag\": "anthropopsychism",\n \"popularity\": 8796\n },\n {\n \"tag\": "panoptic",\n \"popularity\": 8787\n },\n {\n \"tag\": "insufferable",\n \"popularity\": 8777\n },\n {\n \"tag\": "salema",\n \"popularity\": 8768\n },\n {\n \"tag\": "Myriapoda",\n \"popularity\": 8758\n },\n {\n \"tag\": "regarrison",\n \"popularity\": 8748\n },\n {\n \"tag\": "overlearned",\n \"popularity\": 8739\n },\n {\n \"tag\": "ultraroyalist conventical bureaucratical",\n \"popularity\": 8729\n },\n {\n \"tag\": "epicaridan",\n \"popularity\": 8720\n },\n {\n \"tag\": "poetastress",\n \"popularity\": 8711\n },\n {\n \"tag\": "monophthalmus",\n \"popularity\": 8701\n },\n {\n \"tag\": "simnel",\n \"popularity\": 8692\n },\n {\n \"tag\": "compotor",\n \"popularity\": 8682\n },\n {\n \"tag\": "hydrolase",\n \"popularity\": 8673\n },\n {\n \"tag\": "attemptless",\n \"popularity\": 8663\n },\n {\n \"tag\": "visceroptosis",\n \"popularity\": 8654\n },\n {\n \"tag\": "unpreparedly",\n \"popularity\": 8645\n },\n {\n \"tag\": "mastage",\n \"popularity\": 8635\n },\n {\n \"tag\": "preinfluence",\n \"popularity\": 8626\n },\n {\n \"tag\": "Siwan",\n \"popularity\": 8617\n },\n {\n \"tag\": "ceratotheca belvedere",\n \"popularity\": 8607\n },\n {\n \"tag\": "disenablement",\n \"popularity\": 8598\n },\n {\n \"tag\": "nine",\n \"popularity\": 8589\n },\n {\n \"tag\": "spellingdown abridgment",\n \"popularity\": 8580\n },\n {\n \"tag\": "twilightless",\n \"popularity\": 8571\n },\n {\n \"tag\": "overflow",\n \"popularity\": 8561\n },\n {\n \"tag\": "mismeasurement",\n \"popularity\": 8552\n },\n {\n \"tag\": "nawabship",\n \"popularity\": 8543\n },\n {\n \"tag\": "Phrynosoma",\n \"popularity\": 8534\n },\n {\n \"tag\": "unanticipatingly",\n \"popularity\": 8525\n },\n {\n \"tag\": "blankite",\n \"popularity\": 8516\n },\n {\n \"tag\": "role",\n \"popularity\": 8506\n },\n {\n \"tag\": "peperine edelweiss",\n \"popularity\": 8497\n },\n {\n \"tag\": "unhysterical",\n \"popularity\": 8488\n },\n {\n \"tag\": "attentiveness",\n \"popularity\": 8479\n },\n {\n \"tag\": "scintillant",\n \"popularity\": 8470\n },\n {\n \"tag\": "stenostomatous",\n \"popularity\": 8461\n },\n {\n \"tag\": "pectinite",\n \"popularity\": 8452\n },\n {\n \"tag\": "herring",\n \"popularity\": 8443\n },\n {\n \"tag\": "interroom",\n \"popularity\": 8434\n },\n {\n \"tag\": "laccol",\n \"popularity\": 8425\n },\n {\n \"tag\": "unpartably kylite",\n \"popularity\": 8416\n },\n {\n \"tag\": "spirivalve",\n \"popularity\": 8407\n },\n {\n \"tag\": "hoosegow",\n \"popularity\": 8398\n },\n {\n \"tag\": "doat",\n \"popularity\": 8389\n },\n {\n \"tag\": "amphibian",\n \"popularity\": 8380\n },\n {\n \"tag\": "exposit",\n \"popularity\": 8371\n },\n {\n \"tag\": "canopy",\n \"popularity\": 8363\n },\n {\n \"tag\": "houndlike",\n \"popularity\": 8354\n },\n {\n \"tag\": "spikebill",\n \"popularity\": 8345\n },\n {\n \"tag\": "wiseacre pyrotechnic",\n \"popularity\": 8336\n },\n {\n \"tag\": "confessingly woodman",\n \"popularity\": 8327\n },\n {\n \"tag\": "overside",\n \"popularity\": 8318\n },\n {\n \"tag\": "oftwhiles",\n \"popularity\": 8310\n },\n {\n \"tag\": "Musophagidae",\n \"popularity\": 8301\n },\n {\n \"tag\": "slumberer",\n \"popularity\": 8292\n },\n {\n \"tag\": "leiotrichy",\n \"popularity\": 8283\n },\n {\n \"tag\": "Mantispidae",\n \"popularity\": 8275\n },\n {\n \"tag\": "perceptually",\n \"popularity\": 8266\n },\n {\n \"tag\": "biller",\n \"popularity\": 8257\n },\n {\n \"tag\": "eudaemonical",\n \"popularity\": 8249\n },\n {\n \"tag\": "underfiend",\n \"popularity\": 8240\n },\n {\n \"tag\": "impartible",\n \"popularity\": 8231\n },\n {\n \"tag\": "saxicavous",\n \"popularity\": 8223\n },\n {\n \"tag\": "yapster",\n \"popularity\": 8214\n },\n {\n \"tag\": "aliseptal",\n \"popularity\": 8205\n },\n {\n \"tag\": "omniparient",\n \"popularity\": 8197\n },\n {\n \"tag\": "nishiki",\n \"popularity\": 8188\n },\n {\n \"tag\": "yuzluk",\n \"popularity\": 8180\n },\n {\n \"tag\": "solderer",\n \"popularity\": 8171\n },\n {\n \"tag\": "Pinna",\n \"popularity\": 8162\n },\n {\n \"tag\": "reinterfere",\n \"popularity\": 8154\n },\n {\n \"tag\": "superepic",\n \"popularity\": 8145\n },\n {\n \"tag\": "ronquil",\n \"popularity\": 8137\n },\n {\n \"tag\": "bratstvo",\n \"popularity\": 8128\n },\n {\n \"tag\": "Thea",\n \"popularity\": 8120\n },\n {\n \"tag\": "hermaphroditical",\n \"popularity\": 8111\n },\n {\n \"tag\": "enlief",\n \"popularity\": 8103\n },\n {\n \"tag\": "Jesuate",\n \"popularity\": 8095\n },\n {\n \"tag\": "gaysome",\n \"popularity\": 8086\n },\n {\n \"tag\": "iliohypogastric",\n \"popularity\": 8078\n },\n {\n \"tag\": "regardance",\n \"popularity\": 8069\n },\n {\n \"tag\": "cumulately",\n \"popularity\": 8061\n },\n {\n \"tag\": "haustorial nucleolocentrosome",\n \"popularity\": 8053\n },\n {\n \"tag\": "cosmocrat",\n \"popularity\": 8044\n },\n {\n \"tag\": "onyxitis",\n \"popularity\": 8036\n },\n {\n \"tag\": "Cabinda",\n \"popularity\": 8028\n },\n {\n \"tag\": "coresort",\n \"popularity\": 8019\n },\n {\n \"tag\": "drusy preformant",\n \"popularity\": 8011\n },\n {\n \"tag\": "piningly",\n \"popularity\": 8003\n },\n {\n \"tag\": "bootlessly",\n \"popularity\": 7994\n },\n {\n \"tag\": "talari",\n \"popularity\": 7986\n },\n {\n \"tag\": "amidoacetal",\n \"popularity\": 7978\n },\n {\n \"tag\": "pschent",\n \"popularity\": 7970\n },\n {\n \"tag\": "consumptional scarer titivate",\n \"popularity\": 7962\n },\n {\n \"tag\": "Anserinae",\n \"popularity\": 7953\n },\n {\n \"tag\": "flaunter",\n \"popularity\": 7945\n },\n {\n \"tag\": "reindeer",\n \"popularity\": 7937\n },\n {\n \"tag\": "disparage",\n \"popularity\": 7929\n },\n {\n \"tag\": "superheat",\n \"popularity\": 7921\n },\n {\n \"tag\": "Chromatium",\n \"popularity\": 7912\n },\n {\n \"tag\": "Tina",\n \"popularity\": 7904\n },\n {\n \"tag\": "rededicatory",\n \"popularity\": 7896\n },\n {\n \"tag\": "nontransient",\n \"popularity\": 7888\n },\n {\n \"tag\": "Phocaean brinkless",\n \"popularity\": 7880\n },\n {\n \"tag\": "ventriculose",\n \"popularity\": 7872\n },\n {\n \"tag\": "upplough",\n \"popularity\": 7864\n },\n {\n \"tag\": "succorless",\n \"popularity\": 7856\n },\n {\n \"tag\": "hayrake",\n \"popularity\": 7848\n },\n {\n \"tag\": "merriness amorphia",\n \"popularity\": 7840\n },\n {\n \"tag\": "merycism",\n \"popularity\": 7832\n },\n {\n \"tag\": "checkrow",\n \"popularity\": 7824\n },\n {\n \"tag\": "scry",\n \"popularity\": 7816\n },\n {\n \"tag\": "obvolve",\n \"popularity\": 7808\n },\n {\n \"tag\": "orchard",\n \"popularity\": 7800\n },\n {\n \"tag\": "isomerize",\n \"popularity\": 7792\n },\n {\n \"tag\": "competitrix",\n \"popularity\": 7784\n },\n {\n \"tag\": "unbannered",\n \"popularity\": 7776\n },\n {\n \"tag\": "undoctrined",\n \"popularity\": 7768\n },\n {\n \"tag\": "theologian",\n \"popularity\": 7760\n },\n {\n \"tag\": "nebby",\n \"popularity\": 7752\n },\n {\n \"tag\": "Cardiazol",\n \"popularity\": 7745\n },\n {\n \"tag\": "phagedenic",\n \"popularity\": 7737\n },\n {\n \"tag\": "nostalgic",\n \"popularity\": 7729\n },\n {\n \"tag\": "orthodoxy",\n \"popularity\": 7721\n },\n {\n \"tag\": "oversanguine",\n \"popularity\": 7713\n },\n {\n \"tag\": "lish",\n \"popularity\": 7705\n },\n {\n \"tag\": "ketogenic",\n \"popularity\": 7698\n },\n {\n \"tag\": "syndicalize",\n \"popularity\": 7690\n },\n {\n \"tag\": "leeftail",\n \"popularity\": 7682\n },\n {\n \"tag\": "bulbomedullary",\n \"popularity\": 7674\n },\n {\n \"tag\": "reletter",\n \"popularity\": 7667\n },\n {\n \"tag\": "bitterly",\n \"popularity\": 7659\n },\n {\n \"tag\": "participatory",\n \"popularity\": 7651\n },\n {\n \"tag\": "baldberry",\n \"popularity\": 7643\n },\n {\n \"tag\": "prowaterpower",\n \"popularity\": 7636\n },\n {\n \"tag\": "lexicographical",\n \"popularity\": 7628\n },\n {\n \"tag\": "Anisodactyli",\n \"popularity\": 7620\n },\n {\n \"tag\": "amphipodous",\n \"popularity\": 7613\n },\n {\n \"tag\": "triglandular",\n \"popularity\": 7605\n },\n {\n \"tag\": "xanthopsin",\n \"popularity\": 7597\n },\n {\n \"tag\": "indefinitude",\n \"popularity\": 7590\n },\n {\n \"tag\": "bookworm",\n \"popularity\": 7582\n },\n {\n \"tag\": "suffocative",\n \"popularity\": 7574\n },\n {\n \"tag\": "uncongested tyrant",\n \"popularity\": 7567\n },\n {\n \"tag\": "alow harmoniously Pamir",\n \"popularity\": 7559\n },\n {\n \"tag\": "monander",\n \"popularity\": 7552\n },\n {\n \"tag\": "bagatelle",\n \"popularity\": 7544\n },\n {\n \"tag\": "membranology",\n \"popularity\": 7537\n },\n {\n \"tag\": "parturifacient",\n \"popularity\": 7529\n },\n {\n \"tag\": "excitovascular",\n \"popularity\": 7522\n },\n {\n \"tag\": "homopolar",\n \"popularity\": 7514\n },\n {\n \"tag\": "phobiac",\n \"popularity\": 7507\n },\n {\n \"tag\": "clype",\n \"popularity\": 7499\n },\n {\n \"tag\": "unsubversive",\n \"popularity\": 7492\n },\n {\n \"tag\": "bostrychoidal scorpionwort",\n \"popularity\": 7484\n },\n {\n \"tag\": "biliteralism",\n \"popularity\": 7477\n },\n {\n \"tag\": "dentatocostate",\n \"popularity\": 7469\n },\n {\n \"tag\": "Pici",\n \"popularity\": 7462\n },\n {\n \"tag\": "sideritic",\n \"popularity\": 7454\n },\n {\n \"tag\": "syntaxis",\n \"popularity\": 7447\n },\n {\n \"tag\": "ingest",\n \"popularity\": 7440\n },\n {\n \"tag\": "rigmarolish",\n \"popularity\": 7432\n },\n {\n \"tag\": "ocreaceous",\n \"popularity\": 7425\n },\n {\n \"tag\": "hyperbrachyskelic",\n \"popularity\": 7418\n },\n {\n \"tag\": "basophobia",\n \"popularity\": 7410\n },\n {\n \"tag\": "substantialness",\n \"popularity\": 7403\n },\n {\n \"tag\": "agglutinoid",\n \"popularity\": 7396\n },\n {\n \"tag\": "longleaf",\n \"popularity\": 7388\n },\n {\n \"tag\": "electroengraving",\n \"popularity\": 7381\n },\n {\n \"tag\": "laparoenterotomy",\n \"popularity\": 7374\n },\n {\n \"tag\": "oxalylurea",\n \"popularity\": 7366\n },\n {\n \"tag\": "unattaintedly",\n \"popularity\": 7359\n },\n {\n \"tag\": "pennystone",\n \"popularity\": 7352\n },\n {\n \"tag\": "Plumbaginaceae",\n \"popularity\": 7345\n },\n {\n \"tag\": "horntip",\n \"popularity\": 7337\n },\n {\n \"tag\": "begrudge",\n \"popularity\": 7330\n },\n {\n \"tag\": "bechignoned",\n \"popularity\": 7323\n },\n {\n \"tag\": "hologonidium",\n \"popularity\": 7316\n },\n {\n \"tag\": "Pulian",\n \"popularity\": 7309\n },\n {\n \"tag\": "gratulation",\n \"popularity\": 7301\n },\n {\n \"tag\": "Sebright",\n \"popularity\": 7294\n },\n {\n \"tag\": "coinstantaneous emotionally",\n \"popularity\": 7287\n },\n {\n \"tag\": "thoracostracan",\n \"popularity\": 7280\n },\n {\n \"tag\": "saurodont",\n \"popularity\": 7273\n },\n {\n \"tag\": "coseat",\n \"popularity\": 7266\n },\n {\n \"tag\": "irascibility",\n \"popularity\": 7259\n },\n {\n \"tag\": "occlude",\n \"popularity\": 7251\n },\n {\n \"tag\": "metallurgist",\n \"popularity\": 7244\n },\n {\n \"tag\": "extraviolet",\n \"popularity\": 7237\n },\n {\n \"tag\": "clinic",\n \"popularity\": 7230\n },\n {\n \"tag\": "skater",\n \"popularity\": 7223\n },\n {\n \"tag\": "linguistic",\n \"popularity\": 7216\n },\n {\n \"tag\": "attacheship",\n \"popularity\": 7209\n },\n {\n \"tag\": "Rachianectes",\n \"popularity\": 7202\n },\n {\n \"tag\": "foliolose",\n \"popularity\": 7195\n },\n {\n \"tag\": "claudetite",\n \"popularity\": 7188\n },\n {\n \"tag\": "aphidian scratching",\n \"popularity\": 7181\n },\n {\n \"tag\": "Carida",\n \"popularity\": 7174\n },\n {\n \"tag\": "tiepin polymicroscope",\n \"popularity\": 7167\n },\n {\n \"tag\": "telpherage",\n \"popularity\": 7160\n },\n {\n \"tag\": "meek",\n \"popularity\": 7153\n },\n {\n \"tag\": "swiftness",\n \"popularity\": 7146\n },\n {\n \"tag\": "gentes",\n \"popularity\": 7139\n },\n {\n \"tag\": "uncommemorated",\n \"popularity\": 7132\n },\n {\n \"tag\": "Lazarus",\n \"popularity\": 7125\n },\n {\n \"tag\": "redivive",\n \"popularity\": 7119\n },\n {\n \"tag\": "nonfebrile",\n \"popularity\": 7112\n },\n {\n \"tag\": "nymphet",\n \"popularity\": 7105\n },\n {\n \"tag\": "areologically",\n \"popularity\": 7098\n },\n {\n \"tag\": "undonkey",\n \"popularity\": 7091\n },\n {\n \"tag\": "projecting",\n \"popularity\": 7084\n },\n {\n \"tag\": "pinnigrade",\n \"popularity\": 7077\n },\n {\n \"tag\": "butylation",\n \"popularity\": 7071\n },\n {\n \"tag\": "philologistic lenticle",\n \"popularity\": 7064\n },\n {\n \"tag\": "nooky",\n \"popularity\": 7057\n },\n {\n \"tag\": "incestuousness",\n \"popularity\": 7050\n },\n {\n \"tag\": "palingenetically",\n \"popularity\": 7043\n },\n {\n \"tag\": "mitochondria",\n \"popularity\": 7037\n },\n {\n \"tag\": "truthify",\n \"popularity\": 7030\n },\n {\n \"tag\": "titanyl",\n \"popularity\": 7023\n },\n {\n \"tag\": "bestride",\n \"popularity\": 7016\n },\n {\n \"tag\": "chende",\n \"popularity\": 7010\n },\n {\n \"tag\": "Chaucerian monophote",\n \"popularity\": 7003\n },\n {\n \"tag\": "cutback",\n \"popularity\": 6996\n },\n {\n \"tag\": "unpatiently",\n \"popularity\": 6989\n },\n {\n \"tag\": "subvitreous",\n \"popularity\": 6983\n },\n {\n \"tag\": "organizable",\n \"popularity\": 6976\n },\n {\n \"tag\": "anniverse uncomprehensible",\n \"popularity\": 6969\n },\n {\n \"tag\": "hyalescence",\n \"popularity\": 6963\n },\n {\n \"tag\": "amniochorial",\n \"popularity\": 6956\n },\n {\n \"tag\": "Corybantian",\n \"popularity\": 6949\n },\n {\n \"tag\": "genocide Scaphitidae",\n \"popularity\": 6943\n },\n {\n \"tag\": "accordionist",\n \"popularity\": 6936\n },\n {\n \"tag\": "becheck",\n \"popularity\": 6930\n },\n {\n \"tag\": "overproduce",\n \"popularity\": 6923\n },\n {\n \"tag\": "unmaniac frijolillo",\n \"popularity\": 6916\n },\n {\n \"tag\": "multisulcated",\n \"popularity\": 6910\n },\n {\n \"tag\": "wennebergite",\n \"popularity\": 6903\n },\n {\n \"tag\": "tautousious mowth",\n \"popularity\": 6897\n },\n {\n \"tag\": "marigold",\n \"popularity\": 6890\n },\n {\n \"tag\": "affray",\n \"popularity\": 6884\n },\n {\n \"tag\": "nonidolatrous",\n \"popularity\": 6877\n },\n {\n \"tag\": "aphrasia",\n \"popularity\": 6871\n },\n {\n \"tag\": "muddlingly",\n \"popularity\": 6864\n },\n {\n \"tag\": "clear",\n \"popularity\": 6858\n },\n {\n \"tag\": "Clitoria",\n \"popularity\": 6851\n },\n {\n \"tag\": "apportionment underwaist",\n \"popularity\": 6845\n },\n {\n \"tag\": "kodakist",\n \"popularity\": 6838\n },\n {\n \"tag\": "Momotidae",\n \"popularity\": 6832\n },\n {\n \"tag\": "cryptovalency",\n \"popularity\": 6825\n },\n {\n \"tag\": "floe",\n \"popularity\": 6819\n },\n {\n \"tag\": "aphagia",\n \"popularity\": 6812\n },\n {\n \"tag\": "brontograph",\n \"popularity\": 6806\n },\n {\n \"tag\": "tubulous",\n \"popularity\": 6799\n },\n {\n \"tag\": "unhorse",\n \"popularity\": 6793\n },\n {\n \"tag\": "chlordane",\n \"popularity\": 6787\n },\n {\n \"tag\": "colloquy brochan",\n \"popularity\": 6780\n },\n {\n \"tag\": "sloosh",\n \"popularity\": 6774\n },\n {\n \"tag\": "battered",\n \"popularity\": 6767\n },\n {\n \"tag\": "monocularity pluriguttulate",\n \"popularity\": 6761\n },\n {\n \"tag\": "chiastoneury",\n \"popularity\": 6755\n },\n {\n \"tag\": "Sanguinaria",\n \"popularity\": 6748\n },\n {\n \"tag\": "confessionary",\n \"popularity\": 6742\n },\n {\n \"tag\": "enzymic",\n \"popularity\": 6736\n },\n {\n \"tag\": "cord",\n \"popularity\": 6729\n },\n {\n \"tag\": "oviducal",\n \"popularity\": 6723\n },\n {\n \"tag\": "crozzle outsea",\n \"popularity\": 6717\n },\n {\n \"tag\": "balladical",\n \"popularity\": 6710\n },\n {\n \"tag\": "uncollectibleness",\n \"popularity\": 6704\n },\n {\n \"tag\": "predorsal",\n \"popularity\": 6698\n },\n {\n \"tag\": "reauthenticate",\n \"popularity\": 6692\n },\n {\n \"tag\": "ravissant",\n \"popularity\": 6685\n },\n {\n \"tag\": "advantageousness",\n \"popularity\": 6679\n },\n {\n \"tag\": "rung",\n \"popularity\": 6673\n },\n {\n \"tag\": "duncedom",\n \"popularity\": 6667\n },\n {\n \"tag\": "hematolite",\n \"popularity\": 6660\n },\n {\n \"tag\": "thisness",\n \"popularity\": 6654\n },\n {\n \"tag\": "mapau",\n \"popularity\": 6648\n },\n {\n \"tag\": "Hecatic",\n \"popularity\": 6642\n },\n {\n \"tag\": "meningoencephalocele",\n \"popularity\": 6636\n },\n {\n \"tag\": "confection sorra",\n \"popularity\": 6630\n },\n {\n \"tag\": "unsedate",\n \"popularity\": 6623\n },\n {\n \"tag\": "meningocerebritis",\n \"popularity\": 6617\n },\n {\n \"tag\": "biopsychological",\n \"popularity\": 6611\n },\n {\n \"tag\": "clavicithern",\n \"popularity\": 6605\n },\n {\n \"tag\": "resun",\n \"popularity\": 6599\n },\n {\n \"tag\": "bayamo",\n \"popularity\": 6593\n },\n {\n \"tag\": "seeableness",\n \"popularity\": 6587\n },\n {\n \"tag\": "hypsidolichocephalism",\n \"popularity\": 6581\n },\n {\n \"tag\": "salivous",\n \"popularity\": 6574\n },\n {\n \"tag\": "neumatize",\n \"popularity\": 6568\n },\n {\n \"tag\": "stree",\n \"popularity\": 6562\n },\n {\n \"tag\": "markshot",\n \"popularity\": 6556\n },\n {\n \"tag\": "phraseologically",\n \"popularity\": 6550\n },\n {\n \"tag\": "yealing",\n \"popularity\": 6544\n },\n {\n \"tag\": "puggy",\n \"popularity\": 6538\n },\n {\n \"tag\": "sexadecimal",\n \"popularity\": 6532\n },\n {\n \"tag\": "unofficerlike",\n \"popularity\": 6526\n },\n {\n \"tag\": "curiosa",\n \"popularity\": 6520\n },\n {\n \"tag\": "pedomotor",\n \"popularity\": 6514\n },\n {\n \"tag\": "astrally",\n \"popularity\": 6508\n },\n {\n \"tag\": "prosomatic",\n \"popularity\": 6502\n },\n {\n \"tag\": "bulletheaded",\n \"popularity\": 6496\n },\n {\n \"tag\": "fortuned",\n \"popularity\": 6490\n },\n {\n \"tag\": "pixy",\n \"popularity\": 6484\n },\n {\n \"tag\": "protectrix",\n \"popularity\": 6478\n },\n {\n \"tag\": "arthritical",\n \"popularity\": 6472\n },\n {\n \"tag\": "coction",\n \"popularity\": 6466\n },\n {\n \"tag\": "Anthropos",\n \"popularity\": 6460\n },\n {\n \"tag\": "runer",\n \"popularity\": 6454\n },\n {\n \"tag\": "prenotify",\n \"popularity\": 6449\n },\n {\n \"tag\": "microspheric gastroparalysis",\n \"popularity\": 6443\n },\n {\n \"tag\": "Jovicentrical",\n \"popularity\": 6437\n },\n {\n \"tag\": "ceratopsid",\n \"popularity\": 6431\n },\n {\n \"tag\": "Theodoric",\n \"popularity\": 6425\n },\n {\n \"tag\": "Pactolus",\n \"popularity\": 6419\n },\n {\n \"tag\": "spawning",\n \"popularity\": 6413\n },\n {\n \"tag\": "nonconfidential",\n \"popularity\": 6407\n },\n {\n \"tag\": "halotrichite infumate",\n \"popularity\": 6402\n },\n {\n \"tag\": "undiscriminatingly",\n \"popularity\": 6396\n },\n {\n \"tag\": "unexasperated",\n \"popularity\": 6390\n },\n {\n \"tag\": "isoeugenol",\n \"popularity\": 6384\n },\n {\n \"tag\": "pressboard",\n \"popularity\": 6378\n },\n {\n \"tag\": "unshrew",\n \"popularity\": 6372\n },\n {\n \"tag\": "huffingly",\n \"popularity\": 6367\n },\n {\n \"tag\": "wagaun",\n \"popularity\": 6361\n },\n {\n \"tag\": "squirt Philistine",\n \"popularity\": 6355\n },\n {\n \"tag\": "kryptic",\n \"popularity\": 6349\n },\n {\n \"tag\": "paraform",\n \"popularity\": 6344\n },\n {\n \"tag\": "preverify",\n \"popularity\": 6338\n },\n {\n \"tag\": "dalar",\n \"popularity\": 6332\n },\n {\n \"tag\": "interdictor appraisingly",\n \"popularity\": 6326\n },\n {\n \"tag\": "chipped",\n \"popularity\": 6321\n },\n {\n \"tag\": "Pteropoda",\n \"popularity\": 6315\n },\n {\n \"tag\": "Bohairic",\n \"popularity\": 6309\n },\n {\n \"tag\": "felting",\n \"popularity\": 6303\n },\n {\n \"tag\": "compurgatorial",\n \"popularity\": 6298\n },\n {\n \"tag\": "unclead",\n \"popularity\": 6292\n },\n {\n \"tag\": "stockish",\n \"popularity\": 6286\n },\n {\n \"tag\": "mulligatawny",\n \"popularity\": 6281\n },\n {\n \"tag\": "Monotheletism",\n \"popularity\": 6275\n },\n {\n \"tag\": "lutanist",\n \"popularity\": 6269\n },\n {\n \"tag\": "gluttonize",\n \"popularity\": 6264\n },\n {\n \"tag\": "hackneyed",\n \"popularity\": 6258\n },\n {\n \"tag\": "yield",\n \"popularity\": 6253\n },\n {\n \"tag\": "sulphonamido",\n \"popularity\": 6247\n },\n {\n \"tag\": "granulative",\n \"popularity\": 6241\n },\n {\n \"tag\": "swingy",\n \"popularity\": 6236\n },\n {\n \"tag\": "Desmidiales",\n \"popularity\": 6230\n },\n {\n \"tag\": "tootlish",\n \"popularity\": 6224\n },\n {\n \"tag\": "unsatisfiedly",\n \"popularity\": 6219\n },\n {\n \"tag\": "burucha",\n \"popularity\": 6213\n },\n {\n \"tag\": "premeditatingly",\n \"popularity\": 6208\n },\n {\n \"tag\": "cowrie",\n \"popularity\": 6202\n },\n {\n \"tag\": "pleurolysis",\n \"popularity\": 6197\n },\n {\n \"tag\": "nationalist",\n \"popularity\": 6191\n },\n {\n \"tag\": "Pholadacea",\n \"popularity\": 6186\n },\n {\n \"tag\": "anakrousis",\n \"popularity\": 6180\n },\n {\n \"tag\": "proctorial",\n \"popularity\": 6175\n },\n {\n \"tag\": "cavillation",\n \"popularity\": 6169\n },\n {\n \"tag\": "cervicobregmatic",\n \"popularity\": 6163\n },\n {\n \"tag\": "interspecific",\n \"popularity\": 6158\n },\n {\n \"tag\": "Teutonity",\n \"popularity\": 6152\n },\n {\n \"tag\": "snakeholing",\n \"popularity\": 6147\n },\n {\n \"tag\": "balcony",\n \"popularity\": 6142\n },\n {\n \"tag\": "latchless",\n \"popularity\": 6136\n },\n {\n \"tag\": "Mithraea",\n \"popularity\": 6131\n },\n {\n \"tag\": "pseudepigraph",\n \"popularity\": 6125\n },\n {\n \"tag\": "flosser",\n \"popularity\": 6120\n },\n {\n \"tag\": "kotyle",\n \"popularity\": 6114\n },\n {\n \"tag\": "outdo",\n \"popularity\": 6109\n },\n {\n \"tag\": "interclerical",\n \"popularity\": 6103\n },\n {\n \"tag\": "aurar",\n \"popularity\": 6098\n },\n {\n \"tag\": "apophyseal",\n \"popularity\": 6093\n },\n {\n \"tag\": "Miro",\n \"popularity\": 6087\n },\n {\n \"tag\": "Priscillian",\n \"popularity\": 6082\n },\n {\n \"tag\": "alluvia",\n \"popularity\": 6076\n },\n {\n \"tag\": "exordize",\n \"popularity\": 6071\n },\n {\n \"tag\": "breakage",\n \"popularity\": 6066\n },\n {\n \"tag\": "unclosable",\n \"popularity\": 6060\n },\n {\n \"tag\": "monocondylous",\n \"popularity\": 6055\n },\n {\n \"tag\": "dyarchy",\n \"popularity\": 6050\n },\n {\n \"tag\": "subchelate",\n \"popularity\": 6044\n },\n {\n \"tag\": "hearsay",\n \"popularity\": 6039\n },\n {\n \"tag\": "prestigiously",\n \"popularity\": 6034\n },\n {\n \"tag\": "unimuscular",\n \"popularity\": 6028\n },\n {\n \"tag\": "lingwort",\n \"popularity\": 6023\n },\n {\n \"tag\": "jealous",\n \"popularity\": 6018\n },\n {\n \"tag\": "artilleryman",\n \"popularity\": 6012\n },\n {\n \"tag\": "phantasmagorially",\n \"popularity\": 6007\n },\n {\n \"tag\": "stagnum",\n \"popularity\": 6002\n },\n {\n \"tag\": "organotropism shatteringly",\n \"popularity\": 5997\n },\n {\n \"tag\": "Mytilus Hebraist",\n \"popularity\": 5991\n },\n {\n \"tag\": "returf",\n \"popularity\": 5986\n },\n {\n \"tag\": "townfolk",\n \"popularity\": 5981\n },\n {\n \"tag\": "propitiative",\n \"popularity\": 5976\n },\n {\n \"tag\": "Anita unsullied",\n \"popularity\": 5970\n },\n {\n \"tag\": "bandoleered",\n \"popularity\": 5965\n },\n {\n \"tag\": "cubby",\n \"popularity\": 5960\n },\n {\n \"tag\": "Hexanchus",\n \"popularity\": 5955\n },\n {\n \"tag\": "circuminsular",\n \"popularity\": 5949\n },\n {\n \"tag\": "chamberletted eumycete",\n \"popularity\": 5944\n },\n {\n \"tag\": "secure",\n \"popularity\": 5939\n },\n {\n \"tag\": "Edwardean",\n \"popularity\": 5934\n },\n {\n \"tag\": "strenth",\n \"popularity\": 5929\n },\n {\n \"tag\": "exhaustless",\n \"popularity\": 5923\n },\n {\n \"tag\": "electioneerer",\n \"popularity\": 5918\n },\n {\n \"tag\": "estoile",\n \"popularity\": 5913\n },\n {\n \"tag\": "redden",\n \"popularity\": 5908\n },\n {\n \"tag\": "solicitee",\n \"popularity\": 5903\n },\n {\n \"tag\": "nonpatented",\n \"popularity\": 5898\n },\n {\n \"tag\": "lemming",\n \"popularity\": 5893\n },\n {\n \"tag\": "marled subalate",\n \"popularity\": 5887\n },\n {\n \"tag\": "premial horizonward",\n \"popularity\": 5882\n },\n {\n \"tag\": "nonrefueling",\n \"popularity\": 5877\n },\n {\n \"tag\": "rupturewort",\n \"popularity\": 5872\n },\n {\n \"tag\": "unfed",\n \"popularity\": 5867\n },\n {\n \"tag\": "empanelment",\n \"popularity\": 5862\n },\n {\n \"tag\": "isoosmosis",\n \"popularity\": 5857\n },\n {\n \"tag\": "jipijapa",\n \"popularity\": 5852\n },\n {\n \"tag\": "Fiji",\n \"popularity\": 5847\n },\n {\n \"tag\": "interferant",\n \"popularity\": 5842\n },\n {\n \"tag\": "reconstitution",\n \"popularity\": 5837\n },\n {\n \"tag\": "dockyardman",\n \"popularity\": 5832\n },\n {\n \"tag\": "dolichopodous",\n \"popularity\": 5826\n },\n {\n \"tag\": "whiteworm",\n \"popularity\": 5821\n },\n {\n \"tag\": "atheistically",\n \"popularity\": 5816\n },\n {\n \"tag\": "nonconcern",\n \"popularity\": 5811\n },\n {\n \"tag\": "scarabaeidoid",\n \"popularity\": 5806\n },\n {\n \"tag\": "triumviri",\n \"popularity\": 5801\n },\n {\n \"tag\": "rakit",\n \"popularity\": 5796\n },\n {\n \"tag\": "leecheater",\n \"popularity\": 5791\n },\n {\n \"tag\": "Arthrostraca",\n \"popularity\": 5786\n },\n {\n \"tag\": "upknit",\n \"popularity\": 5781\n },\n {\n \"tag\": "tymbalon",\n \"popularity\": 5776\n },\n {\n \"tag\": "inventurous",\n \"popularity\": 5771\n },\n {\n \"tag\": "perradiate",\n \"popularity\": 5766\n },\n {\n \"tag\": "seer",\n \"popularity\": 5762\n },\n {\n \"tag\": "Auricularia",\n \"popularity\": 5757\n },\n {\n \"tag\": "wettish exclusivity",\n \"popularity\": 5752\n },\n {\n \"tag\": "arteriosympathectomy",\n \"popularity\": 5747\n },\n {\n \"tag\": "tunlike",\n \"popularity\": 5742\n },\n {\n \"tag\": "cephalocercal",\n \"popularity\": 5737\n },\n {\n \"tag\": "meaninglessness",\n \"popularity\": 5732\n },\n {\n \"tag\": "fountful",\n \"popularity\": 5727\n },\n {\n \"tag\": "appraisement",\n \"popularity\": 5722\n },\n {\n \"tag\": "geniculated",\n \"popularity\": 5717\n },\n {\n \"tag\": "rotator",\n \"popularity\": 5712\n },\n {\n \"tag\": "foremarch biography",\n \"popularity\": 5707\n },\n {\n \"tag\": "arid",\n \"popularity\": 5703\n },\n {\n \"tag\": "inapprehensible",\n \"popularity\": 5698\n },\n {\n \"tag\": "chlorosulphonic",\n \"popularity\": 5693\n },\n {\n \"tag\": "braguette",\n \"popularity\": 5688\n },\n {\n \"tag\": "panophthalmitis",\n \"popularity\": 5683\n },\n {\n \"tag\": "pro objurgatorily",\n \"popularity\": 5678\n },\n {\n \"tag\": "zooplasty",\n \"popularity\": 5673\n },\n {\n \"tag\": "Terebratulidae",\n \"popularity\": 5669\n },\n {\n \"tag\": "Mahran",\n \"popularity\": 5664\n },\n {\n \"tag\": "anthologize merocele",\n \"popularity\": 5659\n },\n {\n \"tag\": "firecracker chiropractic",\n \"popularity\": 5654\n },\n {\n \"tag\": "tenorist",\n \"popularity\": 5649\n },\n {\n \"tag\": "amphitene",\n \"popularity\": 5645\n },\n {\n \"tag\": "silverbush toadstone",\n \"popularity\": 5640\n },\n {\n \"tag\": "entozoological",\n \"popularity\": 5635\n },\n {\n \"tag\": "trustlessness",\n \"popularity\": 5630\n },\n {\n \"tag\": "reassay",\n \"popularity\": 5625\n },\n {\n \"tag\": "chrysalides",\n \"popularity\": 5621\n },\n {\n \"tag\": "truncation",\n \"popularity\": 5616\n },\n {\n \"tag\": "unwavered mausoleal",\n \"popularity\": 5611\n },\n {\n \"tag\": "unserrated",\n \"popularity\": 5606\n },\n {\n \"tag\": "frampler",\n \"popularity\": 5602\n },\n {\n \"tag\": "celestial",\n \"popularity\": 5597\n },\n {\n \"tag\": "depreter",\n \"popularity\": 5592\n },\n {\n \"tag\": "retaliate",\n \"popularity\": 5588\n },\n {\n \"tag\": "decempunctate",\n \"popularity\": 5583\n },\n {\n \"tag\": "submitter",\n \"popularity\": 5578\n },\n {\n \"tag\": "phenothiazine",\n \"popularity\": 5573\n },\n {\n \"tag\": "hobbledehoyish",\n \"popularity\": 5569\n },\n {\n \"tag\": "erraticness",\n \"popularity\": 5564\n },\n {\n \"tag\": "ovariodysneuria",\n \"popularity\": 5559\n },\n {\n \"tag\": "puja",\n \"popularity\": 5555\n },\n {\n \"tag\": "cesspool",\n \"popularity\": 5550\n },\n {\n \"tag\": "sonation",\n \"popularity\": 5545\n },\n {\n \"tag\": "moggan",\n \"popularity\": 5541\n },\n {\n \"tag\": "overjutting",\n \"popularity\": 5536\n },\n {\n \"tag\": "cohobate",\n \"popularity\": 5531\n },\n {\n \"tag\": "Distoma",\n \"popularity\": 5527\n },\n {\n \"tag\": "Plectognathi",\n \"popularity\": 5522\n },\n {\n \"tag\": "dumple caliphate",\n \"popularity\": 5517\n },\n {\n \"tag\": "shiko",\n \"popularity\": 5513\n },\n {\n \"tag\": "downness",\n \"popularity\": 5508\n },\n {\n \"tag\": "whippletree",\n \"popularity\": 5504\n },\n {\n \"tag\": "nymphaeum",\n \"popularity\": 5499\n },\n {\n \"tag\": "there trest",\n \"popularity\": 5494\n },\n {\n \"tag\": "psychrometer",\n \"popularity\": 5490\n },\n {\n \"tag\": "pyelograph",\n \"popularity\": 5485\n },\n {\n \"tag\": "unsalvable",\n \"popularity\": 5481\n },\n {\n \"tag\": "bescreen",\n \"popularity\": 5476\n },\n {\n \"tag\": "cushy",\n \"popularity\": 5471\n },\n {\n \"tag\": "plicatolobate",\n \"popularity\": 5467\n },\n {\n \"tag\": "lakie",\n \"popularity\": 5462\n },\n {\n \"tag\": "anthropodeoxycholic",\n \"popularity\": 5458\n },\n {\n \"tag\": "resatisfaction",\n \"popularity\": 5453\n },\n {\n \"tag\": "unravelment unaccidental",\n \"popularity\": 5449\n },\n {\n \"tag\": "telewriter monogeneous",\n \"popularity\": 5444\n },\n {\n \"tag\": "unsabred",\n \"popularity\": 5440\n },\n {\n \"tag\": "startlingly",\n \"popularity\": 5435\n },\n {\n \"tag\": "Aralia",\n \"popularity\": 5431\n },\n {\n \"tag\": "alamonti",\n \"popularity\": 5426\n },\n {\n \"tag\": "Franklinization",\n \"popularity\": 5422\n },\n {\n \"tag\": "parliament",\n \"popularity\": 5417\n },\n {\n \"tag\": "schoolkeeper",\n \"popularity\": 5413\n },\n {\n \"tag\": "nonsociety",\n \"popularity\": 5408\n },\n {\n \"tag\": "parenthetic",\n \"popularity\": 5404\n },\n {\n \"tag\": "stog",\n \"popularity\": 5399\n },\n {\n \"tag\": "Pristipomidae",\n \"popularity\": 5395\n },\n {\n \"tag\": "exocarp",\n \"popularity\": 5390\n },\n {\n \"tag\": "monaxonial",\n \"popularity\": 5386\n },\n {\n \"tag\": "tramroad",\n \"popularity\": 5381\n },\n {\n \"tag\": "hookah",\n \"popularity\": 5377\n },\n {\n \"tag\": "saccharonic",\n \"popularity\": 5372\n },\n {\n \"tag\": "perimetrium",\n \"popularity\": 5368\n },\n {\n \"tag\": "libelluloid",\n \"popularity\": 5364\n },\n {\n \"tag\": "overrunningly",\n \"popularity\": 5359\n },\n {\n \"tag\": "untwister",\n \"popularity\": 5355\n },\n {\n \"tag\": "ninnyhammer",\n \"popularity\": 5350\n },\n {\n \"tag\": "metranate",\n \"popularity\": 5346\n },\n {\n \"tag\": "sarcoblast",\n \"popularity\": 5341\n },\n {\n \"tag\": "porkish",\n \"popularity\": 5337\n },\n {\n \"tag\": "chauvinistic",\n \"popularity\": 5333\n },\n {\n \"tag\": "sexagesimal",\n \"popularity\": 5328\n },\n {\n \"tag\": "hematogenic",\n \"popularity\": 5324\n },\n {\n \"tag\": "selfpreservatory",\n \"popularity\": 5320\n },\n {\n \"tag\": "myelauxe",\n \"popularity\": 5315\n },\n {\n \"tag\": "triply",\n \"popularity\": 5311\n },\n {\n \"tag\": "metaphysicous",\n \"popularity\": 5306\n },\n {\n \"tag\": "vitrinoid",\n \"popularity\": 5302\n },\n {\n \"tag\": "glabellae",\n \"popularity\": 5298\n },\n {\n \"tag\": "moonlighter",\n \"popularity\": 5293\n },\n {\n \"tag\": "monotheistically epexegetical",\n \"popularity\": 5289\n },\n {\n \"tag\": "pseudolateral",\n \"popularity\": 5285\n },\n {\n \"tag\": "heptamethylene",\n \"popularity\": 5280\n },\n {\n \"tag\": "salvadora",\n \"popularity\": 5276\n },\n {\n \"tag\": "unjovial diphenylthiourea",\n \"popularity\": 5272\n },\n {\n \"tag\": "thievishness",\n \"popularity\": 5268\n },\n {\n \"tag\": "unridable",\n \"popularity\": 5263\n },\n {\n \"tag\": "underhandedly",\n \"popularity\": 5259\n },\n {\n \"tag\": "fungiform",\n \"popularity\": 5255\n },\n {\n \"tag\": "scruffle",\n \"popularity\": 5250\n },\n {\n \"tag\": "preindisposition",\n \"popularity\": 5246\n },\n {\n \"tag\": "Amadis",\n \"popularity\": 5242\n },\n {\n \"tag\": "Culex",\n \"popularity\": 5238\n },\n {\n \"tag\": "churning",\n \"popularity\": 5233\n },\n {\n \"tag\": "imperite",\n \"popularity\": 5229\n },\n {\n \"tag\": "levorotation",\n \"popularity\": 5225\n },\n {\n \"tag\": "barbate",\n \"popularity\": 5221\n },\n {\n \"tag\": "knotwort",\n \"popularity\": 5216\n },\n {\n \"tag\": "gypsiferous",\n \"popularity\": 5212\n },\n {\n \"tag\": "tourmalinic",\n \"popularity\": 5208\n },\n {\n \"tag\": "helleboric",\n \"popularity\": 5204\n },\n {\n \"tag\": "pneumograph",\n \"popularity\": 5199\n },\n {\n \"tag\": "Peltigeraceae",\n \"popularity\": 5195\n },\n {\n \"tag\": "busine",\n \"popularity\": 5191\n },\n {\n \"tag\": "Ailuridae",\n \"popularity\": 5187\n },\n {\n \"tag\": "azotate",\n \"popularity\": 5183\n },\n {\n \"tag\": "unlikable",\n \"popularity\": 5178\n },\n {\n \"tag\": "sloyd",\n \"popularity\": 5174\n },\n {\n \"tag\": "biblioclasm",\n \"popularity\": 5170\n },\n {\n \"tag\": "Seres",\n \"popularity\": 5166\n },\n {\n \"tag\": "unaccurateness",\n \"popularity\": 5162\n },\n {\n \"tag\": "scrollwise",\n \"popularity\": 5157\n },\n {\n \"tag\": "flandowser",\n \"popularity\": 5153\n },\n {\n \"tag\": "unblackened",\n \"popularity\": 5149\n },\n {\n \"tag\": "schistosternia",\n \"popularity\": 5145\n },\n {\n \"tag\": "fuse",\n \"popularity\": 5141\n },\n {\n \"tag\": "narthecal",\n \"popularity\": 5137\n },\n {\n \"tag\": "Cueva",\n \"popularity\": 5133\n },\n {\n \"tag\": "appositeness",\n \"popularity\": 5128\n },\n {\n \"tag\": "proindustrial",\n \"popularity\": 5124\n },\n {\n \"tag\": "dermatorrhoea",\n \"popularity\": 5120\n },\n {\n \"tag\": "oxyurous tendential",\n \"popularity\": 5116\n },\n {\n \"tag\": "isopurpurin",\n \"popularity\": 5112\n },\n {\n \"tag\": "impose",\n \"popularity\": 5108\n },\n {\n \"tag\": "wordsmanship",\n \"popularity\": 5104\n },\n {\n \"tag\": "saturator",\n \"popularity\": 5100\n },\n {\n \"tag\": "Nordicity",\n \"popularity\": 5096\n },\n {\n \"tag\": "interaccuse",\n \"popularity\": 5092\n },\n {\n \"tag\": "acridinic",\n \"popularity\": 5087\n },\n {\n \"tag\": "scholion",\n \"popularity\": 5083\n },\n {\n \"tag\": "pseudoaconitine",\n \"popularity\": 5079\n },\n {\n \"tag\": "doctorial",\n \"popularity\": 5075\n },\n {\n \"tag\": "Etchimin",\n \"popularity\": 5071\n },\n {\n \"tag\": "oliviform",\n \"popularity\": 5067\n },\n {\n \"tag\": "Pele",\n \"popularity\": 5063\n },\n {\n \"tag\": "Chiromantis Progymnasium",\n \"popularity\": 5059\n },\n {\n \"tag\": "toxosis",\n \"popularity\": 5055\n },\n {\n \"tag\": "spadilla",\n \"popularity\": 5051\n },\n {\n \"tag\": "Actinopterygii",\n \"popularity\": 5047\n },\n {\n \"tag\": "untiring",\n \"popularity\": 5043\n },\n {\n \"tag\": "butyral",\n \"popularity\": 5039\n },\n {\n \"tag\": "Gymnoderinae",\n \"popularity\": 5035\n },\n {\n \"tag\": "testudo",\n \"popularity\": 5031\n },\n {\n \"tag\": "frigorify",\n \"popularity\": 5027\n },\n {\n \"tag\": "aliency",\n \"popularity\": 5023\n },\n {\n \"tag\": "jargon",\n \"popularity\": 5019\n },\n {\n \"tag\": "counterservice",\n \"popularity\": 5015\n },\n {\n \"tag\": "isostrychnine",\n \"popularity\": 5011\n },\n {\n \"tag\": "tellership",\n \"popularity\": 5007\n },\n {\n \"tag\": "miscegenetic",\n \"popularity\": 5003\n },\n {\n \"tag\": "sorcer",\n \"popularity\": 4999\n },\n {\n \"tag\": "tilewright",\n \"popularity\": 4995\n },\n {\n \"tag\": "cyanoplastid",\n \"popularity\": 4991\n },\n {\n \"tag\": "fluxionally",\n \"popularity\": 4987\n },\n {\n \"tag\": "proudhearted",\n \"popularity\": 4983\n },\n {\n \"tag\": "blithely",\n \"popularity\": 4979\n },\n {\n \"tag\": "jestproof",\n \"popularity\": 4975\n },\n {\n \"tag\": "jestwise",\n \"popularity\": 4971\n },\n {\n \"tag\": "nonassimilable",\n \"popularity\": 4967\n },\n {\n \"tag\": "compurgation",\n \"popularity\": 4964\n },\n {\n \"tag\": "unhate",\n \"popularity\": 4960\n },\n {\n \"tag\": "haplodonty",\n \"popularity\": 4956\n },\n {\n \"tag\": "cardholder",\n \"popularity\": 4952\n },\n {\n \"tag\": "rainlight megohmmeter overstout",\n \"popularity\": 4948\n },\n {\n \"tag\": "itchless",\n \"popularity\": 4944\n },\n {\n \"tag\": "begiggle",\n \"popularity\": 4940\n },\n {\n \"tag\": "chromatosphere",\n \"popularity\": 4936\n },\n {\n \"tag\": "typicality",\n \"popularity\": 4932\n },\n {\n \"tag\": "overgrown",\n \"popularity\": 4928\n },\n {\n \"tag\": "envolume",\n \"popularity\": 4925\n },\n {\n \"tag\": "pachycholia",\n \"popularity\": 4921\n },\n {\n \"tag\": "passageable",\n \"popularity\": 4917\n },\n {\n \"tag\": "pathopoiesis",\n \"popularity\": 4913\n },\n {\n \"tag\": "overbreak",\n \"popularity\": 4909\n },\n {\n \"tag\": "satyric",\n \"popularity\": 4905\n },\n {\n \"tag\": "unaudited",\n \"popularity\": 4901\n },\n {\n \"tag\": "whimble",\n \"popularity\": 4898\n },\n {\n \"tag\": "pressureless",\n \"popularity\": 4894\n },\n {\n \"tag\": "Selene",\n \"popularity\": 4890\n },\n {\n \"tag\": "slithery",\n \"popularity\": 4886\n },\n {\n \"tag\": "nondisfigurement",\n \"popularity\": 4882\n },\n {\n \"tag\": "overdelicious",\n \"popularity\": 4878\n },\n {\n \"tag\": "Perca",\n \"popularity\": 4875\n },\n {\n \"tag\": "Palladium",\n \"popularity\": 4871\n },\n {\n \"tag\": "insagacity",\n \"popularity\": 4867\n },\n {\n \"tag\": "peristoma",\n \"popularity\": 4863\n },\n {\n \"tag\": "uncreativeness",\n \"popularity\": 4859\n },\n {\n \"tag\": "incomparability surfboarding",\n \"popularity\": 4856\n },\n {\n \"tag\": "bacillar",\n \"popularity\": 4852\n },\n {\n \"tag\": "ulcerative",\n \"popularity\": 4848\n },\n {\n \"tag\": "stychomythia",\n \"popularity\": 4844\n },\n {\n \"tag\": "sesma somatics nonentry",\n \"popularity\": 4840\n },\n {\n \"tag\": "unsepulchred",\n \"popularity\": 4837\n },\n {\n \"tag\": "cephalanthium",\n \"popularity\": 4833\n },\n {\n \"tag\": "Asiaticization",\n \"popularity\": 4829\n },\n {\n \"tag\": "killeen",\n \"popularity\": 4825\n },\n {\n \"tag\": "Pseudococcus",\n \"popularity\": 4822\n },\n {\n \"tag\": "untractable",\n \"popularity\": 4818\n },\n {\n \"tag\": "apolegamic",\n \"popularity\": 4814\n },\n {\n \"tag\": "hyperpnea",\n \"popularity\": 4810\n },\n {\n \"tag\": "martyrolatry",\n \"popularity\": 4807\n },\n {\n \"tag\": "Sarmatic",\n \"popularity\": 4803\n },\n {\n \"tag\": "nonsurface",\n \"popularity\": 4799\n },\n {\n \"tag\": "adjoined",\n \"popularity\": 4796\n },\n {\n \"tag\": "vasiform",\n \"popularity\": 4792\n },\n {\n \"tag\": "tastelessness",\n \"popularity\": 4788\n },\n {\n \"tag\": "rumbo",\n \"popularity\": 4784\n },\n {\n \"tag\": "subdititious",\n \"popularity\": 4781\n },\n {\n \"tag\": "reparticipation",\n \"popularity\": 4777\n },\n {\n \"tag\": "Yorkshireism",\n \"popularity\": 4773\n },\n {\n \"tag\": "outcrow",\n \"popularity\": 4770\n },\n {\n \"tag\": "casserole",\n \"popularity\": 4766\n },\n {\n \"tag\": "semideltaic",\n \"popularity\": 4762\n },\n {\n \"tag\": "freemason",\n \"popularity\": 4759\n },\n {\n \"tag\": "catkin",\n \"popularity\": 4755\n },\n {\n \"tag\": "conscient",\n \"popularity\": 4751\n },\n {\n \"tag\": "reliably",\n \"popularity\": 4748\n },\n {\n \"tag\": "Telembi",\n \"popularity\": 4744\n },\n {\n \"tag\": "hide",\n \"popularity\": 4740\n },\n {\n \"tag\": "social",\n \"popularity\": 4737\n },\n {\n \"tag\": "ichneutic",\n \"popularity\": 4733\n },\n {\n \"tag\": "polypotome blouse pentagrammatic",\n \"popularity\": 4729\n },\n {\n \"tag\": "airdrome pesthole",\n \"popularity\": 4726\n },\n {\n \"tag\": "unportended",\n \"popularity\": 4722\n },\n {\n \"tag\": "sheerly",\n \"popularity\": 4719\n },\n {\n \"tag\": "acardiac",\n \"popularity\": 4715\n },\n {\n \"tag\": "fetor",\n \"popularity\": 4711\n },\n {\n \"tag\": "storax",\n \"popularity\": 4708\n },\n {\n \"tag\": "syndactylic",\n \"popularity\": 4704\n },\n {\n \"tag\": "otiatrics",\n \"popularity\": 4700\n },\n {\n \"tag\": "range",\n \"popularity\": 4697\n },\n {\n \"tag\": "branchway",\n \"popularity\": 4693\n },\n {\n \"tag\": "beatific",\n \"popularity\": 4690\n },\n {\n \"tag\": "Rugosa",\n \"popularity\": 4686\n },\n {\n \"tag\": "rafty",\n \"popularity\": 4682\n },\n {\n \"tag\": "gapy",\n \"popularity\": 4679\n },\n {\n \"tag\": "heterocercal",\n \"popularity\": 4675\n },\n {\n \"tag\": "actinopterygious",\n \"popularity\": 4672\n },\n {\n \"tag\": "glauconite",\n \"popularity\": 4668\n },\n {\n \"tag\": "limbless priest",\n \"popularity\": 4665\n },\n {\n \"tag\": "chrysene",\n \"popularity\": 4661\n },\n {\n \"tag\": "isentropic",\n \"popularity\": 4658\n },\n {\n \"tag\": "lairdess",\n \"popularity\": 4654\n },\n {\n \"tag\": "butterhead choliambic",\n \"popularity\": 4650\n },\n {\n \"tag\": "hexaseme",\n \"popularity\": 4647\n },\n {\n \"tag\": "treeify",\n \"popularity\": 4643\n },\n {\n \"tag\": "coronetted fructify",\n \"popularity\": 4640\n },\n {\n \"tag\": "admiralty",\n \"popularity\": 4636\n },\n {\n \"tag\": "Flosculariidae",\n \"popularity\": 4633\n },\n {\n \"tag\": "limaceous",\n \"popularity\": 4629\n },\n {\n \"tag\": "subterconscious",\n \"popularity\": 4626\n },\n {\n \"tag\": "stayless",\n \"popularity\": 4622\n },\n {\n \"tag\": "psha",\n \"popularity\": 4619\n },\n {\n \"tag\": "Mediterraneanize",\n \"popularity\": 4615\n },\n {\n \"tag\": "impenetrably",\n \"popularity\": 4612\n },\n {\n \"tag\": "Myrmeleonidae",\n \"popularity\": 4608\n },\n {\n \"tag\": "germander",\n \"popularity\": 4605\n },\n {\n \"tag\": "Buri",\n \"popularity\": 4601\n },\n {\n \"tag\": "papyrotamia",\n \"popularity\": 4598\n },\n {\n \"tag\": "Toxylon",\n \"popularity\": 4594\n },\n {\n \"tag\": "batatilla",\n \"popularity\": 4591\n },\n {\n \"tag\": "fabella assumer",\n \"popularity\": 4587\n },\n {\n \"tag\": "macromethod",\n \"popularity\": 4584\n },\n {\n \"tag\": "Blechnum",\n \"popularity\": 4580\n },\n {\n \"tag\": "pantography",\n \"popularity\": 4577\n },\n {\n \"tag\": "seminovel",\n \"popularity\": 4574\n },\n {\n \"tag\": "disembarrassment",\n \"popularity\": 4570\n },\n {\n \"tag\": "bushmaking",\n \"popularity\": 4567\n },\n {\n \"tag\": "neurosis",\n \"popularity\": 4563\n },\n {\n \"tag\": "Animalia",\n \"popularity\": 4560\n },\n {\n \"tag\": "Bernice",\n \"popularity\": 4556\n },\n {\n \"tag\": "wisen",\n \"popularity\": 4553\n },\n {\n \"tag\": "subhymenium",\n \"popularity\": 4549\n },\n {\n \"tag\": "esophagomycosis",\n \"popularity\": 4546\n },\n {\n \"tag\": "wireworks",\n \"popularity\": 4543\n },\n {\n \"tag\": "Sabellidae",\n \"popularity\": 4539\n },\n {\n \"tag\": "fustianish",\n \"popularity\": 4536\n },\n {\n \"tag\": "professively",\n \"popularity\": 4532\n },\n {\n \"tag\": "overcorruptly",\n \"popularity\": 4529\n },\n {\n \"tag\": "overcreep",\n \"popularity\": 4526\n },\n {\n \"tag\": "Castilloa",\n \"popularity\": 4522\n },\n {\n \"tag\": "forelady Georgie",\n \"popularity\": 4519\n },\n {\n \"tag\": "outsider",\n \"popularity\": 4515\n },\n {\n \"tag\": "Enukki",\n \"popularity\": 4512\n },\n {\n \"tag\": "gypsy",\n \"popularity\": 4509\n },\n {\n \"tag\": "Passamaquoddy",\n \"popularity\": 4505\n },\n {\n \"tag\": "reposit",\n \"popularity\": 4502\n },\n {\n \"tag\": "overtenderness",\n \"popularity\": 4499\n },\n {\n \"tag\": "keratome",\n \"popularity\": 4495\n },\n {\n \"tag\": "interclavicular hypermonosyllable Susanna",\n \"popularity\": 4492\n },\n {\n \"tag\": "mispropose",\n \"popularity\": 4489\n },\n {\n \"tag\": "Membranipora",\n \"popularity\": 4485\n },\n {\n \"tag\": "lampad",\n \"popularity\": 4482\n },\n {\n \"tag\": "header",\n \"popularity\": 4479\n },\n {\n \"tag\": "triseriate",\n \"popularity\": 4475\n },\n {\n \"tag\": "distrainment",\n \"popularity\": 4472\n },\n {\n \"tag\": "staphyloplastic",\n \"popularity\": 4469\n },\n {\n \"tag\": "outscour",\n \"popularity\": 4465\n },\n {\n \"tag\": "tallowmaking",\n \"popularity\": 4462\n },\n {\n \"tag\": "plugger",\n \"popularity\": 4459\n },\n {\n \"tag\": "fashionize",\n \"popularity\": 4455\n },\n {\n \"tag\": "puzzle",\n \"popularity\": 4452\n },\n {\n \"tag\": "imbrue",\n \"popularity\": 4449\n },\n {\n \"tag\": "osteoblast",\n \"popularity\": 4445\n },\n {\n \"tag\": "Hydrocores",\n \"popularity\": 4442\n },\n {\n \"tag\": "Lutra",\n \"popularity\": 4439\n },\n {\n \"tag\": "upridge scarfy",\n \"popularity\": 4435\n },\n {\n \"tag\": "ancon taffle",\n \"popularity\": 4432\n },\n {\n \"tag\": "impest",\n \"popularity\": 4429\n },\n {\n \"tag\": "uncollatedness",\n \"popularity\": 4426\n },\n {\n \"tag\": "hypersensitize",\n \"popularity\": 4422\n },\n {\n \"tag\": "autographically",\n \"popularity\": 4419\n },\n {\n \"tag\": "louther",\n \"popularity\": 4416\n },\n {\n \"tag\": "Ollie",\n \"popularity\": 4413\n },\n {\n \"tag\": "recompensate",\n \"popularity\": 4409\n },\n {\n \"tag\": "Shan",\n \"popularity\": 4406\n },\n {\n \"tag\": "brachycnemic",\n \"popularity\": 4403\n },\n {\n \"tag\": "Carinatae",\n \"popularity\": 4399\n },\n {\n \"tag\": "geotherm",\n \"popularity\": 4396\n },\n {\n \"tag\": "sawback",\n \"popularity\": 4393\n },\n {\n \"tag\": "Novatianist",\n \"popularity\": 4390\n },\n {\n \"tag\": "reapproach",\n \"popularity\": 4387\n },\n {\n \"tag\": "myelopoietic",\n \"popularity\": 4383\n },\n {\n \"tag\": "cyanin",\n \"popularity\": 4380\n },\n {\n \"tag\": "unsmutted",\n \"popularity\": 4377\n },\n {\n \"tag\": "nonpapist",\n \"popularity\": 4374\n },\n {\n \"tag\": "transbaikalian",\n \"popularity\": 4370\n },\n {\n \"tag\": "connately",\n \"popularity\": 4367\n },\n {\n \"tag\": "tenderize iterance",\n \"popularity\": 4364\n },\n {\n \"tag\": "hydrostatical",\n \"popularity\": 4361\n },\n {\n \"tag\": "unflag",\n \"popularity\": 4358\n },\n {\n \"tag\": "translate",\n \"popularity\": 4354\n },\n {\n \"tag\": "Scorzonera",\n \"popularity\": 4351\n },\n {\n \"tag\": "uncomforted",\n \"popularity\": 4348\n },\n {\n \"tag\": "risser varied",\n \"popularity\": 4345\n },\n {\n \"tag\": "plumbate",\n \"popularity\": 4342\n },\n {\n \"tag\": "Usneaceae",\n \"popularity\": 4338\n },\n {\n \"tag\": "fohat",\n \"popularity\": 4335\n },\n {\n \"tag\": "slagging",\n \"popularity\": 4332\n },\n {\n \"tag\": "superserious",\n \"popularity\": 4329\n },\n {\n \"tag\": "theocracy",\n \"popularity\": 4326\n },\n {\n \"tag\": "valonia",\n \"popularity\": 4323\n },\n {\n \"tag\": "Sapindales",\n \"popularity\": 4319\n },\n {\n \"tag\": "palaeozoologist",\n \"popularity\": 4316\n },\n {\n \"tag\": "yalb",\n \"popularity\": 4313\n },\n {\n \"tag\": "unviewed",\n \"popularity\": 4310\n },\n {\n \"tag\": "polyarteritis",\n \"popularity\": 4307\n },\n {\n \"tag\": "vectorial",\n \"popularity\": 4304\n },\n {\n \"tag\": "skimpingly",\n \"popularity\": 4301\n },\n {\n \"tag\": "athort",\n \"popularity\": 4297\n },\n {\n \"tag\": "tribofluorescence",\n \"popularity\": 4294\n },\n {\n \"tag\": "benzonitrol",\n \"popularity\": 4291\n },\n {\n \"tag\": "swiller subobtuse subjacency",\n \"popularity\": 4288\n },\n {\n \"tag\": "uncompassed",\n \"popularity\": 4285\n },\n {\n \"tag\": "cacochymia",\n \"popularity\": 4282\n },\n {\n \"tag\": "commensalist butadiene",\n \"popularity\": 4279\n },\n {\n \"tag\": "culpable",\n \"popularity\": 4276\n },\n {\n \"tag\": "contributive",\n \"popularity\": 4273\n },\n {\n \"tag\": "attemperately",\n \"popularity\": 4269\n },\n {\n \"tag\": "spelt",\n \"popularity\": 4266\n },\n {\n \"tag\": "exoneration",\n \"popularity\": 4263\n },\n {\n \"tag\": "antivivisectionist",\n \"popularity\": 4260\n },\n {\n \"tag\": "granitification",\n \"popularity\": 4257\n },\n {\n \"tag\": "palladize",\n \"popularity\": 4254\n },\n {\n \"tag\": "marksmanship",\n \"popularity\": 4251\n },\n {\n \"tag\": "bullydom",\n \"popularity\": 4248\n },\n {\n \"tag\": "spirality",\n \"popularity\": 4245\n },\n {\n \"tag\": "caliginous",\n \"popularity\": 4242\n },\n {\n \"tag\": "reportedly",\n \"popularity\": 4239\n },\n {\n \"tag\": "polyad",\n \"popularity\": 4236\n },\n {\n \"tag\": "arthroempyesis",\n \"popularity\": 4233\n },\n {\n \"tag\": "semibay facultatively",\n \"popularity\": 4229\n },\n {\n \"tag\": "metastatically",\n \"popularity\": 4226\n },\n {\n \"tag\": "prophetically",\n \"popularity\": 4223\n },\n {\n \"tag\": "Linguatula elapid",\n \"popularity\": 4220\n },\n {\n \"tag\": "pyknatom",\n \"popularity\": 4217\n },\n {\n \"tag\": "centimeter",\n \"popularity\": 4214\n },\n {\n \"tag\": "mensurate",\n \"popularity\": 4211\n },\n {\n \"tag\": "migraine",\n \"popularity\": 4208\n },\n {\n \"tag\": "pentagamist",\n \"popularity\": 4205\n },\n {\n \"tag\": "querken",\n \"popularity\": 4202\n },\n {\n \"tag\": "ambulance",\n \"popularity\": 4199\n },\n {\n \"tag\": "Stokavian",\n \"popularity\": 4196\n },\n {\n \"tag\": "malvasian",\n \"popularity\": 4193\n },\n {\n \"tag\": "uncouthsome",\n \"popularity\": 4190\n },\n {\n \"tag\": "readable",\n \"popularity\": 4187\n },\n {\n \"tag\": "enlodge",\n \"popularity\": 4184\n },\n {\n \"tag\": "plasterwise Appendiculariidae perspectograph",\n \"popularity\": 4181\n },\n {\n \"tag\": "inkweed",\n \"popularity\": 4178\n },\n {\n \"tag\": "streep",\n \"popularity\": 4175\n },\n {\n \"tag\": "diadelphian cultured",\n \"popularity\": 4172\n },\n {\n \"tag\": "hymenopterous",\n \"popularity\": 4169\n },\n {\n \"tag\": "unexorableness",\n \"popularity\": 4166\n },\n {\n \"tag\": "cascaron",\n \"popularity\": 4163\n },\n {\n \"tag\": "undaintiness",\n \"popularity\": 4160\n },\n {\n \"tag\": "Curtana",\n \"popularity\": 4157\n },\n {\n \"tag\": "scurvied",\n \"popularity\": 4154\n },\n {\n \"tag\": "molluscoidal",\n \"popularity\": 4151\n },\n {\n \"tag\": "yurt",\n \"popularity\": 4148\n },\n {\n \"tag\": "deciduitis",\n \"popularity\": 4145\n },\n {\n \"tag\": "creephole",\n \"popularity\": 4142\n },\n {\n \"tag\": "quatrefeuille",\n \"popularity\": 4139\n },\n {\n \"tag\": "bicapitate adenomatome",\n \"popularity\": 4136\n },\n {\n \"tag\": "damassin",\n \"popularity\": 4134\n },\n {\n \"tag\": "planching",\n \"popularity\": 4131\n },\n {\n \"tag\": "dashedly inferential",\n \"popularity\": 4128\n },\n {\n \"tag\": "lobe",\n \"popularity\": 4125\n },\n {\n \"tag\": "Hyrachyus",\n \"popularity\": 4122\n },\n {\n \"tag\": "knab",\n \"popularity\": 4119\n },\n {\n \"tag\": "discohexaster",\n \"popularity\": 4116\n },\n {\n \"tag\": "malign",\n \"popularity\": 4113\n },\n {\n \"tag\": "pedagoguism",\n \"popularity\": 4110\n },\n {\n \"tag\": "shrubbery",\n \"popularity\": 4107\n },\n {\n \"tag\": "undershrub",\n \"popularity\": 4104\n },\n {\n \"tag\": "bureaucrat",\n \"popularity\": 4101\n },\n {\n \"tag\": "pantaleon",\n \"popularity\": 4098\n },\n {\n \"tag\": "mesoventral",\n \"popularity\": 4096\n }]'; + +var log2 = Math.log(2); +var tagInfo = tagInfoJSON.parseJSON(function(a, b) { if (a == "popularity") { return Math.log(b) / log2; } else {return b; } }); + +function makeTagCloud(tagInfo) +{ + var output = '<div class="tagCloud" style="width: 100%">'; + + tagInfo.sort(function(a, b) { if (a.tag < b.tag) { return -1; } else if (a.tag == b.tag) { return 0; } else return 1; }); + + for (var i = 0; i < tagInfo.length; i++) { + var tag = tagInfo[i].tag; + + var validates = true; + for (var j = 0; j < tag.length; j++) { + var ch = tag.charCodeAt(j); + if (ch < 0x20 || ch >= 0x7f) { + validates = false; + break; + } + } + + if (!validates) + continue; + + var url = "http://example.com/tag/" + tag.replace(" ", "").toLowerCase(); + var popularity = tagInfo[i].popularity; + var color = 'rgb(' + Math.floor(255 * (popularity - 12) / 20) + ', 0, 255)'; + output += ' <a href="' + url + '" style="font-size: ' + popularity + 'px; color: ' + color + '">' + tag + '</a> \n'; + } + + output += '</div>'; + output.replace(" ", " "); + + return output; +} + +var tagcloud = makeTagCloud(tagInfo); +tagInfo = null; + + +var _sunSpiderInterval = new Date() - _sunSpiderStartDate; + +document.getElementById("console").innerHTML = _sunSpiderInterval; +</script> + + +</body> +</html> diff --git a/build/pgo/js-input/string-unpack-code.html b/build/pgo/js-input/string-unpack-code.html new file mode 100644 index 000000000..ba80c99ea --- /dev/null +++ b/build/pgo/js-input/string-unpack-code.html @@ -0,0 +1,117 @@ +<!DOCTYPE html> +<head> +<!-- + Copyright (C) 2007 Apple Inc. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--> + +<title>SunSpider string-unpack-code</title> + +</head> + +<body> +<h3>string-unpack-code</h3> +<div id="console"> +</div> + +<script> + +var _sunSpiderStartDate = new Date(); + +// This test case unpacks the compressed code for the MochiKit, +// jQuery, Dojo and Prototype JavaScript libraries. + +/*** + MochiKit.MochiKit 1.3.1 : PACKED VERSION + THIS FILE IS AUTOMATICALLY GENERATED. If creating patches, please + diff against the source tree, not this file. + + See <http://mochikit.com/> for documentation, downloads, license, etc. + + (c) 2005 Bob Ippolito. All rights Reserved. +***/ + +for (var i = 0; i < 2; i++) { + +var decompressedMochiKit = function(p,a,c,k,e,d){e=function(c){return(c<a?"":e(parseInt(c/a)))+((c=c%a)>35?String.fromCharCode(c+29):c.toString(36))};if(!''.replace(/^/,String)){while(c--)d[e(c)]=k[c]||e(c);k=[function(e){return d[e]}];e=function(){return'\\w+'};c=1};while(c--)if(k[c])p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c]);return p}('if(H(1q)!="L"){1q.2X("B.J")}if(H(B)=="L"){B={}}if(H(B.J)=="L"){B.J={}}B.J.1Y="1.3.1";B.J.1r="B.J";B.J.2l=G(7V,vR){if(7V===O){7V={}}R(u i=1;i<M.K;i++){u o=M[i];if(H(o)!="L"&&o!==O){R(u k in o){7V[k]=o[k]}}}F 7V};B.J.2l(B.J,{1K:G(){F"["+D.1r+" "+D.1Y+"]"},1l:G(){F D.1K()},4f:G(n){if(M.K===0){n=1}F G(){F n++}},4L:G(mw){u me=M.2U;if(M.K==1){me.1U=mw;F Y me()}},bg:G(vQ){u X=[];u m=B.J;u aw=m.1R(O,M);1M(aw.K){u o=aw.2P();if(o&&H(o)=="3n"&&H(o.K)=="2y"){R(u i=o.K-1;i>=0;i--){aw.e9(o[i])}}N{X.1c(o)}}F X},1R:G(7U,1i,av){if(!av){av=0}if(1i){u l=1i.K;if(H(l)!="2y"){if(H(B.15)!="L"){1i=B.15.2G(1i);l=1i.K}N{14 Y 3p("au 2E an at-as 3W B.15 2E ar")}}if(!7U){7U=[]}R(u i=av;i<l;i++){7U.1c(1i[i])}}F 7U},8Z:G(5g,1i){if(5g===O){5g={}}R(u i=1;i<M.K;i++){u o=M[i];if(H(o)!="L"&&o!==O){R(u k in o){u v=o[k];if(H(5g[k])=="3n"&&H(v)=="3n"){M.2U(5g[k],v)}N{5g[k]=v}}}}F 5g},lO:G(6c,1i){if(6c===O){6c={}}R(u i=1;i<M.K;i++){u o=M[i];R(u k in o){if(!(k in 6c)){6c[k]=o[k]}}}F 6c},lN:G(1i){u fj=[];R(u mv in 1i){fj.1c(mv)}F fj},lM:G(1i){u fh=[];u e;R(u fi in 1i){u v;1f{v=1i[fi]}1e(e){2V}fh.1c([fi,v])}F fh},jq:G(fg,ff,fe){fe.1U=Y B.J.5a(fg.1r+"."+ff);fg[ff]=fe},4i:{7L:G(a){F!!a},vP:G(a){F!a},eE:G(a){F a},2E:G(a){F~a},vO:G(a){F-a},vN:G(a,b){F a+b},vM:G(a,b){F a-b},4u:G(a,b){F a/b},vL:G(a,b){F a%b},vK:G(a,b){F a*b},3W:G(a,b){F a&b},or:G(a,b){F a|b},vJ:G(a,b){F a^b},vI:G(a,b){F a<<b},vH:G(a,b){F a>>b},vG:G(a,b){F a>>>b},eq:G(a,b){F a==b},ne:G(a,b){F a!=b},gt:G(a,b){F a>b},ge:G(a,b){F a>=b},lt:G(a,b){F a<b},le:G(a,b){F a<=b},vF:G(a,b){F B.J.2f(a,b)===0},vE:G(a,b){F B.J.2f(a,b)!==0},vD:G(a,b){F B.J.2f(a,b)==1},vC:G(a,b){F B.J.2f(a,b)!=-1},vB:G(a,b){F B.J.2f(a,b)==-1},vA:G(a,b){F B.J.2f(a,b)!=1},vz:G(a,b){F a&&b},vy:G(a,b){F a||b},vx:G(a,b){F b in a}},24:G(mu){F G(){F D[mu].1w(D,M)}},lL:G(mt){F G(a9){F a9[mt]}},66:G(){u fd={};R(u i=0;i<M.K;i++){u 6b=M[i];fd[6b]=6b}F G(){R(u i=0;i<M.K;i++){if(!(H(M[i])in fd)){F 1m}}F 1h}},lJ:G(){R(u i=0;i<M.K;i++){if(M[i]!==O){F 1m}}F 1h},lK:G(){R(u i=0;i<M.K;i++){u o=M[i];if(!(H(o)=="L"||o===O)){F 1m}}F 1h},lI:G(1i){F!B.J.7e.1w(D,M)},7e:G(1i){R(u i=0;i<M.K;i++){u o=M[i];if(!(o&&o.K)){F 1m}}F 1h},3A:G(){R(u i=0;i<M.K;i++){u o=M[i];u 6b=H(o);if((6b!="3n"&&!(6b=="G"&&H(o.vw)=="G"))||o===O||H(o.K)!="2y"){F 1m}}F 1h},eN:G(){R(u i=0;i<M.K;i++){u o=M[i];if(H(o)!="3n"||o===O||H(o.9P)!="G"){F 1m}}F 1h},lH:G(fn){if(fn===O){F B.J.1R(O,M,1)}u fc=[];R(u i=1;i<M.K;i++){fc.1c(fn(M[i]))}F fc},2r:G(fn,1g){u m=B.J;u 6a=B.15;u fb=m.3A;if(M.K<=2){if(!fb(1g)){if(6a){1g=6a.2G(1g);if(fn===O){F 1g}}N{14 Y 3p("au 2E an at-as 3W B.15 2E ar")}}if(fn===O){F m.1R(O,1g)}u 69=[];R(u i=0;i<1g.K;i++){69.1c(fn(1g[i]))}F 69}N{if(fn===O){fn=7o}u 7T=O;R(i=1;i<M.K;i++){if(!fb(M[i])){if(6a){F 6a.2G(6a.4c.1w(O,M))}N{14 Y 3p("au 2E an at-as 3W B.15 2E ar")}}u l=M[i].K;if(7T===O||7T>l){7T=l}}69=[];R(i=0;i<7T;i++){u fa=[];R(u j=1;j<M.K;j++){fa.1c(M[j][i])}69.1c(fn.1w(D,fa))}F 69}},lG:G(fn){u f9=[];if(fn===O){fn=B.J.4i.7L}R(u i=1;i<M.K;i++){u o=M[i];if(fn(o)){f9.1c(o)}}F f9},47:G(fn,1g,7S){u aq=[];u m=B.J;if(!m.3A(1g)){if(B.15){1g=B.15.2G(1g)}N{14 Y 3p("au 2E an at-as 3W B.15 2E ar")}}if(fn===O){fn=m.4i.7L}if(H(7o.1U.47)=="G"){F 7o.1U.47.cz(1g,fn,7S)}N{if(H(7S)=="L"||7S===O){R(u i=0;i<1g.K;i++){u o=1g[i];if(fn(o)){aq.1c(o)}}}N{R(i=0;i<1g.K;i++){o=1g[i];if(fn.cz(7S,o)){aq.1c(o)}}}}F aq},mq:G(7R){F G(){hd(M.K){3j 0:F 7R();3j 1:F 7R(M[0]);3j 2:F 7R(M[0],M[1]);3j 3:F 7R(M[0],M[1],M[2])}u f8=[];R(u i=0;i<M.K;i++){f8.1c("M["+i+"]")}F dB("(1A("+f8.2b(",")+"))")}},lv:G(mr,ms){u m=B.J;F m.1O.1w(D,m.1R([ms,mr],M,2))},1O:G(3c,4o){if(H(3c)=="1n"){3c=4o[3c]}u ao=3c.f5;u 5f=3c.am;u f6=3c.f7;u m=B.J;if(H(3c)=="G"&&H(3c.1w)=="L"){3c=m.mq(3c)}if(H(ao)!="G"){ao=3c}if(H(4o)!="L"){f6=4o}if(H(5f)=="L"){5f=[]}N{5f=5f.9T()}m.1R(5f,M,2);u 7Q=G(){u ap=M;u me=M.2U;if(me.am.K>0){ap=m.2o(me.am,ap)}u 4o=me.f7;if(!4o){4o=D}F me.f5.1w(4o,ap)};7Q.f7=f6;7Q.f5=ao;7Q.am=5f;F 7Q},lF:G(7P){u mp=B.J.1O;R(u k in 7P){u f4=7P[k];if(H(f4)=="G"){7P[k]=mp(f4,7P)}}},5u:G(mo,mn,ml,mk){B.J.ae.5M(mo,mn,ml,mk)},mj:{"5L":1h,"1n":1h,"2y":1h},2f:G(a,b){if(a==b){F 0}u f3=(H(a)=="L"||a===O);u f2=(H(b)=="L"||b===O);if(f3&&f2){F 0}N{if(f3){F-1}N{if(f2){F 1}}}u m=B.J;u f1=m.mj;if(!(H(a)in f1&&H(b)in f1)){1f{F m.ae.3C(a,b)}1e(e){if(e!=m.4d){14 e}}}if(a<b){F-1}N{if(a>b){F 1}}u f0=m.U;14 Y 3p(f0(a)+" 3W "+f0(b)+" 9v 2E be vv")},eM:G(a,b){F B.J.2f(a.9P(),b.9P())},eL:G(a,b){u mi=B.J.2f;u 7O=a.K;u al=0;if(7O>b.K){al=1;7O=b.K}N{if(7O<b.K){al=-1}}R(u i=0;i<7O;i++){u 4j=mi(a[i],b[i]);if(4j){F 4j}}F al},7M:G(mh,mg,mf,md){B.J.ad.5M(mh,mg,mf,md)},U:G(o){if(H(o)=="L"){F"L"}N{if(o===O){F"O"}}1f{if(H(o.1K)=="G"){F o.1K()}N{if(H(o.U)=="G"&&o.U!=M.2U){F o.U()}}F B.J.ad.3C(o)}1e(e){if(H(o.1r)=="1n"&&(o.1l==cZ.1U.1l||o.1l==vu.1U.1l)){F o.1r}}1f{u eZ=(o+"")}1e(e){F"["+H(o)+"]"}if(H(o)=="G"){o=eZ.23(/^\\s+/,"");u 5n=o.2A("{");if(5n!=-1){o=o.3H(0,5n)+"{...}"}}F eZ},eK:G(o){u m=B.J;F"["+m.2r(m.U,o).2b(", ")+"]"},ac:G(o){F("\\""+o.23(/(["\\\\])/g,"\\\\$1")+"\\"").23(/[\\f]/g,"\\\\f").23(/[\\b]/g,"\\\\b").23(/[\\n]/g,"\\\\n").23(/[\\t]/g,"\\\\t").23(/[\\r]/g,"\\\\r")},eJ:G(o){F o+""},ly:G(mc,mb,ma,m9){B.J.ab.5M(mc,mb,ma,m9)},lx:G(){F dB("("+M[0]+")")},lz:G(o){u 5e=H(o);if(5e=="L"){F"L"}N{if(5e=="2y"||5e=="5L"){F o+""}N{if(o===O){F"O"}}}u m=B.J;u eY=m.ac;if(5e=="1n"){F eY(o)}u me=M.2U;u 3S;if(H(o.m8)=="G"){3S=o.m8();if(o!==3S){F me(3S)}}if(H(o.m7)=="G"){3S=o.m7();if(o!==3S){F me(3S)}}if(5e!="G"&&H(o.K)=="2y"){u X=[];R(u i=0;i<o.K;i++){u 2i=me(o[i]);if(H(2i)!="1n"){2i="L"}X.1c(2i)}F"["+X.2b(", ")+"]"}1f{3S=m.ab.3C(o);F me(3S)}1e(e){if(e!=m.4d){14 e}}if(5e=="G"){F O}X=[];R(u k in o){u ak;if(H(k)=="2y"){ak="\\""+k+"\\""}N{if(H(k)=="1n"){ak=eY(k)}N{2V}}2i=me(o[k]);if(H(2i)!="1n"){2V}X.1c(ak+":"+2i)}F"{"+X.2b(", ")+"}"},lE:G(a,b){F(B.J.2f(a,b)===0)},lD:G(eX,4n){if(eX.K!=4n.K){F 1m}F(B.J.2f(eX,4n)===0)},2o:G(){u eW=[];u m6=B.J.1R;R(u i=0;i<M.K;i++){m6(eW,M[i])}F eW},eR:G(2h){u m=B.J;u eU=m.2f;if(M.K==1){F G(a,b){F eU(a[2h],b[2h])}}u eV=m.1R(O,M);F G(a,b){u aj=0;R(u i=0;(aj===0)&&(i<eV.K);i++){u 2h=eV[i];aj=eU(a[2h],b[2h])}F aj}},lC:G(2h){u m5=B.J.eR.1w(D,M);F G(a,b){F m5(b,a)}},2z:G(m4){u m=B.J;F m.1O.1w(D,m.1R([m4,L],M,1))},67:G(m0,1g){if(1g.K===0){F O}u ai=1g[0];u m3=B.J.2f;R(u i=1;i<1g.K;i++){u o=1g[i];if(m3(o,ai)==m0){ai=o}}F ai},lB:G(){F B.J.67(1,M)},lA:G(){F B.J.67(-1,M)},bi:G(1g,lY,lZ,3B){if(H(3B)=="L"||3B===O){3B=1g.K}R(u i=(lZ||0);i<3B;i++){if(1g[i]===lY){F i}}F-1},eO:G(1g,lW,lX,3B){if(H(3B)=="L"||3B===O){3B=1g.K}u 4j=B.J.2f;R(u i=(lX||0);i<3B;i++){if(4j(1g[i],lW)===0){F i}}F-1},d4:G(1j,lV){u ah=[1j];u lU=B.J.1R;1M(ah.K){u X=lV(ah.2P());if(X){lU(ah,X)}}},3f:G(ag){u 2w=ag.1r;if(H(2w)=="L"){2w=""}N{2w=2w+"."}R(u 1b in ag){u o=ag[1b];if(H(o)=="G"&&H(o.1r)=="L"){1f{o.1r=2w+1b}1e(e){}}}},dw:G(3s,68){if(H(B.S)!="L"&&M.K==1&&(H(3s)=="1n"||(H(3s.3T)!="L"&&3s.3T>0))){u kv=B.S.d5(3s);3s=kv[0];68=kv[1]}N{if(M.K==1){u o=3s;3s=[];68=[];R(u k in o){u v=o[k];if(H(v)!="G"){3s.1c(k);68.1c(v)}}}}u W=[];u lT=28.2a(3s.K,68.K);u eT=B.J.af;R(u i=0;i<lT;i++){v=68[i];if(H(v)!="L"&&v!==O){W.1c(eT(3s[i])+"="+eT(v))}}F W.2b("&")},lw:G(lS,lQ){u 7N=lS.23(/\\+/g,"%20").2R("&");u o={};u 5d;if(H(lR)!="L"){5d=lR}N{5d=vt}if(lQ){R(u i=0;i<7N.K;i++){u 2n=7N[i].2R("=");u 1b=5d(2n[0]);u 4n=o[1b];if(!(4n 2C 7o)){4n=[];o[1b]=4n}4n.1c(5d(2n[1]))}}N{R(i=0;i<7N.K;i++){2n=7N[i].2R("=");o[5d(2n[0])]=5d(2n[1])}}F o}});B.J.4a=G(){D.4m=[]};B.J.4a.1U={5M:G(1b,eS,3y,lP){if(lP){D.4m.e9([1b,eS,3y])}N{D.4m.1c([1b,eS,3y])}},3C:G(){R(u i=0;i<D.4m.K;i++){u 2n=D.4m[i];if(2n[1].1w(D,M)){F 2n[2].1w(D,M)}}14 B.J.4d},vs:G(1b){R(u i=0;i<D.4m.K;i++){u 2n=D.4m[i];if(2n[0]==1b){D.4m.4y(i,1);F 1h}}F 1m}};B.J.1z=["4f","4L","1R","2l","8Z","lO","lN","lM","5a","4i","24","lL","66","lo","ln","lK","lJ","lI","7e","3A","eN","lH","2r","lG","47","1O","lF","4d","4a","5u","2f","7M","U","lE","lD","2o","eR","lC","2z","lm","67","lp","eI","lB","lA","d4","ll","af","dw","lz","ly","lx","lw","eO","bi","bg","lv"];B.J.1W=["3f","ae","ad","ab","eM","eL","eK","ac","eJ"];B.J.2Y=G(lu,eP){if(H(B.eQ)=="L"){B.eQ=(B.3d||(H(1x)=="L"&&H(1q)=="L"))}if(!B.eQ){F}u 1p=eP.2k[":1p"];R(u i=0;i<1p.K;i++){lu[1p[i]]=eP[1p[i]]}};B.J.2d=G(){u m=D;m.vr=m.24;m.vq=m.eO;if(H(ls)!="L"){m.af=G(lr){F ls(lr).23(/\\\'/g,"%27")}}N{m.af=G(lq){F vp(lq).23(/\\+/g,"%2B").23(/\\"/g,"%22").W.23(/\\\'/g,"%27")}}m.5a=G(1b){D.43=1b;D.1b=1b};m.5a.1U=Y 2x();m.2l(m.5a.1U,{U:G(){if(D.43&&D.43!=D.1b){F D.1b+"("+m.U(D.43)+")"}N{F D.1b+"()"}},1l:m.24("U")});m.4d=Y m.5a("B.J.4d");m.lp=m.2z(m.67,1);m.eI=m.2z(m.67,-1);m.lo=m.66("G");m.ln=m.66("L");m.lm=m.2z(m.2l,O);m.ll=m.2z(m.2r,O);m.ae=Y m.4a();m.5u("vo",m.eN,m.eM);m.5u("ej",m.3A,m.eL);m.ad=Y m.4a();m.7M("ej",m.3A,m.eK);m.7M("1n",m.66("1n"),m.ac);m.7M("vn",m.66("2y","5L"),m.eJ);m.ab=Y m.4a();u 1p=m.2o(m.1z,m.1W);m.2k={":3e":m.2o(m.1W),":1p":1p};m.3f(D)};B.J.2d();if(!B.3d){2f=B.J.2f}B.J.2Y(D,B.J);if(H(1q)!="L"){1q.2X("B.15");1q.2M("B.J")}if(H(1x)!="L"){1x.26("B.J",[])}1f{if(H(B.J)=="L"){14""}}1e(e){14"B.15 3F on B.J!"}if(H(B.15)=="L"){B.15={}}B.15.1r="B.15";B.15.1Y="1.3.1";B.J.2l(B.15,{1K:G(){F"["+D.1r+" "+D.1Y+"]"},1l:G(){F D.1K()},9W:G(1b,lk,lj,lh){B.15.9Y.5M(1b,lk,lj,lh)},1Q:G(3R,lg){u I=B.15;if(M.K==2){F I.9Z(G(a){F a!=lg},3R)}if(H(3R.1a)=="G"){F 3R}N{if(H(3R.1Q)=="G"){F 3R.1Q()}}1f{F I.9Y.3C(3R)}1e(e){u m=B.J;if(e==m.4d){e=Y 3p(H(3R)+": "+m.U(3R)+" is 2E vm")}14 e}},eu:G(n){if(!n){n=0}u m=B.J;F{U:G(){F"eu("+n+")"},1l:m.24("U"),1a:m.4f(n)}},et:G(p){u I=B.15;u m=B.J;u 1g=[];u lf=I.1Q(p);F{U:G(){F"et(...)"},1l:m.24("U"),1a:G(){1f{u W=lf.1a();1g.1c(W);F W}1e(e){if(e!=I.25){14 e}if(1g.K===0){D.1a=G(){14 I.25}}N{u i=-1;D.1a=G(){i=(i+1)%1g.K;F 1g[i]}}F D.1a()}}}},7b:G(Q,n){u m=B.J;if(H(n)=="L"){F{U:G(){F"7b("+m.U(Q)+")"},1l:m.24("U"),1a:G(){F Q}}}F{U:G(){F"7b("+m.U(Q)+", "+n+")"},1l:m.24("U"),1a:G(){if(n<=0){14 B.15.25}n-=1;F Q}}},1a:G(ld){F ld.1a()},es:G(p,q){u m=B.J;u 1a=B.15.1a;u lc=m.2r(1Q,M);F{U:G(){F"es(...)"},1l:m.24("U"),1a:G(){F m.2r(1a,lc)}}},a1:G(3b,1V){u m=B.J;1V=B.15.1Q(1V);if(3b===O){3b=m.4i.7L}F{U:G(){F"a1(...)"},1l:m.24("U"),1a:G(){1M(1h){u W=1V.1a();if(3b(W)){F W}}F L}}},a0:G(3b,1V){u m=B.J;1V=B.15.1Q(1V);if(3b===O){3b=m.4i.7L}F{U:G(){F"a0(...)"},1l:m.24("U"),1a:G(){1M(1h){u W=1V.1a();if(!3b(W)){F W}}F L}}},er:G(1V){u I=B.15;u m=B.J;1V=I.1Q(1V);u 5c=0;u 2J=0;u 3a=1;u i=-1;if(M.K==2){2J=M[1]}N{if(M.K==3){5c=M[1];2J=M[2]}N{5c=M[1];2J=M[2];3a=M[3]}}F{U:G(){F"er("+["...",5c,2J,3a].2b(", ")+")"},1l:m.24("U"),1a:G(){u W;1M(i<5c){W=1V.1a();i++}if(5c>=2J){14 I.25}5c+=3a;F W}}},4c:G(aa,p,q){u m=B.J;u I=B.15;u lb=m.2r(I.1Q,m.1R(O,M,1));u 2r=m.2r;u 1a=I.1a;F{U:G(){F"4c(...)"},1l:m.24("U"),1a:G(){F aa.1w(D,2r(1a,lb))}}},ep:G(aa,1V,I){1V=B.15.1Q(1V);u m=B.J;F{U:G(){F"ep(...)"},1l:m.24("U"),1a:G(){F aa.1w(I,1V.1a())}}},55:G(p,q){u I=B.15;u m=B.J;if(M.K==1){F I.1Q(M[0])}u 64=m.2r(I.1Q,M);F{U:G(){F"55(...)"},1l:m.24("U"),1a:G(){1M(64.K>1){1f{F 64[0].1a()}1e(e){if(e!=I.25){14 e}64.2P()}}if(64.K==1){u a9=64.2P();D.1a=m.1O("1a",a9);F D.1a()}14 I.25}}},9Z:G(3b,1V){u I=B.15;1V=I.1Q(1V);F{U:G(){F"9Z(...)"},1l:B.J.24("U"),1a:G(){u W=1V.1a();if(!3b(W)){D.1a=G(){14 I.25};D.1a()}F W}}},eo:G(3b,1V){1V=B.15.1Q(1V);u m=B.J;u 1O=m.1O;F{"U":G(){F"eo(...)"},"1l":m.24("U"),"1a":G(){1M(1h){u W=1V.1a();if(!3b(W)){2K}}D.1a=1O("1a",1V);F W}}},a7:G(63,2u,la){2u.62[63]=-1;u m=B.J;u l9=m.eI;F{U:G(){F"en("+63+", ...)"},1l:m.24("U"),1a:G(){u W;u i=2u.62[63];if(i==2u.29){W=la.1a();2u.a8.1c(W);2u.29+=1;2u.62[63]+=1}N{W=2u.a8[i-2u.2a];2u.62[63]+=1;if(i==2u.2a&&l9(2u.62)!=2u.2a){2u.2a+=1;2u.a8.2P()}}F W}}},en:G(a6,n){u W=[];u 2u={"62":[],"a8":[],"29":-1,"2a":-1};if(M.K==1){n=2}u I=B.15;a6=I.1Q(a6);u a7=I.a7;R(u i=0;i<n;i++){W.1c(a7(i,2u,a6))}F W},2G:G(4l){u m=B.J;if(H(4l.9T)=="G"){F 4l.9T()}N{if(m.3A(4l)){F m.2o(4l)}}u I=B.15;4l=I.1Q(4l);u W=[];1f{1M(1h){W.1c(4l.1a())}}1e(e){if(e!=I.25){14 e}F W}F L},7H:G(fn,7K,l8){u i=0;u x=l8;u I=B.15;7K=I.1Q(7K);if(M.K<3){1f{x=7K.1a()}1e(e){if(e==I.25){e=Y 3p("7H() of vl vk vj no vi 3m")}14 e}i++}1f{1M(1h){x=fn(x,7K.1a())}}1e(e){if(e!=I.25){14 e}}F x},7I:G(){u 4k=0;u 2J=0;u 3a=1;if(M.K==1){2J=M[0]}N{if(M.K==2){4k=M[0];2J=M[1]}N{if(M.K==3){4k=M[0];2J=M[1];3a=M[2]}N{14 Y 3p("7I() vh 1, 2, or 3 M!")}}}if(3a===0){14 Y 3p("7I() 3a 5p 2E be 0")}F{1a:G(){if((3a>0&&4k>=2J)||(3a<0&&4k<=2J)){14 B.15.25}u W=4k;4k+=3a;F W},U:G(){F"7I("+[4k,2J,3a].2b(", ")+")"},1l:B.J.24("U")}},l0:G(a5,l7){u x=l7||0;u I=B.15;a5=I.1Q(a5);1f{1M(1h){x+=a5.1a()}}1e(e){if(e!=I.25){14 e}}F x},em:G(a4){u I=B.15;a4=I.1Q(a4);1f{1M(1h){a4.1a()}}1e(e){if(e!=I.25){14 e}}},9a:G(7J,1A,I){u m=B.J;if(M.K>2){1A=m.1O(1A,I)}if(m.3A(7J)){1f{R(u i=0;i<7J.K;i++){1A(7J[i])}}1e(e){if(e!=B.15.25){14 e}}}N{I=B.15;I.em(I.4c(1A,7J))}},kZ:G(l6,1A){u I=B.15;1f{I.a0(1A,l6).1a();F 1m}1e(e){if(e!=I.25){14 e}F 1h}},kY:G(l5,4j){u W=B.15.2G(l5);if(M.K==1){4j=B.J.2f}W.iz(4j);F W},kX:G(l4){u W=B.15.2G(l4);W.vg();F W},kW:G(l3,1A){u I=B.15;1f{I.a1(1A,l3).1a();F 1h}1e(e){if(e!=I.25){14 e}F 1m}},kV:G(1g,5b){if(B.J.3A(5b)){R(u i=0;i<5b.K;i++){1g.1c(5b[i])}}N{u I=B.15;5b=I.1Q(5b);1f{1M(1h){1g.1c(5b.1a())}}1e(e){if(e!=I.25){14 e}}}F 1g},ek:G(a3,eH){u m=B.J;u I=B.15;if(M.K<2){eH=m.4i.eE}a3=I.1Q(a3);u pk=L;u k=L;u v;G eF(){v=a3.1a();k=eH(v)}G l2(){u 7j=v;v=L;F 7j}u eG=1h;F{U:G(){F"ek(...)"},1a:G(){1M(k==pk){eF();if(eG){eG=1m;2K}}pk=k;F[k,{1a:G(){if(v==L){eF()}if(k!=pk){14 I.25}F l2()}}]}}},kU:G(a2,eD){u m=B.J;u I=B.15;if(M.K<2){eD=m.4i.eE}a2=I.1Q(a2);u ey=[];u eA=1h;u ez;1M(1h){1f{u eB=a2.1a();u 2h=eD(eB)}1e(e){if(e==I.25){2K}14 e}if(eA||2h!=ez){u eC=[];ey.1c([2h,eC])}eC.1c(eB);eA=1m;ez=2h}F ey},9X:G(ex){u i=0;F{U:G(){F"9X(...)"},1l:B.J.24("U"),1a:G(){if(i>=ex.K){14 B.15.25}F ex[i++]}}},eh:G(ew){F(ew&&H(ew.ei)=="G")},9V:G(l1){F{U:G(){F"9V(...)"},1l:B.J.24("U"),1a:G(){u W=l1.ei();if(W===O||W===L){14 B.15.25}F W}}}});B.15.1W=["9Y","9X","eh","9V",];B.15.1z=["25","9W","1Q","eu","et","7b","1a","es","a1","a0","er","4c","ep","55","9Z","eo","en","2G","7H","7I","l0","em","9a","kZ","kY","kX","kW","kV","ek","kU"];B.15.2d=G(){u m=B.J;D.25=Y m.5a("25");D.9Y=Y m.4a();D.9W("ej",m.3A,D.9X);D.9W("ei",D.eh,D.9V);D.2k={":3e":D.1z,":1p":m.2o(D.1z,D.1W)};m.3f(D)};B.15.2d();if(!B.3d){7H=B.15.7H}B.J.2Y(D,B.15);if(H(1q)!="L"){1q.2X("B.1H");1q.2M("B.J")}if(H(1x)!="L"){1x.26("B.J",[])}1f{if(H(B.J)=="L"){14""}}1e(e){14"B.1H 3F on B.J!"}if(H(B.1H)=="L"){B.1H={}}B.1H.1r="B.1H";B.1H.1Y="1.3.1";B.1H.1K=G(){F"["+D.1r+" "+D.1Y+"]"};B.1H.1l=G(){F D.1K()};B.1H.1z=["5C","49","7A","kR","2L","5Z","kG","ch","kE","kC"];B.1H.1W=["ef","e8","e7"];B.1H.49=G(1P,kT,3z){D.1P=1P;D.3N=kT;D.3z=3z;D.vf=Y 3Q()};B.1H.49.1U={U:G(){u m=B.J;F"49("+m.2r(m.U,[D.1P,D.3N,D.3z]).2b(", ")+")"},1l:B.J.24("U")};B.J.2l(B.1H,{ef:G(7F){u I=B.1H;if(H(7F)=="1n"){7F=I.5C[7F]}F G(1t){u 7G=1t.3N;if(H(7G)=="1n"){7G=I.5C[7G]}F 7G>=7F}},e8:G(){u kS=B.1H.49;R(u i=0;i<M.K;i++){if(!(M[i]2C kS)){F 1m}}F 1h},e7:G(a,b){F B.J.2f([a.3N,a.3z],[b.3N,b.3z])},kR:G(1t){cq("1P: "+1t.1P+"\\ve: "+1t.3N+"\\vd: "+1t.3z.2b(" "))}});B.1H.7A=G(7E){D.4f=0;if(H(7E)=="L"||7E===O){7E=-1}D.ec=7E;D.4h=[];D.7C={};D.e5=1m};B.1H.7A.1U={vc:G(){D.4h.4y(0,D.4h.K)},kK:G(1t){if(H(2O)!="L"&&2O.eg&&2O.eg.5Z){2O.eg.5Z(1t)}N{if(H(7h)!="L"&&7h.kQ){7h.kQ(1t)}N{if(H(5X)=="G"){5X(1t)}}}},kL:G(1t){R(u k in D.7C){u 2n=D.7C[k];if(2n.kO!=k||(2n[0]&&!2n[0](1t))){2V}2n[1](1t)}},hE:G(ee,7D,kP){if(H(7D)=="1n"){7D=B.1H.ef(7D)}u ed=[7D,kP];ed.kO=ee;D.7C[ee]=ed},c9:G(kN){gi D.7C[kN]},kH:G(kM,vb){u 1t=Y B.1H.49(D.4f,kM,B.J.1R(O,M,1));D.4h.1c(1t);D.kL(1t);if(D.e5){D.kK(1t.3N+": "+1t.3z.2b(" "))}D.4f+=1;1M(D.ec>=0&&D.4h.K>D.ec){D.4h.2P()}},c8:G(9U){u ea=0;if(!(H(9U)=="L"||9U===O)){ea=28.29(0,D.4h.K-9U)}F D.4h.9T(ea)},kJ:G(7B){if(H(7B)=="L"||7B===O){7B=30}u 9S=D.c8(7B);if(9S.K){u 1g=2r(G(m){F"\\n ["+m.1P+"] "+m.3N+": "+m.3z.2b(" ")},9S);1g.e9("va "+9S.K+" v9:");F 1g.2b("")}F""},v8:G(kI){if(H(B.1I)=="L"){cq(D.kJ())}N{B.1I.bY(kI||1m)}}};B.1H.2d=G(){D.5C={8M:40,8L:50,8K:30,8J:20,8I:10};u m=B.J;m.5u("49",D.e8,D.e7);u 61=m.2z;u e6=D.7A;u 60=e6.1U.kH;m.2l(D.7A.1U,{kF:61(60,"8I"),5Z:61(60,"8J"),dE:61(60,"8M"),kD:61(60,"8L"),kB:61(60,"8K")});u I=D;u 5Y=G(1b){F G(){I.2L[1b].1w(I.2L,M)}};D.5Z=5Y("5Z");D.kG=5Y("dE");D.ch=5Y("kF");D.kE=5Y("kD");D.kC=5Y("kB");D.2L=Y e6();D.2L.e5=1h;D.2k={":3e":D.1z,":1p":m.2o(D.1z,D.1W)};m.3f(D)};if(H(5X)=="L"&&H(2v)!="L"&&2v.kA&&H(kz)!="L"){5X=G(){5X.3G=M;u ev=2v.kA("v7");ev.v6("5X",1m,1h);kz(ev)}}B.1H.2d();B.J.2Y(D,B.1H);if(H(1q)!="L"){1q.2X("B.1D")}if(H(B)=="L"){B={}}if(H(B.1D)=="L"){B.1D={}}B.1D.1r="B.1D";B.1D.1Y="1.3.1";B.1D.1K=G(){F"["+D.1r+" "+D.1Y+"]"};B.1D.1l=G(){F D.1K()};B.1D.ks=G(1y){1y=1y+"";if(H(1y)!="1n"||1y.K===0){F O}u 7z=1y.2R("-");if(7z.K===0){F O}F Y 3Q(7z[0],7z[1]-1,7z[2])};B.1D.ky=/(\\d{4,})(?:-(\\d{1,2})(?:-(\\d{1,2})(?:[T ](\\d{1,2}):(\\d{1,2})(?::(\\d{1,2})(?:\\.(\\d+))?)?(?:(Z)|([+-])(\\d{1,2})(?::(\\d{1,2}))?)?)?)?)?/;B.1D.kr=G(1y){1y=1y+"";if(H(1y)!="1n"||1y.K===0){F O}u X=1y.3C(B.1D.ky);if(H(X)=="L"||X===O){F O}u 5W,7y,7x,9R,2a,9Q,7w;5W=3w(X[1],10);if(H(X[2])=="L"||X[2]===""){F Y 3Q(5W)}7y=3w(X[2],10)-1;7x=3w(X[3],10);if(H(X[4])=="L"||X[4]===""){F Y 3Q(5W,7y,7x)}9R=3w(X[4],10);2a=3w(X[5],10);9Q=(H(X[6])!="L"&&X[6]!=="")?3w(X[6],10):0;if(H(X[7])!="L"&&X[7]!==""){7w=28.ha(c5*4M("0."+X[7]))}N{7w=0}if((H(X[8])=="L"||X[8]==="")&&(H(X[9])=="L"||X[9]==="")){F Y 3Q(5W,7y,7x,9R,2a,9Q,7w)}u 58;if(H(X[9])!="L"&&X[9]!==""){58=3w(X[10],10)*v5;if(H(X[11])!="L"&&X[11]!==""){58+=3w(X[11],10)*kw}if(X[9]=="-"){58=-58}}N{58=0}F Y 3Q(3Q.v4(5W,7y,7x,9R,2a,9Q,7w)-58)};B.1D.dY=G(2g,kx){if(H(2g)=="L"||2g===O){F O}u hh=2g.v3();u mm=2g.v2();u ss=2g.v1();u 1g=[((kx&&(hh<10))?"0"+hh:hh),((mm<10)?"0"+mm:mm),((ss<10)?"0"+ss:ss)];F 1g.2b(":")};B.1D.kq=G(2g,7v){if(H(2g)=="L"||2g===O){F O}u ku=7v?"T":" ";u kt=7v?"Z":"";if(7v){2g=Y 3Q(2g.9P()+(2g.v0()*kw))}F B.1D.dX(2g)+ku+B.1D.dY(2g,7v)+kt};B.1D.dX=G(2g){if(H(2g)=="L"||2g===O){F O}u e4=B.1D.e3;F[2g.dZ(),e4(2g.e1()+1),e4(2g.e0())].2b("-")};B.1D.kp=G(d){d=d+"";if(H(d)!="1n"||d.K===0){F O}u a=d.2R("/");F Y 3Q(a[2],a[0]-1,a[1])};B.1D.e3=G(n){F(n>9)?n:"0"+n};B.1D.ko=G(d){if(H(d)=="L"||d===O){F O}u e2=B.1D.e3;F[e2(d.e1()+1),e2(d.e0()),d.dZ()].2b("/")};B.1D.kn=G(d){if(H(d)=="L"||d===O){F O}F[d.e1()+1,d.e0(),d.dZ()].2b("/")};B.1D.1z=["ks","kr","dY","kq","dX","kp","ko","kn"];B.1D.1W=[];B.1D.2k={":3e":B.1D.1z,":1p":B.1D.1z};B.1D.2d=G(){u 2w=D.1r+".";R(u k in D){u o=D[k];if(H(o)=="G"&&H(o.1r)=="L"){1f{o.1r=2w+k}1e(e){}}}};B.1D.2d();if(H(B.J)!="L"){B.J.2Y(D,B.1D)}N{(G(km,dW){if((H(1x)=="L"&&H(1q)=="L")||(H(B.3d)=="5L"&&B.3d)){u 1p=dW.2k[":1p"];R(u i=0;i<1p.K;i++){km[1p[i]]=dW[1p[i]]}}})(D,B.1D)}if(H(1q)!="L"){1q.2X("B.1s")}if(H(B)=="L"){B={}}if(H(B.1s)=="L"){B.1s={}}B.1s.1r="B.1s";B.1s.1Y="1.3.1";B.1s.1K=G(){F"["+D.1r+" "+D.1Y+"]"};B.1s.1l=G(){F D.1K()};B.1s.ke=G(kl,kk,kj,ki,kh,dV,kg,9N,kf){F G(1P){1P=4M(1P);if(H(1P)=="L"||1P===O||k8(1P)){F kl}u 9L=kk;u 9K=kj;if(1P<0){1P=-1P}N{9L=9L.23(/-/,"")}u me=M.2U;u 9M=B.1s.dJ(ki);if(kh){1P=1P*3k;9K=9M.9y+9K}1P=B.1s.dK(1P,dV);u 9O=1P.2R(/\\./);u 3r=9O[0];u 3P=(9O.K==1)?"":9O[1];u X="";1M(3r.K<kg){3r="0"+3r}if(9N){1M(3r.K>9N){u i=3r.K-9N;X=9M.9A+3r.2W(i,3r.K)+X;3r=3r.2W(0,i)}}X=3r+X;if(dV>0){1M(3P.K<kf){3P=3P+"0"}X=X+9M.9z+3P}F 9L+X+9K}};B.1s.k5=G(9J,9H,9G){if(H(9H)=="L"){9H=""}u 3q=9J.3C(/((?:[0#]+,)?[0#]+)(?:\\.([0#]+))?(%)?/);if(!3q){14 3p("uZ uY")}u 7u=9J.3H(0,3q.c6);u kd=9J.3H(3q.c6+3q[0].K);if(7u.uX(/-/)==-1){7u=7u+"-"}u 9I=3q[1];u 3P=(H(3q[2])=="1n"&&3q[2]!="")?3q[2]:"";u kc=(H(3q[3])=="1n"&&3q[3]!="");u dU=9I.2R(/,/);u 9F;if(H(9G)=="L"){9G="dG"}if(dU.K==1){9F=O}N{9F=dU[1].K}u ka=9I.K-9I.23(/0/g,"").K;u k9=3P.K-3P.23(/0/g,"").K;u kb=3P.K;u W=B.1s.ke(9H,7u,kd,9G,kc,kb,ka,9F,k9);u m=B.J;if(m){u fn=M.2U;u 3G=m.2o(M);W.U=G(){F[I.1r,"(",2r(m.U,3G).2b(", "),")"].2b("")}}F W};B.1s.dJ=G(4g){if(H(4g)=="L"||4g===O){4g="dG"}if(H(4g)=="1n"){u W=B.1s.5V[4g];if(H(W)=="1n"){W=M.2U(W);B.1s.5V[4g]=W}F W}N{F 4g}};B.1s.k4=G(dT,9E){if(9E){u X=dT/9E;if(!k8(X)){F B.1s.9B(dT/9E)}}F"0"};B.1s.9B=G(dS){u dR=(dS<0?"-":"");u s=28.8B(28.uW(dS)*3k).1l();if(s=="0"){F s}if(s.K<3){1M(s.3Z(s.K-1)=="0"){s=s.2W(0,s.K-1)}F dR+"0."+s}u 5E=dR+s.2W(0,s.K-2);u 7t=s.2W(s.K-2,s.K);if(7t=="uV"){F 5E}N{if(7t.3Z(1)=="0"){F 5E+"."+7t.3Z(0)}N{F 5E+"."+7t}}};B.1s.dI=G(1y,dQ){1y=1y+"";if(H(1y)!="1n"){F O}if(!dQ){F 1y.23(/^\\s+/,"")}N{F 1y.23(Y 8V("^["+dQ+"]+"),"")}};B.1s.dH=G(1y,dP){1y=1y+"";if(H(1y)!="1n"){F O}if(!dP){F 1y.23(/\\s+$/,"")}N{F 1y.23(Y 8V("["+dP+"]+$"),"")}};B.1s.k2=G(1y,dO){u I=B.1s;F I.dH(I.dI(1y,dO),dO)};B.1s.dL=G(9D,9C){9D=28.8B(9D*28.dN(10,9C));u X=(9D*28.dN(10,-9C)).6I(9C);if(X.3Z(0)=="."){X="0"+X}F X};B.1s.dK=G(k7,dM){F B.1s.dL(k7+0.5*28.dN(10,-dM),dM)};B.1s.k3=G(k6){F B.1s.9B(3k*k6)+"%"};B.1s.1z=["dL","dK","k5","dJ","k4","9B","k3","dI","dH","k2"];B.1s.5V={k1:{9A:",",9z:".",9y:"%"},uU:{9A:".",9z:",",9y:"%"},uT:{9A:" ",9z:",",9y:"%"},"dG":"k1"};B.1s.1W=[];B.1s.2k={":1p":B.1s.1z,":3e":B.1s.1z};B.1s.2d=G(){u 2w=D.1r+".";u k,v,o;R(k in D.5V){o=D.5V[k];if(H(o)=="3n"){o.U=G(){F D.1r};o.1r=2w+"5V."+k}}R(k in D){o=D[k];if(H(o)=="G"&&H(o.1r)=="L"){1f{o.1r=2w+k}1e(e){}}}};B.1s.2d();if(H(B.J)!="L"){B.J.2Y(D,B.1s)}N{(G(k0,dF){if((H(1x)=="L"&&H(1q)=="L")||(H(B.3d)=="5L"&&B.3d)){u 1p=dF.2k[":1p"];R(u i=0;i<1p.K;i++){k0[1p[i]]=dF[1p[i]]}}})(D,B.1s)}if(H(1q)!="L"){1q.2X("B.1k");1q.2M("B.J")}if(H(1x)!="L"){1x.26("B.J",[])}1f{if(H(B.J)=="L"){14""}}1e(e){14"B.1k 3F on B.J!"}if(H(B.1k)=="L"){B.1k={}}B.1k.1r="B.1k";B.1k.1Y="1.3.1";B.1k.1K=G(){F"["+D.1r+" "+D.1Y+"]"};B.1k.1l=G(){F D.1K()};B.1k.2t=G(jZ){D.55=[];D.id=D.7n();D.2H=-1;D.54=0;D.53=[O,O];D.7m=jZ;D.7l=1m;D.7r=1m};B.1k.2t.1U={U:G(){u 7s;if(D.2H==-1){7s="uS"}N{if(D.2H===0){7s="uR"}N{7s="dE"}}F"2t("+D.id+", "+7s+")"},1l:B.J.24("U"),7n:B.J.4f(),jY:G(){u I=B.1k;if(D.2H==-1){if(D.7m){D.7m(D)}N{D.7l=1h}if(D.2H==-1){D.52(Y I.di(D))}}N{if((D.2H===0)&&(D.53[0]2C I.2t)){D.53[0].jY()}}},jQ:G(){D.54++},jX:G(){D.54--;if((D.54===0)&&(D.2H>=0)){D.9u()}},jR:G(X){D.9x(X);D.jX()},9x:G(X){D.2H=((X 2C 2x)?1:0);D.53[D.2H]=X;D.9u()},dD:G(){if(D.2H!=-1){if(!D.7l){14 Y B.1k.dj(D)}D.7l=1m;F}},3o:G(X){D.dD();if(X 2C B.1k.2t){14 Y 2x("2t jW 9v aB be 7r if jV jU jT jS of a 3o")}D.9x(X)},52:G(X){D.dD();u I=B.1k;if(X 2C I.2t){14 Y 2x("2t jW 9v aB be 7r if jV jU jT jS of a 3o")}if(!(X 2C 2x)){X=Y I.9p(X)}D.9x(X)},jP:G(fn){if(M.K>1){fn=B.J.2z.1w(O,M)}F D.9w(fn,fn)},5Q:G(fn){if(M.K>1){fn=B.J.2z.1w(O,M)}F D.9w(fn,O)},jA:G(fn){if(M.K>1){fn=B.J.2z.1w(O,M)}F D.9w(O,fn)},9w:G(cb,eb){if(D.7r){14 Y 2x("uQ uP 9v 2E be re-uO")}D.55.1c([cb,eb]);if(D.2H>=0){D.9u()}F D},9u:G(){u dC=D.55;u 56=D.2H;u X=D.53[56];u I=D;u cb=O;1M(dC.K>0&&D.54===0){u 2n=dC.2P();u f=2n[56];if(f===O){2V}1f{X=f(X);56=((X 2C 2x)?1:0);if(X 2C B.1k.2t){cb=G(X){I.jR(X)};D.jQ()}}1e(3O){56=1;if(!(3O 2C 2x)){3O=Y B.1k.9p(3O)}X=3O}}D.2H=56;D.53[56]=X;if(cb&&D.54){X.jP(cb);X.7r=1h}}};B.J.2l(B.1k,{dk:G(){F dB("("+M[0].jN+")")},dp:G(uN){u d=Y B.1k.2t();d.3o.1w(d,M);F d},9q:G(uM){u d=Y B.1k.2t();d.52.1w(d,M);F d},do:G(){u I=M.2U;if(!I.7q){u dy=[G(){F Y 7q()},G(){F Y dA("jO.dz")},G(){F Y dA("uL.dz")},G(){F Y dA("jO.dz.4.0")},G(){14 Y B.1k.dh("uK uJ 2E uI 7q")}];R(u i=0;i<dy.K;i++){u 1A=dy[i];1f{I.7q=1A;F 1A()}1e(e){}}}F I.7q()},dx:G(){},jK:G(d){if(D.uH==4){1f{D.5T=O}1e(e){1f{D.5T=B.1k.dx}1e(e){}}u 5U=O;1f{5U=D.jm;if(!5U&&B.J.7e(D.jN)){5U=jM}}1e(e){}if(5U==hQ||5U==jM){d.3o(D)}N{u 3O=Y B.1k.dg(D,"uG uF");if(3O.2y){d.52(3O)}N{d.52(3O)}}}},jL:G(2s){1f{2s.5T=O}1e(e){1f{2s.5T=B.1k.dx}1e(e){}}2s.uE()},dl:G(2s,7p){if(H(7p)=="L"||7p===O){7p=""}u m=B.J;u I=B.1k;u d=Y I.2t(m.2z(I.jL,2s));1f{2s.5T=m.1O(I.jK,2s,d);2s.uD(7p)}1e(e){1f{2s.5T=O}1e(uC){}d.52(e)}F d},dn:G(5F){u I=B.1k;u 2s=I.do();if(M.K>1){u m=B.J;u qs=m.dw.1w(O,m.1R(O,M,1));if(qs){5F+="?"+qs}}2s.cp("uB",5F,1h);F I.dl(2s)},jv:G(5F){u I=B.1k;u d=I.dn.1w(I,M);d=d.5Q(I.dk);F d},dm:G(jJ,dv){u d=Y B.1k.2t();u m=B.J;if(H(dv)!="L"){d.5Q(G(){F dv})}u jI=uA(m.1O("3o",d),28.8B(jJ*c5));d.7m=G(){1f{uz(jI)}1e(e){}};F d},ju:G(jH,1A){u m=B.J;u jG=m.2z.1w(m,m.1R(O,M,1));F B.1k.dm(jH).5Q(G(X){F jG()})}});B.1k.5O=G(){D.5S=[];D.4e=1m;D.id=D.7n()};B.1k.5O.1U={bX:B.1k.5O,uy:G(){d=Y B.1k.2t();if(D.4e){D.5S.1c(d)}N{D.4e=1h;d.3o(D)}F d},jF:G(){if(!D.4e){14 3p("ux to jF an jE 5O")}D.4e=1m;if(D.5S.K>0){D.4e=1h;D.5S.2P().3o(D)}},7n:B.J.4f(),U:G(){u 9t;if(D.4e){9t="4e, "+D.5S.K+" 5S"}N{9t="jE"}F"5O("+D.id+", "+9t+")"},1l:B.J.24("U")};B.1k.7i=G(2G,du,jC,jB,jD){D.2G=2G;D.9r=Y 7o(D.2G.K);D.55=[];D.id=D.7n();D.2H=-1;D.54=0;D.53=[O,O];D.7m=jD;D.7l=1m;if(D.2G.K===0&&!du){D.3o(D.9r)}D.dr=0;D.jz=du;D.jy=jC;D.jx=jB;u 9s=0;B.J.2r(B.J.1O(G(d){d.5Q(B.J.1O(D.dt,D),9s,1h);d.jA(B.J.1O(D.dt,D),9s,1m);9s+=1},D),D.2G)};B.J.2l(B.1k.7i.1U,B.1k.2t.1U);B.J.2l(B.1k.7i.1U,{dt:G(ds,7k,5R){D.9r[ds]=[7k,5R];D.dr+=1;if(D.2H!==0){if(7k&&D.jz){D.3o([ds,5R])}N{if(!7k&&D.jy){D.52(5R)}N{if(D.dr==D.2G.K){D.3o(D.9r)}}}}if(!7k&&D.jx){5R=O}F 5R}});B.1k.jt=G(jw){u d=Y B.1k.7i(jw,1m,1h,1m);d.5Q(G(dq){u 7j=[];R(u i=0;i<dq.K;i++){7j.1c(dq[i][1])}F 7j});F d};B.1k.jr=G(1A){u I=B.1k;u 5P;1f{u r=1A.1w(O,B.J.1R([],M,1));if(r 2C I.2t){5P=r}N{if(r 2C 2x){5P=I.9q(r)}N{5P=I.dp(r)}}}1e(e){5P=I.9q(e)}F 5P};B.1k.1z=["dj","di","dh","9p","dg","2t","dp","9q","do","dn","jv","dm","ju","dl","5O","7i","jt","jr"];B.1k.1W=["dk"];B.1k.2d=G(){u m=B.J;u ne=m.2z(m.jq,D);ne("dj",G(jp){D.jo=jp});ne("di",G(jn){D.jo=jn});ne("dh",G(1t){D.43=1t});ne("9p",G(1t){D.43=1t});ne("dg",G(2s,1t){D.2s=2s;D.43=1t;1f{D.2y=2s.jm}1e(e){}});D.2k={":3e":D.1z,":1p":m.2o(D.1z,D.1W)};m.3f(D)};B.1k.2d();B.J.2Y(D,B.1k);if(H(1q)!="L"){1q.2X("B.S");1q.2M("B.15")}if(H(1x)!="L"){1x.26("B.15",[])}1f{if(H(B.15)=="L"){14""}}1e(e){14"B.S 3F on B.15!"}if(H(B.S)=="L"){B.S={}}B.S.1r="B.S";B.S.1Y="1.3.1";B.S.1K=G(){F"["+D.1r+" "+D.1Y+"]"};B.S.1l=G(){F D.1K()};B.S.1z=["d5","cr","b9","95","94","j3","9k","cX","cw","iT","iV","4X","9j","iQ","hS","cs","ia","i9","i8","i7","i6","i5","i4","hV","i3","i2","i1","cu","hW","ct","i0","hZ","hY","hX","P","io","il","ik","ij","cm","ih","ii","ig","ie","ic","cv","8d","A","6m","ib","1E","$","4q","aH","cO","cN","iM","5G","iK","9d","9e","iH","iD","9c","iB","cG","97","hU","hT","iw","jh","jb","j6","j5","jk","jl"];B.S.1W=["9b"];B.S.5N=G(w,h){D.w=w;D.h=h};B.S.5N.1U.U=G(){u U=B.J.U;F"{w: "+U(D.w)+", h: "+U(D.h)+"}"};B.S.5t=G(x,y){D.x=x;D.y=y};B.S.5t.1U.U=G(){u U=B.J.U;F"{x: "+U(D.x)+", y: "+U(D.y)+"}"};B.S.5t.1U.1l=G(){F D.U()};B.J.2l(B.S,{jl:G(Q,o){Q=B.S.1E(Q);B.S.4X(Q,{"1T":{"9o":o,"-hL-9o":o,"-uw-9o":o,"47":" uv(9o="+(o*3k)+")"}})},jk:G(){u d=Y B.S.5N();u w=B.S.3X;u b=B.S.1Z.5s;if(w.jj){d.w=w.jj;d.h=w.uu}N{if(b.dd.9n){d.w=b.dd.9n;d.h=b.dd.ji}N{if(b&&b.9n){d.w=b.9n;d.h=b.ji}}}F d},jh:G(Q){u I=B.S;if(H(Q.w)=="2y"||H(Q.h)=="2y"){F Y I.5N(Q.w||0,Q.h||0)}Q=I.1E(Q);if(!Q){F L}if(I.4q(Q,"3u")!="98"){F Y I.5N(Q.jg||0,Q.ci||0)}u s=Q.1T;u je=s.dc;u jf=s.6P;s.dc="fR";s.6P="j8";s.3u="";u jd=Q.jg;u jc=Q.ci;s.3u="98";s.6P=jf;s.dc=je;F Y I.5N(jd,jc)},jb:G(Q,4Z){u I=B.S;Q=I.1E(Q);if(!Q){F L}u c=Y I.5t(0,0);if(Q.x&&Q.y){c.x+=Q.x||0;c.y+=Q.y||0;F c}N{if(Q.3t===O||I.4q(Q,"3u")=="98"){F L}}u 51=O;u 2j=O;u d=B.S.1Z;u de=d.7Z;u b=d.5s;if(Q.ja){51=Q.ja();c.x+=51.2I+(de.6y||b.6y)-(de.8q||b.8q);c.y+=51.3D+(de.4C||b.4C)-(de.8p||b.8p)}N{if(d.j9){51=d.j9(Q);c.x+=51.x;c.y+=51.y}N{if(Q.8g){c.x+=Q.db;c.y+=Q.da;2j=Q.8g;if(2j!=Q){1M(2j){c.x+=2j.db;c.y+=2j.da;2j=2j.8g}}u ua=ut.us.8G();if((H(7h)!="L"&&4M(7h.ur())<9)||(ua.2A("uq")!=-1&&I.4q(Q,"6P")=="j8")){c.x-=b.db;c.y-=b.da}}}}if(H(4Z)!="L"){4Z=M.2U(4Z);if(4Z){c.x-=(4Z.x||0);c.y-=(4Z.y||0)}}if(Q.3t){2j=Q.3t}N{2j=O}1M(2j&&2j.j7!="uo"&&2j.j7!="co"){c.x-=2j.6y;c.y-=2j.4C;if(2j.3t){2j=2j.3t}N{2j=O}}F c},j6:G(Q,d9,7g){Q=B.S.1E(Q);if(H(7g)=="L"){7g="px"}B.S.4X(Q,{"1T":{"5A":d9.w+7g,"3V":d9.h+7g}})},j5:G(Q,d8,7f){Q=B.S.1E(Q);if(H(7f)=="L"){7f="px"}B.S.4X(Q,{"1T":{"2I":d8.x+7f,"3D":d8.y+7f}})},cr:G(){F B.S.3X},b9:G(){F B.S.1Z},95:G(2m,1A){u I=B.S;u d6=I.1Z;u d7=I.un;u W;1f{I.3X=2m;I.1Z=2m.2v;W=1A()}1e(e){I.3X=d7;I.1Z=d6;14 e}I.3X=d7;I.1Z=d6;F W},d5:G(Q){u 7d=[];u 7c=[];u m=B.J;u I=B.S;if(H(Q)=="L"||Q===O){Q=I.1Z}N{Q=I.1E(Q)}m.d4(Q,G(Q){u 1b=Q.1b;if(m.7e(1b)){u 4Y=Q.cD;if(4Y=="cv"&&(Q.1J=="um"||Q.1J=="uk")&&!Q.ip){F O}if(4Y=="ct"){if(Q.j4>=0){u 9m=Q.1S[Q.j4];7d.1c(1b);7c.1c((9m.3m)?9m.3m:9m.7X);F O}7d.1c(1b);7c.1c("");F O}if(4Y=="cu"||4Y=="P"||4Y=="8d"||4Y=="6m"){F Q.5h}7d.1c(1b);7c.1c(Q.3m||"");F O}F Q.5h});F[7d,7c]},94:G(1N,1A){u I=B.S;u d3=I.1Z;u W;1f{I.1Z=1N;W=1A()}1e(e){I.1Z=d3;14 e}I.1Z=d3;F W},j3:G(1b,j2,3y,j1){B.S.9b.5M(1b,j2,3y,j1)},9k:G(1j,7a){u im=B.15;u I=B.S;u 1Q=im.1Q;u iY=im.7b;u 4c=im.4c;u iX=I.9b;u iZ=I.9k;u iW=B.J.4d;1M(1h){if(H(1j)=="L"||1j===O){F O}if(H(1j.3T)!="L"&&1j.3T>0){F 1j}if(H(1j)=="2y"||H(1j)=="5L"){1j=1j.1l()}if(H(1j)=="1n"){F I.1Z.4S(1j)}if(H(1j.j0)=="G"){1j=1j.j0(7a);2V}if(H(1j)=="G"){1j=1j(7a);2V}u 9l=O;1f{9l=1Q(1j)}1e(e){}if(9l){F 4c(iZ,9l,iY(7a))}1f{1j=iX.3C(1j,7a);2V}1e(e){if(e!=iW){14 e}}F I.1Z.4S(1j.1l())}F L},iV:G(1j,79,iU){u o={};o[79]=iU;1f{F B.S.4X(1j,o)}1e(e){}F O},iT:G(1j,79){u I=B.S;u d2=I.4U.99[79];1j=I.1E(1j);1f{if(d2){F 1j[d2]}F 1j.fm(79)}1e(e){}F O},4X:G(1j,5K){u Q=1j;u I=B.S;if(H(1j)=="1n"){Q=I.1E(1j)}if(5K){u d0=B.J.8Z;if(I.4U.6X){R(u k in 5K){u v=5K[k];if(H(v)=="3n"&&H(Q[k])=="3n"){d0(Q[k],v)}N{if(k.2W(0,2)=="on"){if(H(v)=="1n"){v=Y cZ(v)}Q[k]=v}N{Q.4p(k,v)}}}}N{u iS=I.4U.99;R(k in 5K){v=5K[k];u d1=iS[k];if(k=="1T"&&H(v)=="1n"){Q.1T.3x=v}N{if(H(d1)=="1n"){Q[d1]=v}N{if(H(Q[k])=="3n"&&H(v)=="3n"){d0(Q[k],v)}N{if(k.2W(0,2)=="on"){if(H(v)=="1n"){v=Y cZ(v)}Q[k]=v}N{Q.4p(k,v)}}}}}}}F Q},9j:G(1j){u Q=1j;u I=B.S;if(H(1j)=="1n"){Q=I.1E(1j)}u 78=[I.9k(B.J.1R(O,M,1),Q)];u iR=B.J.2o;1M(78.K){u n=78.2P();if(H(n)=="L"||n===O){}N{if(H(n.3T)=="2y"){Q.2c(n)}N{78=iR(n,78)}}}F Q},iQ:G(1j){u Q=1j;u I=B.S;if(H(1j)=="1n"){Q=I.1E(1j);M[0]=Q}u cY;1M((cY=Q.6n)){Q.6S(cY)}if(M.K<2){F Q}N{F I.9j.1w(D,M)}},cX:G(1b,4b){u Q;u I=B.S;u m=B.J;if(H(4b)=="1n"||H(4b)=="2y"){u 3G=m.1R([1b,O],M,1);F M.2U.1w(D,3G)}if(H(1b)=="1n"){if(4b&&"1b"in 4b&&!I.4U.6X){1b=("<"+1b+" 1b=\\""+I.9c(4b.1b)+"\\">")}Q=I.1Z.2S(1b)}N{Q=1b}if(4b){I.4X(Q,4b)}if(M.K<=2){F Q}N{u 3G=m.1R([Q],M,2);F I.9j.1w(D,3G)}},cw:G(){u m=B.J;F m.2z.1w(D,m.1R([B.S.cX],M))},cs:G(5J,1d){u I=B.S;5J=I.1E(5J);u cW=5J.3t;if(1d){1d=I.1E(1d);cW.uj(1d,5J)}N{cW.6S(5J)}F 1d},1E:G(id){u I=B.S;if(M.K==1){F((H(id)=="1n")?I.1Z.hN(id):id)}N{F B.J.2r(I.1E,M)}},4q:G(iP,cV,cU){if(M.K==2){cU=cV}u I=B.S;u el=I.1E(iP);u 77=I.1Z;if(!el||el==77){F L}if(el.iO){F el.iO[cV]}if(H(77.5k)=="L"){F L}if(77.5k===O){F L}u 9i=77.5k.g4(el,O);if(H(9i)=="L"||9i===O){F L}F 9i.6q(cU)},aH:G(76,9g,4W){u I=B.S;if(H(76)=="L"||76===O){76="*"}if(H(4W)=="L"||4W===O){4W=I.1Z}4W=I.1E(4W);u 9h=(4W.fr(76)||I.1Z.1p);if(H(9g)=="L"||9g===O){F B.J.1R(O,9h)}u cR=[];R(u i=0;i<9h.K;i++){u cS=9h[i];u cT=cS.3M.2R(" ");R(u j=0;j<cT.K;j++){if(cT[j]==9g){cR.1c(cS);2K}}}F cR},iN:G(5I,9f){u W=G(){u cQ=M.2U.5H;R(u i=0;i<cQ.K;i++){if(cQ[i].1w(D,M)===1m){2K}}if(9f){1f{D[5I]=O}1e(e){}}};W.5H=[];F W},cO:G(cP,5I,1A,9f){u I=B.S;u 4V=cP[5I];u 75=4V;if(!(H(4V)=="G"&&H(4V.5H)=="3n"&&4V.5H!==O)){75=I.iN(5I,9f);if(H(4V)=="G"){75.5H.1c(4V)}cP[5I]=75}75.5H.1c(1A)},cN:G(1A){u I=B.S;I.cO(I.3X,"gh",1A,1h)},iM:G(74){u I=B.S;I.cN(G(){74=I.1E(74);if(74){74.ui()}})},5G:G(iL,cM){u I=B.S;u 1i=I.1E(iL);if(I.4U.6X){1i.4p("iq",cM)}N{1i.4p("3M",cM)}},iK:G(cL){u I=B.S;R(u i=1;i<M.K;i++){u 1i=I.1E(M[i]);if(!I.9d(1i,cL)){I.9e(1i,cL)}}},9d:G(iJ,73){u I=B.S;u 1i=I.1E(iJ);u 2F=1i.3M;if(2F.K===0){I.5G(1i,73);F 1h}if(2F==73){F 1m}u cK=1i.3M.2R(" ");R(u i=0;i<cK.K;i++){if(cK[i]==73){F 1m}}I.5G(1i,2F+" "+73);F 1h},9e:G(iI,cJ){u I=B.S;u 1i=I.1E(iI);u 2F=1i.3M;if(2F.K===0){F 1m}if(2F==cJ){I.5G(1i,"");F 1h}u 72=1i.3M.2R(" ");R(u i=0;i<72.K;i++){if(72[i]==cJ){72.4y(i,1);I.5G(1i,72.2b(" "));F 1h}}F 1m},iH:G(iG,iF,iE){u 1i=B.S.1E(iG);u X=B.S.9e(1i,iF);if(X){B.S.9d(1i,iE)}F X},iD:G(iC,uh){u 1i=B.S.1E(iC);u cI=1i.3M.2R(" ");R(u i=1;i<M.K;i++){u cH=1m;R(u j=0;j<cI.K;j++){if(cI[j]==M[i]){cH=1h;2K}}if(!cH){F 1m}}F 1h},9c:G(s){F s.23(/&/g,"&ug;").23(/"/g,"&uf;").23(/</g,"<").23(/>/g,">")},iB:G(2q){F B.S.cG(2q).2b("")},cG:G(2q,1g){if(H(1g)=="L"||1g===O){1g=[]}u 70=[2q];u I=B.S;u cB=I.9c;u iA=I.4U;1M(70.K){2q=70.hP();if(H(2q)=="1n"){1g.1c(2q)}N{if(2q.3T==1){1g.1c("<"+2q.cD.8G());u 71=[];u cF=iA(2q);R(u i=0;i<cF.K;i++){u a=cF[i];71.1c([" ",a.1b,"=\\"",cB(a.3m),"\\""])}71.iz();R(i=0;i<71.K;i++){u cE=71[i];R(u j=0;j<cE.K;j++){1g.1c(cE[j])}}if(2q.ue()){1g.1c(">");70.1c("</"+2q.cD.8G()+">");u cC=2q.5h;R(i=cC.K-1;i>=0;i--){70.1c(cC[i])}}N{1g.1c("/>")}}N{if(2q.3T==3){1g.1c(cB(2q.iv))}}}}F 1g},97:G(ix,cA){u m=B.J;u iy=m.1R(O,M,1);B.15.9a(m.47(O,m.2r(B.S.1E,iy)),G(cA){cA.1T.3u=ix})},iw:G(1j,iu){u W=[];(G(1j){u cn=1j.5h;if(cn){R(u i=0;i<cn.K;i++){M.2U.cz(D,cn[i])}}u cy=1j.iv;if(H(cy)=="1n"){W.1c(cy)}})(B.S.1E(1j));if(iu){F W}N{F W.2b("")}},2d:G(2m){u m=B.J;D.1Z=2v;D.3X=2m;D.9b=Y m.4a();u 6Z=D.1Z.2S("cj");u 2T;if(6Z&&6Z.6Y&&6Z.6Y.K>0){u it=m.47;2T=G(1j){F it(2T.ir,1j.6Y)};2T.cx={};B.15.9a(6Z.6Y,G(a){2T.cx[a.1b]=a.3m});2T.ir=G(a){F(2T.cx[a.1b]!=a.3m)};2T.6X=1m;2T.99={"iq":"3M","ip":"ud","uc":"ub","R":"u9"}}N{2T=G(1j){F 1j.6Y};2T.6X=1h;2T.99={}}D.4U=2T;u 1C=D.cw;D.io=1C("ul");D.il=1C("ol");D.ik=1C("li");D.ij=1C("td");D.cm=1C("tr");D.ii=1C("u8");D.ih=1C("u7");D.ig=1C("u6");D.ie=1C("u5");D.ic=1C("th");D.cv=1C("ck");D.8d=1C("cj");D.A=1C("a");D.6m=1C("4u");D.ib=1C("u4");D.ia=1C("2e");D.i9=1C("tt");D.i8=1C("4O");D.i7=1C("h1");D.i6=1C("h2");D.i5=1C("h3");D.i4=1C("br");D.i3=1C("hr");D.i2=1C("u3");D.i1=1C("u2");D.cu=1C("u1");D.P=1C("p");D.ct=1C("u0");D.i0=1C("hJ");D.hZ=1C("tZ");D.hY=1C("tY");D.hX=1C("tX");D.hW=1C("tW");D.hV=1C("tV");D.hU=m.2z(D.97,"98");D.hT=m.2z(D.97,"8c");D.hS=D.cs;D.$=D.1E;D.2k={":3e":D.1z,":1p":m.2o(D.1z,D.1W)};m.3f(D)}});B.S.2d(((H(2O)=="L")?D:2O));if(!B.3d){95=B.S.95;94=B.S.94}B.J.2Y(D,B.S);if(H(1q)!="L"){1q.2X("B.1I");1q.2M("B.1H");1q.2M("B.J")}if(H(1x)!="L"){1x.26("B.1H",[]);1x.26("B.J",[])}1f{if(H(B.J)=="L"||H(B.1H)=="L"){14""}}1e(e){14"B.1I 3F on B.J 3W B.1H!"}if(H(B.1I)=="L"){B.1I={}}B.1I.1r="B.1I";B.1I.1Y="1.3.1";B.1I.1K=G(){F"["+D.1r+" "+D.1Y+"]"};B.1I.1l=G(){F D.1K()};B.1I.bY=G(6W){u m=B.1I;6W=!(!6W);if(m.3l&&m.3l.8Q!=6W){m.3l.hA();m.3l=O}if(!m.3l||m.3l.8P){m.3l=Y m.1I(6W,B.1H.2L)}F m.3l};B.1I.1I=G(4R,6V){if(H(6V)=="L"||6V===O){6V=B.1H.2L}D.2L=6V;u tU=B.J.2l;u c3=B.J.8Z;u 1O=B.J.1O;u hM=B.J.4L;u 2m=2O;u 6U="tT";if(H(B.S)!="L"){2m=B.S.cr()}if(!4R){u 5F=2m.tS.tR.2R("?")[0].23(/[:\\/.><&]/g,"hR");u 1b=6U+"hR"+5F;u 5D=2m.cp("",1b,"tQ,tP,3V=hQ");if(!5D){cq("tO tN to cp tM 2O tL to hP-up tK.");F L}5D.2v.fl("<!tJ co tI \\"-//tH//tG co 4.0 tF//tE\\" "+"\\"fq://fp.tD.fo/cm/tC/tB.tA\\">"+"<hO><5E><8Y>[B.1I]</8Y></5E>"+"<5s></5s></hO>");5D.2v.hG();5D.2v.8Y+=" "+2m.2v.8Y;2m=5D}u 1N=2m.2v;D.1N=1N;u 21=1N.hN(6U);u c4=!!21;if(21&&H(21.5B)!="L"){21.5B.2L=D.2L;21.5B.6K();F 21.5B}if(c4){u cl;1M((cl=21.6n)){21.6S(cl)}}N{21=1N.2S("4u");21.id=6U}21.5B=D;u 8T=1N.2S("ck");u 8S=1N.2S("ck");u 6O=1N.2S("2e");u 6N=1N.2S("2e");u 6M=1N.2S("2e");u 6L=1N.2S("2e");u 3L=1N.2S("4u");u 42=1N.2S("4u");u 8U=6U+"tz";D.8N=hM(D.8N);u 4T=[];u 6R=O;u cf=G(1t){u 6T=1t.3N;if(H(6T)=="2y"){6T=B.1H.5C[6T]}F 6T};u cd=G(1t){F 1t.3z.2b(" ")};u ca=1O(G(1t){u 8W=cf(1t);u 7X=cd(1t);u c=D.8N[8W];u p=1N.2S("cj");p.3M="B-49 B-5C-"+8W;p.1T.3x="ty: 2N; 4F-8X: -hL-4O-3y; 4F-8X: -o-4O-3y; 4F-8X: 4O-3y; 4F-8X: 4O-tx; hK-3y: 2K-hK; 3y-hJ: tw; 3U: "+c;p.2c(1N.4S(8W+": "+7X));42.2c(p);42.2c(1N.2S("br"));if(3L.ci>3L.hI){3L.4C=0}N{3L.4C=3L.hI}},D);u hD=G(1t){4T[4T.K]=1t;ca(1t)};u hF=G(){u cg,ce;1f{cg=Y 8V(8T.3m);ce=Y 8V(8S.3m)}1e(e){ch("2x in 47 tv: "+e.43);F O}F G(1t){F(cg.hH(cf(1t))&&ce.hH(cd(1t)))}};u cc=G(){1M(42.6n){42.6S(42.6n)}};u hB=G(){4T=[];cc()};u bZ=1O(G(){if(D.8P){F}D.8P=1h;if(B.1I.3l==D){B.1I.3l=O}D.2L.c9(8U);21.5B=O;if(4R){21.3t.6S(21)}N{D.2m.hG()}},D);u c7=G(){cc();R(u i=0;i<4T.K;i++){u 1t=4T[i];if(6R===O||6R(1t)){ca(1t)}}};D.6K=G(){6R=hF();c7();D.2L.c9(8U);D.2L.hE(8U,6R,hD)};u c0=1O(G(){4T=D.2L.c8();c7()},D);u c2=1O(G(6Q){6Q=6Q||2O.6D;2h=6Q.6w||6Q.8t;if(2h==13){D.6K()}},D);u 31="3u: 8c; z-c6: c5; 2I: 2N; 6f: 2N; 6P: tu; 5A: 3k%; he-3U: 4F; c1: "+D.8O;if(4R){31+="; 3V: ts; 3E-3D: fO 8a 8y"}N{31+="; 3V: 3k%;"}21.1T.3x=31;if(!c4){1N.5s.2c(21)}31={"3x":"5A: 33%; 3u: 8Q; c1: "+D.8O};c3(8T,{"3m":"8L|8M|8K|8J|8I","hC":c2,"1T":31});21.2c(8T);c3(8S,{"3m":".*","hC":c2,"1T":31});21.2c(8S);31="5A: 8%; 3u:8Q; c1: "+D.8O;6O.2c(1N.4S("tq"));6O.8R=1O("6K",D);6O.1T.3x=31;21.2c(6O);6N.2c(1N.4S("tp"));6N.8R=c0;6N.1T.3x=31;21.2c(6N);6M.2c(1N.4S("tn"));6M.8R=hB;6M.1T.3x=31;21.2c(6M);6L.2c(1N.4S("tm"));6L.8R=bZ;6L.1T.3x=31;21.2c(6L);3L.1T.3x="fS: tk; 5A: 3k%";42.1T.3x="5A: 3k%; 3V: "+(4R?"tj":"3k%");3L.2c(42);21.2c(3L);D.6K();c0();if(4R){D.2m=L}N{D.2m=2m}D.8Q=4R;D.hA=bZ;D.8P=1m;F D};B.1I.1I.1U={"8O":"ti tg,tf-te","8N":{"8M":"1v","8L":"gU","8K":"1F","8J":"8y","8I":"bx"}};B.1I.1W=["1I"];B.1I.1z=["bY"];B.1I.2d=G(){D.2k={":3e":D.1z,":1p":B.J.2o(D.1z,D.1W)};B.J.3f(D);B.1I.3l=O};B.1I.2d();B.J.2Y(D,B.1I);if(H(1q)!="L"){1q.2X("B.V");1q.2M("B.J")}if(H(1x)!="L"){1x.26("B.J",[])}1f{if(H(B.J)=="L"){14""}}1e(e){14"B.V 3F on B.J"}if(H(B.V)=="L"){B.V={}}B.V.1r="B.V";B.V.1Y="1.3.1";B.V.1K=G(){F"["+D.1r+" "+D.1Y+"]"};B.V.1l=G(){F D.1K()};B.V.V=G(1v,hz,1F,6J){if(H(6J)=="L"||6J===O){6J=1}D.1B={r:1v,g:hz,b:1F,a:6J}};B.V.V.1U={bX:B.V.V,tc:G(hy){u 1B=D.1B;u m=B.V;F m.V.3Y(1B.r,1B.g,1B.b,hy)},tb:G(1o){u 1G=D.41();1G.h=1o;u m=B.V;F m.V.4H(1G)},ta:G(hx){u 1G=D.41();1G.s=hx;u m=B.V;F m.V.4H(1G)},t9:G(hw){u 1G=D.41();1G.l=hw;u m=B.V;F m.V.4H(1G)},t8:G(hv){u 1G=D.41();1G.l=28.29(1G.l-hv,0);u m=B.V;F m.V.4H(1G)},t7:G(hu){u 1G=D.41();1G.l=28.2a(1G.l+hu,1);u m=B.V;F m.V.4H(1G)},fJ:G(ht,5z){if(H(5z)=="L"||5z===O){5z=0.5}u sf=1-5z;u s=D.1B;u d=ht.1B;u df=5z;F B.V.V.3Y((s.r*sf)+(d.r*df),(s.g*sf)+(d.g*df),(s.b*sf)+(d.b*df),(s.a*sf)+(d.a*df))},h4:G(hs){u a=D.6r();u b=hs.6r();F B.J.2f([a.r,a.g,a.b,a.a],[b.r,b.g,b.b,b.a])},hq:G(){F D.41().b>0.5},t6:G(){F(!D.hq())},t5:G(){u c=D.41();u 2Z=B.V.6F;u W=D.ho;if(!W){u 5y=(2Z(c.h,bF).6I(0)+","+2Z(c.s,3k).hp(4)+"%"+","+2Z(c.l,3k).hp(4)+"%");u a=c.a;if(a>=1){a=1;W="1G("+5y+")"}N{if(a<=0){a=0}W="t4("+5y+","+a+")"}D.ho=W}F W},hl:G(){u c=D.1B;u 2Z=B.V.6F;u W=D.hn;if(!W){u 5y=(2Z(c.r,3h).6I(0)+","+2Z(c.g,3h).6I(0)+","+2Z(c.b,3h).6I(0));if(c.a!=1){W="t3("+5y+","+c.a+")"}N{W="1B("+5y+")"}D.hn=W}F W},6r:G(){F B.J.4L(D.1B)},t2:G(){u m=B.V;u c=D.1B;u 2Z=B.V.6F;u W=D.hm;if(!W){W=("#"+m.6E(2Z(c.r,3h))+m.6E(2Z(c.g,3h))+m.6E(2Z(c.b,3h)));D.hm=W}F W},t1:G(){u 2Q=D.2Q;u c=D.1B;if(H(2Q)=="L"||2Q===O){2Q=B.V.bA(D.1B);D.2Q=2Q}F B.J.4L(2Q)},41:G(){u 1G=D.1G;u c=D.1B;if(H(1G)=="L"||1G===O){1G=B.V.bC(D.1B);D.1G=1G}F B.J.4L(1G)},1l:G(){F D.hl()},U:G(){u c=D.1B;u hk=[c.r,c.g,c.b,c.a];F D.bX.1r+"("+hk.2b(", ")+")"}};B.J.2l(B.V.V,{3Y:G(1v,bW,1F,8H){u hj=B.V.V;if(M.K==1){u 1B=1v;1v=1B.r;bW=1B.g;1F=1B.b;if(H(1B.a)=="L"){8H=L}N{8H=1B.a}}F Y hj(1v,bW,1F,8H)},4H:G(1o,t0,sZ,sY){u m=B.V;F m.V.3Y(m.bB.1w(m,M))},sX:G(1o,sW,sV,sU){u m=B.V;F m.V.3Y(m.bz.1w(m,M))},hi:G(1b){u 8F=B.V.V;if(1b.3Z(0)=="\\""){1b=1b.3H(1,1b.K-2)}u bV=8F.by[1b.8G()];if(H(bV)=="1n"){F 8F.bT(bV)}N{if(1b=="aP"){F 8F.sT()}}F O},8f:G(4Q){u I=B.V.V;u bU=4Q.3H(0,3);if(bU=="1B"){F I.h9(4Q)}N{if(bU=="1G"){F I.h8(4Q)}N{if(4Q.3Z(0)=="#"){F I.bT(4Q)}}}F I.hi(4Q)},bT:G(4P){if(4P.3Z(0)=="#"){4P=4P.2W(1)}u 8E=[];u i,5x;if(4P.K==3){R(i=0;i<3;i++){5x=4P.3H(i,1);8E.1c(3w(5x+5x,16)/3h)}}N{R(i=0;i<6;i+=2){5x=4P.3H(i,2);8E.1c(3w(5x,16)/3h)}}u bS=B.V.V;F bS.3Y.1w(bS,8E)},bG:G(4O,hf,hg,4N){if(4N.2A(4O)===0){4N=4N.2W(4N.2A("(",3)+1,4N.K-1)}u bR=4N.2R(/\\s*,\\s*/);u bP=[];R(u i=0;i<bR.K;i++){u c=bR[i];u 2i;u bQ=c.2W(c.K-3);if(c.3Z(c.K-1)=="%"){2i=0.bE*4M(c.2W(0,c.K-1))}N{if(bQ=="sS"){2i=4M(c)/bF}N{if(bQ=="sR"){2i=4M(c)/(28.sQ*2)}N{2i=hg[i]*4M(c)}}}bP.1c(2i)}F D[hf].1w(D,bP)},bN:G(Q,sP,sO){u d=B.S;u 2F=B.V.V;R(Q=d.1E(Q);Q;Q=Q.3t){u bO=d.4q.1w(d,M);if(!bO){2V}u 8D=2F.8f(bO);if(!8D){2K}if(8D.6r().a>0){F 8D}}F O},ba:G(Q){u 2F=B.V.V;F 2F.bN(Q,"aZ","he-3U")||2F.sN()},sM:G(Q){u 2F=B.V.V;F 2F.bN(Q,"3U","3U")||2F.sL()},sK:G(){F B.J.4L(B.V.V.by)}});B.J.2l(B.V,{6F:G(v,8C){v*=8C;if(v<0){F 0}N{if(v>8C){F 8C}N{F v}}},hc:G(n1,n2,1o){if(1o>6){1o-=6}N{if(1o<0){1o+=6}}u 2i;if(1o<1){2i=n1+(n2-n1)*1o}N{if(1o<3){2i=n2}N{if(1o<4){2i=n1+(n2-n1)*(4-1o)}N{2i=n1}}}F 2i},bz:G(1o,5w,3i,bM){if(M.K==1){u 2Q=1o;1o=2Q.h;5w=2Q.s;3i=2Q.v;bM=2Q.a}u 1v;u 3K;u 1F;if(5w===0){1v=0;3K=0;1F=0}N{u i=28.8B(1o*6);u f=(1o*6)-i;u p=3i*(1-5w);u q=3i*(1-(5w*f));u t=3i*(1-(5w*(1-f)));hd(i){3j 1:1v=q;3K=3i;1F=p;2K;3j 2:1v=p;3K=3i;1F=t;2K;3j 3:1v=p;3K=q;1F=3i;2K;3j 4:1v=t;3K=p;1F=3i;2K;3j 5:1v=3i;3K=p;1F=q;2K;3j 6:3j 0:1v=3i;3K=t;1F=p;2K}}F{r:1v,g:3K,b:1F,a:bM}},bB:G(1o,5v,3v,bL){if(M.K==1){u 1G=1o;1o=1G.h;5v=1G.s;3v=1G.l;bL=1G.a}u 1v;u 8A;u 1F;if(5v===0){1v=3v;8A=3v;1F=3v}N{u m2;if(3v<=0.5){m2=3v*(1+5v)}N{m2=3v+5v-(3v*5v)}u m1=(2*3v)-m2;u f=B.V.hc;u h6=1o*6;1v=f(m1,m2,h6+2);8A=f(m1,m2,h6);1F=f(m1,m2,h6-2)}F{r:1v,g:8A,b:1F,a:bL}},bA:G(1v,4K,1F,bK){if(M.K==1){u 1B=1v;1v=1B.r;4K=1B.g;1F=1B.b;bK=1B.a}u 29=28.29(28.29(1v,4K),1F);u 2a=28.2a(28.2a(1v,4K),1F);u 1o;u 8z;u hb=29;if(2a==29){1o=0;8z=0}N{u 6H=(29-2a);8z=6H/29;if(1v==29){1o=(4K-1F)/6H}N{if(4K==29){1o=2+((1F-1v)/6H)}N{1o=4+((1v-4K)/6H)}}1o/=6;if(1o<0){1o+=1}if(1o>1){1o-=1}}F{h:1o,s:8z,v:hb,a:bK}},bC:G(1v,4J,1F,bI){if(M.K==1){u 1B=1v;1v=1B.r;4J=1B.g;1F=1B.b;bI=1B.a}u 29=28.29(1v,28.29(4J,1F));u 2a=28.2a(1v,28.2a(4J,1F));u 1o;u 6G;u bJ=(29+2a)/2;u 4I=29-2a;if(4I===0){1o=0;6G=0}N{if(bJ<=0.5){6G=4I/(29+2a)}N{6G=4I/(2-29-2a)}if(1v==29){1o=(4J-1F)/4I}N{if(4J==29){1o=2+((1F-1v)/4I)}N{1o=4+((1v-4J)/4I)}}1o/=6;if(1o<0){1o+=1}if(1o>1){1o-=1}}F{h:1o,s:6G,l:bJ,a:bI}},6E:G(1P){1P=28.ha(1P);u bH=1P.1l(16);if(1P<16){F"0"+bH}F bH},2d:G(){u m=B.J;D.V.h9=m.1O(D.V.bG,D.V,"1B","3Y",[1/3h,1/3h,1/3h,1]);D.V.h8=m.1O(D.V.bG,D.V,"1G","4H",[1/bF,0.bE,0.bE,1]);u 4G=1/3;u bD={8y:[0,0,0],1F:[0,0,1],gY:[0.6,0.4,0.2],gX:[0,1,1],sJ:[4G,4G,4G],gR:[0.5,0.5,0.5],bx:[0,1,0],sI:[2*4G,2*4G,2*4G],gN:[1,0,1],gL:[1,0.5,0],gK:[0.5,0,0.5],1v:[1,0,0],aP:[0,0,0,0],4F:[1,1,1],gI:[1,1,0]};u h7=G(1b,r,g,b,a){u W=D.3Y(r,g,b,a);D[1b]=G(){F W};F W};R(u k in bD){u 1b=k+"V";u h5=m.2o([h7,D.V,1b],bD[k]);D.V[1b]=m.1O.1w(O,h5)}u h0=G(){R(u i=0;i<M.K;i++){if(!(M[i]2C V)){F 1m}}F 1h};u gZ=G(a,b){F a.h4(b)};m.3f(D);m.5u(D.V.1r,h0,gZ);D.2k={":3e":D.1z,":1p":m.2o(D.1z,D.1W)}}});B.V.1z=["V"];B.V.1W=["6F","bC","bB","bA","bz","6E"];B.V.2d();B.J.2Y(D,B.V);B.V.V.by={sH:"#sG",sF:"#sE",sD:"#gW",sC:"#sB",sA:"#sz",sy:"#sx",sw:"#sv",8y:"#su",st:"#sr",1F:"#sq",sp:"#so",gY:"#sn",sm:"#sl",sk:"#sj",si:"#sh",sg:"#se",sd:"#sc",sb:"#sa",s9:"#s8",s7:"#s6",gX:"#gW",s5:"#s4",s3:"#s2",s1:"#s0",rZ:"#gV",rY:"#rX",rW:"#gV",rV:"#rU",rT:"#rS",rR:"#rQ",rP:"#rO",rN:"#rM",gU:"#rL",rK:"#rJ",rI:"#rH",rG:"#rF",rE:"#gT",rD:"#gT",rC:"#rB",rA:"#rz",ry:"#rx",rw:"#rv",ru:"#gS",rt:"#gS",rs:"#rr",rq:"#rp",ro:"#rn",rm:"#rl",rk:"#gM",rj:"#ri",rh:"#rg",rf:"#rd",rc:"#rb",gR:"#gQ",bx:"#ra",r9:"#r8",r7:"#gQ",r6:"#r5",r4:"#r3",r2:"#r1",r0:"#qZ",qY:"#qX",qW:"#qV",qU:"#qT",qS:"#qR",qQ:"#qP",qO:"#qN",qM:"#qL",qK:"#qJ",qI:"#qH",qG:"#qF",qE:"#gP",qD:"#qC",qB:"#gP",qA:"#qz",qy:"#qx",qw:"#qv",qu:"#qt",qr:"#gO",qq:"#gO",qp:"#qo",qn:"#qm",ql:"#qk",qj:"#qi",qh:"#qg",gN:"#gM",qf:"#qe",qd:"#qc",qb:"#qa",q9:"#q8",q7:"#q6",q5:"#q4",q3:"#q2",q1:"#q0",pZ:"#pY",pX:"#pW",pV:"#pU",pT:"#pS",pR:"#pQ",pP:"#pO",pN:"#pM",pL:"#pK",pJ:"#pI",pH:"#pG",pF:"#pE",gL:"#pD",pC:"#pB",pA:"#pz",py:"#pw",pv:"#pu",pt:"#ps",pr:"#pq",pp:"#po",pn:"#pm",pl:"#pj",pi:"#ph",pg:"#pf",pe:"#pd",gK:"#pc",1v:"#pb",pa:"#p9",p8:"#p7",p6:"#p5",p4:"#p3",p2:"#p1",p0:"#oZ",oY:"#oX",oW:"#oV",oU:"#oT",oS:"#oR",oQ:"#oP",oO:"#gJ",oN:"#gJ",oM:"#oL",oK:"#oJ",oI:"#oH",oG:"#oF",oE:"#oD",oC:"#oB",oA:"#oz",oy:"#ox",ow:"#ov",ou:"#ot",4F:"#os",oq:"#op",gI:"#oo",om:"#ok"};if(H(1q)!="L"){1q.2X("B.1u");1q.2M("B.J");1q.2M("B.S")}if(H(1x)!="L"){1x.26("B.J",[]);1x.26("B.S",[])}1f{if(H(B.J)=="L"){14""}}1e(e){14"B.1u 3F on B.J!"}1f{if(H(B.S)=="L"){14""}}1e(e){14"B.1u 3F on B.S!"}if(H(B.1u)=="L"){B.1u={}}B.1u.1r="B.1u";B.1u.1Y="1.3.1";B.1u.4x=[];B.1u.bq=G(1d,e){D.1L=e||2O.6D;D.gH=1d};B.J.2l(B.1u.bq.1U,{1K:G(){u U=B.J.U;u 1y="{6D(): "+U(D.6D())+", 1d(): "+U(D.1d())+", 1J(): "+U(D.1J())+", 8x(): "+U(D.8x())+", 4E(): "+"{8w: "+U(D.4E().8w)+", 8v: "+U(D.4E().8v)+", 8u: "+U(D.4E().8u)+", 2P: "+U(D.4E().2P)+", bw: "+U(D.4E().bw)+"}";if(D.1J()&&D.1J().2A("2h")===0){1y+=", 2h(): {3J: "+U(D.2h().3J)+", 1n: "+U(D.2h().1n)+"}"}if(D.1J()&&(D.1J().2A("3I")===0||D.1J().2A("gE")!=-1||D.1J()=="gD")){1y+=", 3I(): {4D: "+U(D.3I().4D)+", 6A: "+U(D.3I().6A);if(D.1J()!="gC"){1y+=", 2e: {2I: "+U(D.3I().2e.2I)+", 6v: "+U(D.3I().2e.6v)+", 3g: "+U(D.3I().2e.3g)+"}}"}N{1y+="}"}}if(D.1J()=="gG"||D.1J()=="gF"){1y+=", 6C(): "+U(D.6C())}1y+="}";F 1y},1l:G(){F D.1K()},1d:G(){F D.gH},6D:G(){F D.1L},1J:G(){F D.1L.1J||L},8x:G(){F D.1L.8x||D.1L.oj},6C:G(){if(D.1J()=="gG"){F(D.1L.6C||D.1L.aW)}N{if(D.1J()=="gF"){F(D.1L.6C||D.1L.oi)}}F L},4E:G(){u m={};m.8w=D.1L.oh;m.8v=D.1L.og;m.8u=D.1L.oe||1m;m.2P=D.1L.od;m.bw=m.8w||m.8v||m.2P||m.8u;F m},2h:G(){u k={};if(D.1J()&&D.1J().2A("2h")===0){if(D.1J()=="oc"||D.1J()=="ob"){k.3J=D.1L.8t;k.1n=(B.1u.5r[k.3J]||"oa");F k}N{if(D.1J()=="o9"){k.3J=0;k.1n="";if(H(D.1L.6B)!="L"&&D.1L.6B!==0&&!B.1u.bv[D.1L.6B]){k.3J=D.1L.6B;k.1n=bu.bt(k.3J)}N{if(D.1L.8t&&H(D.1L.6B)=="L"){k.3J=D.1L.8t;k.1n=bu.bt(k.3J)}}F k}}}F L},3I:G(){u m={};u e=D.1L;if(D.1J()&&(D.1J().2A("3I")===0||D.1J().2A("gE")!=-1||D.1J()=="gD")){m.6A=Y B.S.5t(0,0);if(e.6z||e.6x){m.6A.x=(!e.6z||e.6z<0)?0:e.6z;m.6A.y=(!e.6x||e.6x<0)?0:e.6x}m.4D=Y B.S.5t(0,0);if(e.8s||e.8r){m.4D.x=(!e.8s||e.8s<0)?0:e.8s;m.4D.y=(!e.8r||e.8r<0)?0:e.8r}N{u de=B.S.1Z.7Z;u b=B.S.1Z.5s;m.4D.x=e.6z+(de.6y||b.6y)-(de.8q||b.8q);m.4D.y=e.6x+(de.4C||b.4C)-(de.8p||b.8p)}if(D.1J()!="gC"){m.2e={};m.2e.2I=1m;m.2e.3g=1m;m.2e.6v=1m;if(e.6w){m.2e.2I=(e.6w==1);m.2e.6v=(e.6w==2);m.2e.3g=(e.6w==3)}N{m.2e.2I=!!(e.2e&1);m.2e.3g=!!(e.2e&2);m.2e.6v=!!(e.2e&4)}}F m}F L},2J:G(){D.8o();D.8n()},8o:G(){if(D.1L.8o){D.1L.8o()}N{D.1L.o8=1h}},8n:G(){if(D.1L.8n){D.1L.8n()}N{D.1L.o7=1m}}});B.1u.bv={3:"gz",o6:"gA",o5:"gy",o4:"gx",o3:"gw",o2:"gv",o1:"gu",o0:"gs",nZ:"gr",nY:"gq",nX:"gp",nW:"go"};R(i=gB;i<=nV;i++){B.1u.bv[i]="gk"+(i-gB+1)}B.1u.5r={8:"nU",9:"nT",12:"gA",13:"gz",16:"nS",17:"nR",18:"nQ",19:"nP",20:"nO",27:"nN",32:"nM",33:"gy",34:"gx",35:"gw",36:"gv",37:"gu",38:"gs",39:"gr",40:"gq",44:"nL",45:"gp",46:"go",59:"gn",91:"nK",92:"nJ",93:"nI",nH:"nG",nF:"nE",nD:"nC-gm",nB:"nA",nz:"ny",nx:"nw",nv:"nu",nt:"gn",ns:"nr",nq:"np",nn:"nm-gm",nl:"nk",nj:"ni",nh:"ng",nf:"nd",nc:"nb",na:"n9",n8:"n7"};R(u i=48;i<=57;i++){B.1u.5r[i]="gl"+(i-48)}R(i=65;i<=90;i++){B.1u.5r[i]="gl"+bu.bt(i)}R(i=96;i<=n6;i++){B.1u.5r[i]="n5"+(i-96)}R(i=gj;i<=n4;i++){B.1u.5r[i]="gk"+(i-gj+1)}B.J.2l(B.1u,{1K:G(){F"["+D.1r+" "+D.1Y+"]"},1l:G(){F D.1K()},g7:G(){u I=B.1u;u bs=I.4x;R(u i=0;i<bs.K;i++){I.6t(bs[i])}gi I.4x;1f{2O.gh=L}1e(e){}1f{2O.g8=L}1e(e){}},gb:G(1d,1A,1i,gg){u E=B.1u.bq;if(!gg){F B.J.1O(1A,1i)}1i=1i||1d;if(H(1A)=="1n"){F G(gf){1i[1A].1w(1i,[Y E(1d,gf)])}}N{F G(gd){1A.1w(1i,[Y E(1d,gd)])}}},6s:G(1d,2D,5q,4B){1d=B.S.1E(1d);u I=B.1u;if(H(2D)!="1n"){14 Y 2x("\'2D\' 5p be a 1n")}u 1i=O;u 1A=O;if(H(4B)!="L"){1i=5q;1A=4B;if(H(4B)=="1n"){if(H(5q[4B])!="G"){14 Y 2x("\'bp\' 5p be a G on \'gc\'")}}N{if(H(4B)!="G"){14 Y 2x("\'bp\' 5p be a G or 1n")}}}N{if(H(5q)!="G"){14 Y 2x("\'gc\' 5p be a G if \'bp\' is 2E n3")}N{1A=5q}}if(H(1i)=="L"||1i===O){1i=1d}u bm=!!(1d.bo||1d.bn);u 8m=I.gb(1d,1A,1i,bm);if(1d.bo){1d.bo(2D.3H(2),8m,1m)}N{if(1d.bn){1d.bn(2D,8m)}}u bk=[1d,2D,8m,bm,5q,4B];I.4x.1c(bk);F bk},6t:G(6u){if(!6u[3]){F}u 1d=6u[0];u 2D=6u[1];u bj=6u[2];if(1d.ga){1d.ga(2D.3H(2),bj,1m)}N{if(1d.g9){1d.g9(2D,bj)}N{14 Y 2x("\'1d\' 5p be a S n0")}}},8j:G(bh){u I=B.1u;u 5o=I.4x;u m=B.J;if(M.K>1){u 1d=B.S.1E(M[0]);u 2D=M[1];u 1i=M[2];u 1A=M[3];R(u i=5o.K-1;i>=0;i--){u o=5o[i];if(o[0]===1d&&o[1]===2D&&o[4]===1i&&o[5]===1A){I.6t(o);5o.4y(i,1);F 1h}}}N{u 5n=m.bi(5o,bh);if(5n>=0){I.6t(bh);5o.4y(5n,1);F 1h}}F 1m},8i:G(1d,2D){1d=B.S.1E(1d);u m=B.J;u 8l=m.bg(m.1R(O,M,1));u I=B.1u;u bd=I.6t;u 4z=I.4x;if(8l.K===0){R(u i=4z.K-1;i>=0;i--){u 4A=4z[i];if(4A[0]===1d){bd(4A);4z.4y(i,1)}}}N{u bf={};R(u i=0;i<8l.K;i++){bf[8l[i]]=1h}R(u i=4z.K-1;i>=0;i--){u 4A=4z[i];if(4A[0]===1d&&4A[1]in bf){bd(4A);4z.4y(i,1)}}}},8h:G(1d,2D){u bc=B.1u.4x;1d=B.S.1E(1d);u 3G=B.J.1R(O,M,2);u 5m=[];R(u i=0;i<bc.K;i++){u 8k=bc[i];if(8k[0]===1d&&8k[1]===2D){1f{8k[2].1w(1d,3G)}1e(e){5m.1c(e)}}}if(5m.K==1){14 5m[0]}N{if(5m.K>1){u e=Y 2x("mZ bb mY in mX \'2D\', mW bb mV");e.bb=5m;14 e}}}});B.1u.1W=[];B.1u.1z=["6s","8j","8h","8i"];B.1u.2d=G(2m){u m=B.J;D.1Z=2v;D.3X=2m;1f{D.6s(2O,"g8",D.g7)}1e(e){}D.2k={":3e":D.1z,":1p":m.2o(D.1z,D.1W)};m.3f(D)};B.1u.2d(D);if(!B.3d){6s=B.1u.6s;8j=B.1u.8j;8i=B.1u.8i;8h=B.1u.8h}B.J.2Y(D,B.1u);if(H(1q)!="L"){1q.2X("B.1X");1q.2M("B.J");1q.2M("B.S");1q.2M("B.V")}if(H(1x)!="L"){1x.26("B.J",[]);1x.26("B.S",[]);1x.26("B.V",[])}1f{if(H(B.J)=="L"||H(B.S)=="L"||H(B.V)=="L"){14""}}1e(e){14"B.1X 3F on B.J, B.S 3W B.V!"}if(H(B.1X)=="L"){B.1X={}}B.1X.1r="B.1X";B.1X.1Y="1.3.1";B.1X.1K=G(){F"["+D.1r+" "+D.1Y+"]"};B.1X.1l=G(){F D.1K()};B.1X.aI=G(e,g6){e=B.S.1E(e);D.fN(g6);if(D.1S.fL){e=D.g5(e)}u 4w=D.1S.3U;u C=B.V.V;if(D.1S.3U=="aW"){4w=C.ba(e)}N{if(!(4w 2C C)){4w=C.8f(4w)}}D.82=(4w.6r().a<=0);u 5l=D.1S.aV;if(D.1S.aV=="fM"){5l=C.ba(e.8g)}N{if(!(5l 2C C)){5l=C.8f(5l)}}D.g3(e,4w,5l)};B.1X.aI.1U={g5:G(e){u mU=e.3t;u 1N=B.S.b9();if(H(1N.5k)=="L"||1N.5k===O){F e}u 4v=1N.5k.g4(e,O);if(H(4v)=="L"||4v===O){F e}u b8=B.S.6m({"1T":{3u:"8c",mT:4v.6q("6p-3D"),85:4v.6q("6p-3g"),mS:4v.6q("6p-6f"),86:4v.6q("6p-2I"),6p:"2N"}});b8.6o=e.6o;e.6o="";e.2c(b8);F e},g3:G(e,b7,8e){if(D.1S.3E){D.g2(e,8e)}if(D.fy()){D.fX(e,b7,8e)}if(D.fx()){D.fV(e,b7,8e)}},g2:G(el,g1){u b6="6l 8a "+D.aQ(g1);u g0="3E-2I: "+b6;u fZ="3E-3g: "+b6;u fY="1T=\'"+g0+";"+fZ+"\'";el.6o="<4u "+fY+">"+el.6o+"</4u>"},fX:G(el,fW,b5){u b4=D.b1(b5);R(u i=0;i<D.1S.89;i++){b4.2c(D.b0(fW,b5,i,"3D"))}el.1T.mR=0;el.mQ(b4,el.6n)},fV:G(el,fU,b3){u b2=D.b1(b3);R(u i=(D.1S.89-1);i>=0;i--){b2.2c(D.b0(fU,b3,i,"6f"))}el.1T.mP=0;el.2c(b2)},b1:G(fT){u 2q=B.S;F 2q.6m({1T:{aZ:fT.1l()}})},b0:G(aY,fQ,n,aX){u 6k=B.S.8d();u 2p=6k.1T;2p.aZ=aY.1l();2p.3u="8c";2p.3V="6l";2p.fS="fR";2p.mO="6l";u 8b=D.aQ(aY,fQ);if(D.1S.3E&&n===0){2p.mN="8a";2p.mM="6l";2p.84="2N";2p.83="2N";2p.mL="2N";2p.3V="2N";2p.fP=8b.1l()}N{if(8b){2p.fP=8b.1l();2p.mK="8a";2p.mJ="2N 6l"}}if(!D.1S.4r&&(n==(D.1S.89-1))){2p.3V="fO"}D.fI(6k,n,aX);D.fG(6k,n,aX);F 6k},fN:G(fK){D.1S={6g:"1p",3U:"aW",aV:"fM",5j:1h,3E:1m,4r:1m,fL:1m};B.J.2l(D.1S,fK);D.1S.89=(D.1S.4r?2:4)},aL:G(){u 88=D.1S.6g;if(D.6h(88,"1p","3D")){F""}u aU=(88.2A("tl")!=-1);u aT=(88.2A("tr")!=-1);if(aU&&aT){F""}if(aU){F"2I"}if(aT){F"3g"}F""},aK:G(){u 87=D.1S.6g;if(D.6h(87,"1p","6f")){F""}u aS=(87.2A("bl")!=-1);u aR=(87.2A("br")!=-1);if(aS&&aR){F""}if(aS){F"2I"}if(aR){F"3g"}F""},aQ:G(aN,aO){if(aN=="aP"){F aO}N{if(D.1S.3E){F D.1S.3E}N{if(D.1S.5j){F aO.fJ(aN)}}}F""},fI:G(el,n,fH){u 6j=D.fE(n)+"px";u aM=(fH=="3D"?D.aL():D.aK());u 4t=el.1T;if(aM=="2I"){4t.86=6j;4t.85="2N"}N{if(aM=="3g"){4t.85=6j;4t.86="2N"}N{4t.86=6j;4t.85=6j}}},fG:G(el,n,fF){u 6i=D.fz(n)+"px";u aJ=(fF=="3D"?D.aL():D.aK());u 4s=el.1T;if(aJ=="2I"){4s.84=6i;4s.83="2N"}N{if(aJ=="3g"){4s.83=6i;4s.84="2N"}N{4s.84=6i;4s.83=6i}}},fE:G(n){if(D.82){F 0}u o=D.1S;if(o.4r&&o.5j){u fD=[1,0];F fD[n]}N{if(o.4r){u fC=[2,1];F fC[n]}N{if(o.5j){u fB=[3,2,1,0];F fB[n]}N{u fA=[5,3,2,1];F fA[n]}}}},fz:G(n){u o=D.1S;u 5i;if(o.4r&&(o.5j||D.82)){F 1}N{if(o.4r){5i=[1,0]}N{if(o.5j){5i=[2,1,1,1]}N{if(o.3E){5i=[0,2,0,0]}N{if(D.82){5i=[5,3,2,1]}N{F 0}}}}}F 5i[n]},6h:G(1y){R(u i=1;i<M.K;i++){if(1y.2A(M[i])!=-1){F 1h}}F 1m},fy:G(){F D.6h(D.1S.6g,"1p","3D","tl","tr")},fx:G(){F D.6h(D.1S.6g,"1p","6f","bl","br")},mI:G(el){F(el.5h.K==1&&el.5h[0].3T==3)}};B.1X.aF=G(e,fw){Y B.1X.aI(e,fw)};B.1X.fs=G(fv,fu,ft){u aG=B.S.aH(fv,fu);R(u i=0;i<aG.K;i++){B.1X.aF(aG[i],ft)}};B.1X.V=B.V.V;B.1X.mH=B.S.4q;B.1X.2d=G(){u m=B.J;m.3f(D);D.2k={":3e":D.1z,":1p":m.2o(D.1z,D.1W)}};B.1X.1z=["aF","fs"];B.1X.1W=[];B.1X.2d();B.J.2Y(D,B.1X);if(H(B)=="L"){B={}}if(H(B.B)=="L"){B.B={}}B.B.1r="B.B";B.B.1Y="1.3.1";B.B.1K=G(){F"["+D.1r+" "+D.1Y+"]"};B.B.1l=G(){F D.1K()};B.B.aA=["J","15","1H","1D","1s","1k","S","1I","V","1u","1X"];if(H(1x)!="L"||H(1q)!="L"){if(H(1q)!="L"){1q.2X("B.B");1q.2M("B.*")}if(H(1x)!="L"){1x.26("B.J",[]);1x.26("B.15",[]);1x.26("B.1H",[]);1x.26("B.1D",[]);1x.26("B.1s",[]);1x.26("B.1k",[]);1x.26("B.S",[]);1x.26("B.1I",[]);1x.26("B.V",[]);1x.26("B.1u",[]);1x.26("B.1X",[])}(G(){u 6e=B.J.1R;u I=B.B;u aE=I.aA;u aD=[];u aC=[];u 81={};u i,k,m,1p;R(i=0;i<aE.K;i++){m=B[aE[i]];6e(aD,m.1z);6e(aC,m.1W);R(k in m.2k){81[k]=6e(81[k],m.2k[k])}1p=m.2k[":1p"];if(!1p){1p=6e(O,m.1z,m.1W)}u j;R(j=0;j<1p.K;j++){k=1p[j];I[k]=m[k]}}I.1z=aD;I.1W=aC;I.2k=81}())}N{if(H(B.3d)=="L"){B.3d=1h}(G(){u 80=2v.fr("7W");u ay="fq://fp.mG.fo/mF/mE/mD.is.aB.mC";u 2w=O;u ax=O;u az={};u i;R(i=0;i<80.K;i++){u 1d=80[i].fm("1d");if(!1d){2V}az[1d]=1h;if(1d.3C(/B.js$/)){2w=1d.2W(0,1d.mB("B.js"));ax=80[i]}}if(2w===O){F}u 6d=B.B.aA;R(u i=0;i<6d.K;i++){if(B[6d[i]]){2V}u 7Y=2w+6d[i]+".js";if(7Y in az){2V}if(2v.7Z&&2v.7Z.mA==ay){u s=2v.mz(ay,"7W");s.4p("id","my"+2w+6d[i]);s.4p("1d",7Y);s.4p("1J","mx/x-fk");ax.3t.2c(s)}N{2v.fl("<7W 1d=\\""+7Y+"\\" 1J=\\"7X/fk\\"></7W>")}}})()}',62,1976,'||||||||||||||||||||||||||||||var|||||||MochiKit||this||return|function|typeof|self|Base|length|undefined|arguments|else|null||elem|for|DOM||repr|Color|rval|res|new||||||throw|Iter|||||next|name|push|src|catch|try|lst|true|obj|node|Async|toString|false|string|hue|all|dojo|NAME|Format|msg|Signal|red|apply|JSAN|str|EXPORT|func|rgb|_425|DateTime|getElement|blue|hsl|Logging|LoggingPane|type|__repr__|_event|while|doc|bind|num|iter|extend|options|style|prototype|seq|EXPORT_OK|Visual|VERSION|_document||_434||replace|forwardCall|StopIteration|use||Math|max|min|join|appendChild|__new__|button|compare|date|key|val|_329|EXPORT_TAGS|update|win|pair|concat|_596|dom|map|req|Deferred|sync|document|base|Error|number|partial|indexOf||instanceof|sig|not|cls|list|fired|left|stop|break|logger|require|0px|window|shift|hsv|split|createElement|_423|callee|continue|substring|provide|_exportSymbols|ccc||_464|||||||||step|pred|_51|__compat__|common|nameFunctions|right|255|_517|case|100|_loggingPane|value|object|callback|TypeError|_251|_246|_113|parentNode|display|_522|parseInt|cssText|wrap|info|isArrayLike|end|match|top|border|depends|args|substr|mouse|code|_519|_443|className|level|err|frac|Date|_135|_85|nodeType|color|height|and|_window|fromRGB|charAt||asHSL|_444|message||||filter||LogMessage|AdapterRegistry|_366|imap|NotFound|locked|counter|_262|_messages|operator|cmp|_165|_161|pairs|arr|_52|setAttribute|computedStyle|compact|_614|_610|div|_576|_572|_observers|splice|_565|_566|_555|scrollTop|page|modifier|white|_541|fromHSL|_539|_535|_528|clone|parseFloat|_505|pre|_499|_497|_427|createTextNode|_446|attributeArray|_388|_379|updateNodeAttributes|_341|_326||box|errback|results|paused|chain|_285||ofs||NamedError|_175|_147|_122|_83|_54|_17|childNodes|_619|blend|defaultView|_574|_569|idx|_562|must|_554|_specialKeys|body|Coordinates|registerComparator|_521|_516|hex|mid|_478|width|loggingPane|LogLevel|nwin|head|url|setElementClass|callStack|path|dest|_359|boolean|register|Dimensions|DeferredLock|_313|addCallback|_310|waiting|onreadystatechange|_290|LOCALE|year|printfire|_214|log|_213|_211|pos|_155|_153||typeMatcher|listMinMax|_114|_40|itr|typ|_19|_634|_625|bottom|corners|_hasString|_612|_608|_595|1px|DIV|firstChild|innerHTML|padding|getPropertyValue|asRGB|connect|_disconnect|_559|middle|which|clientY|scrollLeft|clientX|client|charCode|relatedTarget|event|toColorPart|clampColorComponent|_537|_534|toFixed|_468|buildAndApplyFilter|_442|_441|_440|_439|position|_463|_447|removeChild|_449|uid|_428|_426|compliant|attributes|_422|_409|_412|_400|_395|_390|_389|_377|_375|_363|attr|ctx|repeat|_340|_339|isNotEmpty|_335|_333|opera|DeferredList|ret|_309|silentlyCancelled|canceller|_nextId|Array|_293|XMLHttpRequest|chained|_281|tail|_252|_225|msec|day|month|iso|Logger|_208|listeners|_200|_198|_194|_196|reduce|range|_169|_162|truth|registerRepr|_121|_70|_58|_56|_47|_45|_41|_13|_1|script|text|uri|documentElement|_630|_629|isTransparent|borderRightWidth|borderLeftWidth|marginRight|marginLeft|_602|_599|numSlices|solid|_597|block|SPAN|_579|fromString|offsetParent|signal|disconnectAll|disconnect|_570|_563|_557|preventDefault|stopPropagation|clientTop|clientLeft|pageY|pageX|keyCode|meta|ctrl|alt|target|black|_532|_524|floor|_513|_512|_500|_495|toLowerCase|_487|DEBUG|INFO|WARNING|FATAL|ERROR|colorTable|logFont|closed|inline|onclick|_438|_437|_445|RegExp|_452|space|title|updatetree|||||withDocument|withWindow||setDisplayForElement|none|renames|forEach|domConverters|escapeHTML|addElementClass|removeElementClass|once|_378|_380|_376|appendChildNodes|coerceToDOM|_355|opt|clientWidth|opacity|GenericError|fail|resultList|_307|_301|_fire|can|addCallbacks|_resback|percent|decimal|separator|twoDigitFloat|_274|_273|_264|_257|_250|_249|_254|_248|_243|_242|fmt|_240|_245|getTime|sec|hour|_209|slice|_206|iterateNextIter|registerIteratorFactory|arrayLikeIter|iteratorRegistry|takewhile|ifilterfalse|ifilter|_181|_176|_168|_166|_159|_tee|deque|arg|fun|jsonRegistry|reprString|reprRegistry|comparatorRegistry|urlEncode|_110|_108|cur|_95|_87|_71|im_preargs||_53|_57|_46|present|like|array|Argument|_15|_12|_632|_631|_633|SUBMODULES|only|_628|_627|_626|roundElement|_624|getElementsByTagAndClassName|_RoundCorners|_613|_whichSideBottom|_whichSideTop|_609|_605|_606|transparent|_borderColor|_604|_603|_601|_600|bgColor|fromElement|_594|_592|backgroundColor|_createCornerSlice|_createCorner|_590|_589|_587|_586|_581|_578|_577|currentDocument|fromBackground|errors|_568|_564||sigs|flattenArguments|_561|findIdentical|_560|_558||_556|attachEvent|addEventListener|funcOrStr|Event||_548|fromCharCode|String|_specialMacKeys|any|green|_namedColors|hsvToRGB|rgbToHSV|hslToRGB|rgbToHSL|_542|01|360|_fromColorString|_540|_536|_538|_529|_523|_518|fromComputedStyle|_511|_507|_508|_506|_501|fromHexString|_498|_496|_486|__class__|createLoggingPane|_459|_461|font|_462|_430|_435|1000|index|_460|getMessages|removeListener|_451||_457|_450|infore|_448|_456|logDebug|offsetHeight|span|input|_436|TR||HTML|open|alert|currentWindow|swapDOM|SELECT|FORM|INPUT|createDOMFunc|ignoreAttr|_421|call|_417|_410|_415|nodeName|_414|_413|emitHTML|good|_406|_399|_397|_393|_392|addLoadEvent|addToCallStack|_387|_386|_381|_382|_383|_373|_372|_369|createDOM|_365|Function|_360|_362|_358|_344|nodeWalk|formContents|_337|_338|_334|_332|offsetTop|offsetLeft|visibility|parentElement|||XMLHttpRequestError|BrowserComplianceError|CancelledError|AlreadyCalledError|evalJSONRequest|sendXMLHttpRequest|wait|doSimpleXMLHttpRequest|getXMLHttpRequest|succeed|_312|finishedCount|_308|_cbDeferred|_303|_297|queryString|_nothing|_289|XMLHTTP|ActiveXObject|eval|_284|_check|error|_279|default|rstrip|lstrip|formatLocale|roundToFixed|truncToFixed|_276|pow|_272|_271|_270|sign|_265|_263|tmp|_238|_232|toISODate|toISOTime|getFullYear|getDate|getMonth|_230|_padTwo|_228|useNativeConsole|_212|compareLogMessage|isLogMessage|unshift|_207||maxSize|_202|_199|logLevelAtLeast|console|hasIterateNext|iterateNext|arrayLike|groupby||exhaust|tee|dropwhile|applymap||islice|izip|cycle|count||_189|_188|_183|_185|_184|_186|_187|_182|identity|fetch|_180|_177|listMin|reprNumber|reprArrayLike|compareArrayLike|compareDateLike|isDateLike|findValue|_128|__export__|keyComparator|_124|_118|_93|_94|_90|_88|_84|_77|_68|_67|_66|_65|_60|im_func|_55|im_self|_48|_44|_42|_39|_36|_33|_27|_26|_25|_22|_24|_20|javascript|write|getAttribute||org|www|http|getElementsByTagName|roundClass|_623|_622|_621|_620|_isBottomRounded|_isTopRounded|_borderSize|_618|_617|_616|_615|_marginSize|_611|_setBorder|_607|_setMargin|blendedColor|_598|__unstable__wrapElement|fromParent|_setOptions|2px|borderColor|_593|hidden|overflow|_591|_588|_roundBottomCorners|_585|_roundTopCorners|_584|_583|_582|_580|_renderBorder|_roundCornersImpl|getComputedStyle|_doWrap|_571|_unloadCache|onunload|detachEvent|removeEventListener|_listener|objOrFunc|_552||_551|_549|onload|delete|112|KEY_F|KEY_|MINUS|KEY_SEMICOLON|KEY_DELETE|KEY_INSERT|KEY_ARROW_DOWN|KEY_ARROW_RIGHT|KEY_ARROW_UP||KEY_ARROW_LEFT|KEY_HOME|KEY_END|KEY_PAGE_DOWN|KEY_PAGE_UP|KEY_ENTER|KEY_NUM_PAD_CLEAR|63236|mousemove|contextmenu|click|mouseout|mouseover|_src|yellow|708090|purple|orange|ff00ff|magenta|778899|d3d3d3|808080|gray|696969|2f4f4f|darkred|a9a9a9|00ffff|cyan|brown|_547|_546||||compareRGB|_545||_543|fromHSLString|fromRGBString|round|_533|_hslValue|switch|background|_503|_504||fromName|_488|col|toRGBString|_hexString|_rgbString|_hslString|toPrecision|isLight||_481|_477|_476|_475|_474|_473|_469|_466|closePane|_458|onkeypress|_454|addListener|_455|close|test|scrollHeight|option|word|moz|_431|getElementById|html|pop|200|_|removeElement|showElement|hideElement|CANVAS|STRONG|FIELDSET|LEGEND|OPTGROUP|OPTION|TEXTAREA|LABEL|HR|BR|H3|H2|H1|PRE|TT|BUTTON|IMG|TH||TABLE||TFOOT|THEAD|TBODY|TD|LI|OL|||UL|checked|class|ignoreAttrFilter||_424|_419|nodeValue|scrapeText|_416|_418|sort|_411|toHTML|_404|hasElementClass|_403|_402|_401|swapElementClass|_398|_394|toggleElementClass|_391|focusOnLoad|_newCallStack|currentStyle|_371|replaceChildNodes|_364|_361|getNodeAttribute|_357|setNodeAttribute|_354|_352|_350|_353|toDOM|_346|_345|registerDOMConverter|selectedIndex|setElementPosition|setElementDimensions|tagName|absolute|getBoxObjectFor|getBoundingClientRect|elementPosition|_325|_324|_322|_323|offsetWidth|elementDimensions|clientHeight|innerWidth|getViewportDimensions|setOpacity|status|_317|deferred|_316|_newNamedError|maybeDeferred||gatherResults|callLater|loadJSONDoc|_311|consumeErrors|fireOnOneErrback|fireOnOneCallback|addErrback|_305|_304|_306|unlocked|release|_300|_299|_298|_296|_xhr_onreadystatechange|_xhr_canceller|304|responseText|Msxml2|addBoth|_pause|_continue|result|the|are|they|instances|_unpause|cancel|_280|_278|en_US|strip|percentFormat|twoDigitAverage|numberFormatter|_277|_275|isNaN|_259|_258|_260|_255|_253|_numberFormatter|_241|_239|_237|_236|_235|_234|_233|_231|toAmericanDate|toPaddedAmericanDate|americanDate|toISOTimestamp|isoTimestamp|isoDate|foot|sep||60000|_221|_isoRegexp|dispatchEvent|createEvent|warning|logWarning|fatal|logFatal|debug|logError|baseLog|_210|getMessageText|logToConsole|dispatchListeners|_204|_203|ident|_201|postError|alertListener|_197|_192|groupby_as_array|iextend|some|reversed|sorted|every|sum|_190|eat|_174|_173|_172|_171|_167|_163|_158|_157|_151|_144|_141||_139|_136|_134||_133|_132|zip|merge|isUndefined|isCallable|listMax|_131|_130|encodeURIComponent||_127|method|parseQueryString|evalJSON|registerJSON|serializeJSON|objMin|objMax|reverseKeyComparator|arrayEqual|objEqual|bindMethods|xfilter|xmap|isEmpty|isNull|isUndefinedOrNull|itemgetter|items|keys|setdefault|_126|_120|decodeURIComponent|_119|len|_109|_107|_104|_105|_101|_102|_98|||_100|_97|_96|_91|json|__json__|_82|_81|_80|_79|_76||_75|_74|_73|_69|_primitives|_64|_63||_62|_61|_59|_wrapDumbFunction|_49|_50|_31|_30|_21|_7|application|MochiKit_|createElementNS|namespaceURI|lastIndexOf|xul|there|gatekeeper|keymaster|mozilla|getElementsComputedStyle|_hasSingleTextChild|borderWidth|borderStyle|borderBottomWidth|borderTopWidth|borderTopStyle|fontSize|paddingBottom|insertBefore|paddingTop|marginBottom|marginTop|_575|property|see|handling|thrown|Multiple|element|||given|123|KEY_NUM_PAD_|105|KEY_APOSTROPHE|222|KEY_RIGHT_SQUARE_BRACKET|221|KEY_REVERSE_SOLIDUS|220|KEY_LEFT_SQUARE_BRACKET||219|KEY_GRAVE_ACCENT|192|KEY_SOLIDUS|191|KEY_FULL_STOP|190|KEY_HYPHEN|189||KEY_COMMA|188|KEY_EQUALS_SIGN|187|186|KEY_SCROLL_LOCK|145|KEY_NUM_LOCK|144|KEY_NUM_PAD_SOLIDUS|111|KEY_NUM_PAD_FULL_STOP|110|KEY_NUM_PAD_HYPHEN|109|KEY_NUM_PAD_PLUS_SIGN|107|KEY_NUM_PAD_ASTERISK|106|KEY_SELECT|KEY_WINDOWS_RIGHT|KEY_WINDOWS_LEFT|KEY_PRINT_SCREEN|KEY_SPACEBAR|KEY_ESCAPE|KEY_CAPS_LOCK|KEY_PAUSE|KEY_ALT|KEY_CTRL|KEY_SHIFT|KEY_TAB|KEY_BACKSPACE|63242|63272|63302|63233|63235|63232|63234|63273|63275|63277|63276|63289|returnValue|cancelBubble|keypress|KEY_UNKNOWN|keyup|keydown|shiftKey|metaKey||ctrlKey|altKey|toElement|srcElement|9acd32||yellowgreen||ffff00|f5f5f5|whitesmoke||ffffff|f5deb3|wheat|ee82ee|violet|40e0d0|turquoise|ff6347|tomato|d8bfd8|thistle|008080|teal|d2b48c|tan|4682b4|steelblue|00ff7f|springgreen|fffafa|snow|slategrey|slategray|6a5acd|slateblue|87ceeb|skyblue|c0c0c0|silver|a0522d|sienna|fff5ee|seashell|2e8b57|seagreen|f4a460|sandybrown|fa8072|salmon|8b4513|saddlebrown|4169e1|royalblue|bc8f8f|rosybrown|ff0000|800080|b0e0e6|powderblue|dda0dd|plum|ffc0cb|pink|cd853f||peru|ffdab9|peachpuff|ffefd5|papayawhip|db7093|palevioletred|afeeee|paleturquoise|98fb98|palegreen|eee8aa||palegoldenrod|da70d6|orchid|ff4500|orangered|ffa500|6b8e23|olivedrab|808000|olive|fdf5e6|oldlace|000080|navy|ffdead|navajowhite|ffe4b5|moccasin|ffe4e1|mistyrose|f5fffa|mintcream|191970|midnightblue|c71585|mediumvioletred|48d1cc|mediumturquoise|00fa9a|mediumspringgreen|7b68ee|mediumslateblue|3cb371|mediumseagreen|9370db|mediumpurple|ba55d3|mediumorchid|0000cd|mediumblue|66cdaa|mediumaquamarine|800000|maroon|faf0e6|linen|32cd32|limegreen|00ff00|lime|ffffe0|lightyellow|b0c4de|lightsteelblue|lightslategrey|lightslategray||87cefa|lightskyblue|20b2aa|lightseagreen|ffa07a|lightsalmon|ffb6c1|lightpink|lightgrey|90ee90|lightgreen|lightgray|fafad2|lightgoldenrodyellow|e0ffff|lightcyan|f08080|lightcoral|add8e6|lightblue|fffacd|lemonchiffon|7cfc00|lawngreen|fff0f5|lavenderblush|e6e6fa|lavender|f0e68c|khaki|fffff0|ivory|4b0082|indigo|cd5c5c|indianred|ff69b4|hotpink|f0fff0|honeydew|grey|adff2f|greenyellow|008000|daa520|goldenrod|ffd700||gold|f8f8ff|ghostwhite|dcdcdc|gainsboro|fuchsia|228b22|forestgreen|fffaf0|floralwhite|b22222|firebrick|1e90ff|dodgerblue|dimgrey|dimgray|00bfff|deepskyblue|ff1493|deeppink|9400d3|darkviolet|00ced1|darkturquoise|darkslategrey|darkslategray|483d8b|darkslateblue|8fbc8f|darkseagreen|e9967a|darksalmon|8b0000|9932cc|darkorchid|ff8c00|darkorange|556b2f|darkolivegreen|8b008b|darkmagenta|bdb76b|darkkhaki|darkgrey|006400|darkgreen|darkgray|b8860b|darkgoldenrod|008b8b|darkcyan|00008b|darkblue|dc143c|crimson|fff8dc|cornsilk|6495ed|cornflowerblue|ff7f50|coral|d2691e||chocolate|7fff00|chartreuse|5f9ea0|cadetblue|deb887|burlywood|a52a2a|8a2be2|blueviolet|0000ff|ffebcd||blanchedalmond|000000|ffe4c4|bisque|f5f5dc|beige|f0ffff|azure|7fffd4|aquamarine|aqua|faebd7|antiquewhite|f0f8ff|aliceblue|lightGray|darkGray|namedColors|blackColor|fromText|whiteColor|_510|_509|PI|rad|deg|transparentColor|_494|_493|_492|fromHSV|_491|_490|_489|asHSV|toHexString|rgba|hsla|toHSLString|isDark|lighterColorWithLevel|darkerColorWithLevel|colorWithLightness|colorWithSaturation|colorWithHue|colorWithAlpha||serif|sans|Verdana||8pt|8em|auto||Close|Clear||Load|Filter||10em||fixed|regex|emergency|line|margin|_Listener|dtd|loose|html4|w3|EN|Transitional|DTD|W3C|PUBLIC|DOCTYPE|blocking|due|debugging|able|Not|resizable|dependent|href|location|_MochiKit_LoggingPane|_429|canvas|strong|fieldset|legend|optgroup|select|form|textarea|label|img|table|tfoot|thead|tbody|htmlFor||useMap|usemap|defaultChecked|hasChildNodes|quot|amp|_405|focus|replaceChild|checkbox||radio|_win|BODY||safari|version|userAgent|navigator|innerHeight|alpha|khtml|Tried|acquire|clearTimeout|setTimeout|GET|ignore|send|abort|failed|Request|readyState|support|does|Browser|Microsoft|_288|_287|used|Deferreds|Chained|success|unfired|fr_FR|de_DE|00|abs|search|pattern|Invalid|getTimezoneOffset|getSeconds|getMinutes|getHours|UTC|3600000|initEvent|Events|debuggingBookmarklet|MESSAGES|LAST|_205|clear|ninfo|nlevel|timestamp|reverse|takes|initial|with|sequence|empty|iterable|numbers|dateLike|escape|find|forward|unregister|unescape|Object|compared|item|contains|logor|logand|cle|clt|cge|cgt|cne|ceq|zrshift|rshift|lshift|xor|mul|mod|sub|add|neg|lognot|_9|_2'.split('|'),0,{}) + + +/* + * jQuery 1.2.1 - New Wave Javascript + * + * Copyright (c) 2007 John Resig (jquery.com) + * Dual licensed under the MIT (MIT-LICENSE.txt) + * and GPL (GPL-LICENSE.txt) licenses. + * + * $Date: 2007-09-16 23:42:06 -0400 (Sun, 16 Sep 2007) $ + * $Rev: 3353 $ + */ + +var decompressedJQuery = function(p,a,c,k,e,r){e=function(c){return(c<a?'':e(parseInt(c/a)))+((c=c%a)>35?String.fromCharCode(c+29):c.toString(36))};if(!''.replace(/^/,String)){while(c--)r[e(c)]=k[c]||e(c);k=[function(e){return r[e]}];e=function(){return'\\w+'};c=1};while(c--)if(k[c])p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c]);return p}('(G(){9(1m E!="W")H w=E;H E=18.15=G(a,b){I 6 7u E?6.5N(a,b):1u E(a,b)};9(1m $!="W")H D=$;18.$=E;H u=/^[^<]*(<(.|\\s)+>)[^>]*$|^#(\\w+)$/;E.1b=E.3A={5N:G(c,a){c=c||U;9(1m c=="1M"){H m=u.2S(c);9(m&&(m[1]||!a)){9(m[1])c=E.4D([m[1]],a);J{H b=U.3S(m[3]);9(b)9(b.22!=m[3])I E().1Y(c);J{6[0]=b;6.K=1;I 6}J c=[]}}J I 1u E(a).1Y(c)}J 9(E.1n(c))I 1u E(U)[E.1b.2d?"2d":"39"](c);I 6.6v(c.1c==1B&&c||(c.4c||c.K&&c!=18&&!c.1y&&c[0]!=W&&c[0].1y)&&E.2h(c)||[c])},4c:"1.2.1",7Y:G(){I 6.K},K:0,21:G(a){I a==W?E.2h(6):6[a]},2o:G(a){H b=E(a);b.4Y=6;I b},6v:G(a){6.K=0;1B.3A.1a.16(6,a);I 6},N:G(a,b){I E.N(6,a,b)},4I:G(a){H b=-1;6.N(G(i){9(6==a)b=i});I b},1x:G(f,d,e){H c=f;9(f.1c==3X)9(d==W)I 6.K&&E[e||"1x"](6[0],f)||W;J{c={};c[f]=d}I 6.N(G(a){L(H b 1i c)E.1x(e?6.R:6,b,E.1e(6,c[b],e,a,b))})},17:G(b,a){I 6.1x(b,a,"3C")},2g:G(e){9(1m e!="5i"&&e!=S)I 6.4n().3g(U.6F(e));H t="";E.N(e||6,G(){E.N(6.3j,G(){9(6.1y!=8)t+=6.1y!=1?6.6x:E.1b.2g([6])})});I t},5m:G(b){9(6[0])E(b,6[0].3H).6u().3d(6[0]).1X(G(){H a=6;1W(a.1w)a=a.1w;I a}).3g(6);I 6},8m:G(a){I 6.N(G(){E(6).6q().5m(a)})},8d:G(a){I 6.N(G(){E(6).5m(a)})},3g:G(){I 6.3z(1q,Q,1,G(a){6.58(a)})},6j:G(){I 6.3z(1q,Q,-1,G(a){6.3d(a,6.1w)})},6g:G(){I 6.3z(1q,P,1,G(a){6.12.3d(a,6)})},50:G(){I 6.3z(1q,P,-1,G(a){6.12.3d(a,6.2q)})},2D:G(){I 6.4Y||E([])},1Y:G(t){H b=E.1X(6,G(a){I E.1Y(t,a)});I 6.2o(/[^+>] [^+>]/.14(t)||t.1g("..")>-1?E.4V(b):b)},6u:G(e){H f=6.1X(G(){I 6.67?E(6.67)[0]:6.4R(Q)});H d=f.1Y("*").4O().N(G(){9(6[F]!=W)6[F]=S});9(e===Q)6.1Y("*").4O().N(G(i){H c=E.M(6,"2P");L(H a 1i c)L(H b 1i c[a])E.1j.1f(d[i],a,c[a][b],c[a][b].M)});I f},1E:G(t){I 6.2o(E.1n(t)&&E.2W(6,G(b,a){I t.16(b,[a])})||E.3m(t,6))},5V:G(t){I 6.2o(t.1c==3X&&E.3m(t,6,Q)||E.2W(6,G(a){I(t.1c==1B||t.4c)?E.2A(a,t)<0:a!=t}))},1f:G(t){I 6.2o(E.1R(6.21(),t.1c==3X?E(t).21():t.K!=W&&(!t.11||E.11(t,"2Y"))?t:[t]))},3t:G(a){I a?E.3m(a,6).K>0:P},7c:G(a){I 6.3t("."+a)},3i:G(b){9(b==W){9(6.K){H c=6[0];9(E.11(c,"24")){H e=c.4Z,a=[],Y=c.Y,2G=c.O=="24-2G";9(e<0)I S;L(H i=2G?e:0,33=2G?e+1:Y.K;i<33;i++){H d=Y[i];9(d.26){H b=E.V.1h&&!d.9V["1Q"].9L?d.2g:d.1Q;9(2G)I b;a.1a(b)}}I a}J I 6[0].1Q.1p(/\\r/g,"")}}J I 6.N(G(){9(b.1c==1B&&/4k|5j/.14(6.O))6.2Q=(E.2A(6.1Q,b)>=0||E.2A(6.2H,b)>=0);J 9(E.11(6,"24")){H a=b.1c==1B?b:[b];E("9h",6).N(G(){6.26=(E.2A(6.1Q,a)>=0||E.2A(6.2g,a)>=0)});9(!a.K)6.4Z=-1}J 6.1Q=b})},4o:G(a){I a==W?(6.K?6[0].3O:S):6.4n().3g(a)},6H:G(a){I 6.50(a).28()},6E:G(i){I 6.2J(i,i+1)},2J:G(){I 6.2o(1B.3A.2J.16(6,1q))},1X:G(b){I 6.2o(E.1X(6,G(a,i){I b.2O(a,i,a)}))},4O:G(){I 6.1f(6.4Y)},3z:G(f,d,g,e){H c=6.K>1,a;I 6.N(G(){9(!a){a=E.4D(f,6.3H);9(g<0)a.8U()}H b=6;9(d&&E.11(6,"1I")&&E.11(a[0],"4m"))b=6.4l("1K")[0]||6.58(U.5B("1K"));E.N(a,G(){H a=c?6.4R(Q):6;9(!5A(0,a))e.2O(b,a)})})}};G 5A(i,b){H a=E.11(b,"1J");9(a){9(b.3k)E.3G({1d:b.3k,3e:P,1V:"1J"});J E.5f(b.2g||b.6s||b.3O||"");9(b.12)b.12.3b(b)}J 9(b.1y==1)E("1J",b).N(5A);I a}E.1k=E.1b.1k=G(){H c=1q[0]||{},a=1,2c=1q.K,5e=P;9(c.1c==8o){5e=c;c=1q[1]||{}}9(2c==1){c=6;a=0}H b;L(;a<2c;a++)9((b=1q[a])!=S)L(H i 1i b){9(c==b[i])6r;9(5e&&1m b[i]==\'5i\'&&c[i])E.1k(c[i],b[i]);J 9(b[i]!=W)c[i]=b[i]}I c};H F="15"+(1u 3D()).3B(),6p=0,5c={};E.1k({8a:G(a){18.$=D;9(a)18.15=w;I E},1n:G(a){I!!a&&1m a!="1M"&&!a.11&&a.1c!=1B&&/G/i.14(a+"")},4a:G(a){I a.2V&&!a.1G||a.37&&a.3H&&!a.3H.1G},5f:G(a){a=E.36(a);9(a){9(18.6l)18.6l(a);J 9(E.V.1N)18.56(a,0);J 3w.2O(18,a)}},11:G(b,a){I b.11&&b.11.27()==a.27()},1L:{},M:G(c,d,b){c=c==18?5c:c;H a=c[F];9(!a)a=c[F]=++6p;9(d&&!E.1L[a])E.1L[a]={};9(b!=W)E.1L[a][d]=b;I d?E.1L[a][d]:a},30:G(c,b){c=c==18?5c:c;H a=c[F];9(b){9(E.1L[a]){2E E.1L[a][b];b="";L(b 1i E.1L[a])1T;9(!b)E.30(c)}}J{2a{2E c[F]}29(e){9(c.53)c.53(F)}2E E.1L[a]}},N:G(a,b,c){9(c){9(a.K==W)L(H i 1i a)b.16(a[i],c);J L(H i=0,48=a.K;i<48;i++)9(b.16(a[i],c)===P)1T}J{9(a.K==W)L(H i 1i a)b.2O(a[i],i,a[i]);J L(H i=0,48=a.K,3i=a[0];i<48&&b.2O(3i,i,3i)!==P;3i=a[++i]){}}I a},1e:G(c,b,d,e,a){9(E.1n(b))b=b.2O(c,[e]);H f=/z-?4I|7T-?7Q|1r|69|7P-?1H/i;I b&&b.1c==4W&&d=="3C"&&!f.14(a)?b+"2T":b},1o:{1f:G(b,c){E.N((c||"").2l(/\\s+/),G(i,a){9(!E.1o.3K(b.1o,a))b.1o+=(b.1o?" ":"")+a})},28:G(b,c){b.1o=c!=W?E.2W(b.1o.2l(/\\s+/),G(a){I!E.1o.3K(c,a)}).66(" "):""},3K:G(t,c){I E.2A(c,(t.1o||t).3s().2l(/\\s+/))>-1}},2k:G(e,o,f){L(H i 1i o){e.R["3r"+i]=e.R[i];e.R[i]=o[i]}f.16(e,[]);L(H i 1i o)e.R[i]=e.R["3r"+i]},17:G(e,p){9(p=="1H"||p=="2N"){H b={},42,41,d=["7J","7I","7G","7F"];E.N(d,G(){b["7C"+6]=0;b["7B"+6+"5Z"]=0});E.2k(e,b,G(){9(E(e).3t(\':3R\')){42=e.7A;41=e.7w}J{e=E(e.4R(Q)).1Y(":4k").5W("2Q").2D().17({4C:"1P",2X:"4F",19:"2Z",7o:"0",1S:"0"}).5R(e.12)[0];H a=E.17(e.12,"2X")||"3V";9(a=="3V")e.12.R.2X="7g";42=e.7e;41=e.7b;9(a=="3V")e.12.R.2X="3V";e.12.3b(e)}});I p=="1H"?42:41}I E.3C(e,p)},3C:G(h,j,i){H g,2w=[],2k=[];G 3n(a){9(!E.V.1N)I P;H b=U.3o.3Z(a,S);I!b||b.4y("3n")==""}9(j=="1r"&&E.V.1h){g=E.1x(h.R,"1r");I g==""?"1":g}9(j.1t(/4u/i))j=y;9(!i&&h.R[j])g=h.R[j];J 9(U.3o&&U.3o.3Z){9(j.1t(/4u/i))j="4u";j=j.1p(/([A-Z])/g,"-$1").2p();H d=U.3o.3Z(h,S);9(d&&!3n(h))g=d.4y(j);J{L(H a=h;a&&3n(a);a=a.12)2w.4w(a);L(a=0;a<2w.K;a++)9(3n(2w[a])){2k[a]=2w[a].R.19;2w[a].R.19="2Z"}g=j=="19"&&2k[2w.K-1]!=S?"2s":U.3o.3Z(h,S).4y(j)||"";L(a=0;a<2k.K;a++)9(2k[a]!=S)2w[a].R.19=2k[a]}9(j=="1r"&&g=="")g="1"}J 9(h.3Q){H f=j.1p(/\\-(\\w)/g,G(m,c){I c.27()});g=h.3Q[j]||h.3Q[f];9(!/^\\d+(2T)?$/i.14(g)&&/^\\d/.14(g)){H k=h.R.1S;H e=h.4v.1S;h.4v.1S=h.3Q.1S;h.R.1S=g||0;g=h.R.71+"2T";h.R.1S=k;h.4v.1S=e}}I g},4D:G(a,e){H r=[];e=e||U;E.N(a,G(i,d){9(!d)I;9(d.1c==4W)d=d.3s();9(1m d=="1M"){d=d.1p(/(<(\\w+)[^>]*?)\\/>/g,G(m,a,b){I b.1t(/^(70|6Z|6Y|9Q|4t|9N|9K|3a|9G|9E)$/i)?m:a+"></"+b+">"});H s=E.36(d).2p(),1s=e.5B("1s"),2x=[];H c=!s.1g("<9y")&&[1,"<24>","</24>"]||!s.1g("<9w")&&[1,"<6T>","</6T>"]||s.1t(/^<(9u|1K|9t|9r|9p)/)&&[1,"<1I>","</1I>"]||!s.1g("<4m")&&[2,"<1I><1K>","</1K></1I>"]||(!s.1g("<9m")||!s.1g("<9k"))&&[3,"<1I><1K><4m>","</4m></1K></1I>"]||!s.1g("<6Y")&&[2,"<1I><1K></1K><6L>","</6L></1I>"]||E.V.1h&&[1,"1s<1s>","</1s>"]||[0,"",""];1s.3O=c[1]+d+c[2];1W(c[0]--)1s=1s.5p;9(E.V.1h){9(!s.1g("<1I")&&s.1g("<1K")<0)2x=1s.1w&&1s.1w.3j;J 9(c[1]=="<1I>"&&s.1g("<1K")<0)2x=1s.3j;L(H n=2x.K-1;n>=0;--n)9(E.11(2x[n],"1K")&&!2x[n].3j.K)2x[n].12.3b(2x[n]);9(/^\\s/.14(d))1s.3d(e.6F(d.1t(/^\\s*/)[0]),1s.1w)}d=E.2h(1s.3j)}9(0===d.K&&(!E.11(d,"2Y")&&!E.11(d,"24")))I;9(d[0]==W||E.11(d,"2Y")||d.Y)r.1a(d);J r=E.1R(r,d)});I r},1x:G(c,d,a){H e=E.4a(c)?{}:E.5o;9(d=="26"&&E.V.1N)c.12.4Z;9(e[d]){9(a!=W)c[e[d]]=a;I c[e[d]]}J 9(E.V.1h&&d=="R")I E.1x(c.R,"9e",a);J 9(a==W&&E.V.1h&&E.11(c,"2Y")&&(d=="9d"||d=="9a"))I c.97(d).6x;J 9(c.37){9(a!=W){9(d=="O"&&E.11(c,"4t")&&c.12)6G"O 94 93\'t 92 91";c.90(d,a)}9(E.V.1h&&/6C|3k/.14(d)&&!E.4a(c))I c.4p(d,2);I c.4p(d)}J{9(d=="1r"&&E.V.1h){9(a!=W){c.69=1;c.1E=(c.1E||"").1p(/6O\\([^)]*\\)/,"")+(3I(a).3s()=="8S"?"":"6O(1r="+a*6A+")")}I c.1E?(3I(c.1E.1t(/1r=([^)]*)/)[1])/6A).3s():""}d=d.1p(/-([a-z])/8Q,G(z,b){I b.27()});9(a!=W)c[d]=a;I c[d]}},36:G(t){I(t||"").1p(/^\\s+|\\s+$/g,"")},2h:G(a){H r=[];9(1m a!="8P")L(H i=0,2c=a.K;i<2c;i++)r.1a(a[i]);J r=a.2J(0);I r},2A:G(b,a){L(H i=0,2c=a.K;i<2c;i++)9(a[i]==b)I i;I-1},1R:G(a,b){9(E.V.1h){L(H i=0;b[i];i++)9(b[i].1y!=8)a.1a(b[i])}J L(H i=0;b[i];i++)a.1a(b[i]);I a},4V:G(b){H r=[],2f={};2a{L(H i=0,6y=b.K;i<6y;i++){H a=E.M(b[i]);9(!2f[a]){2f[a]=Q;r.1a(b[i])}}}29(e){r=b}I r},2W:G(b,a,c){9(1m a=="1M")a=3w("P||G(a,i){I "+a+"}");H d=[];L(H i=0,4g=b.K;i<4g;i++)9(!c&&a(b[i],i)||c&&!a(b[i],i))d.1a(b[i]);I d},1X:G(c,b){9(1m b=="1M")b=3w("P||G(a){I "+b+"}");H d=[];L(H i=0,4g=c.K;i<4g;i++){H a=b(c[i],i);9(a!==S&&a!=W){9(a.1c!=1B)a=[a];d=d.8M(a)}}I d}});H v=8K.8I.2p();E.V={4s:(v.1t(/.+(?:8F|8E|8C|8B)[\\/: ]([\\d.]+)/)||[])[1],1N:/6w/.14(v),34:/34/.14(v),1h:/1h/.14(v)&&!/34/.14(v),35:/35/.14(v)&&!/(8z|6w)/.14(v)};H y=E.V.1h?"4h":"5h";E.1k({5g:!E.V.1h||U.8y=="8x",4h:E.V.1h?"4h":"5h",5o:{"L":"8w","8v":"1o","4u":y,5h:y,4h:y,3O:"3O",1o:"1o",1Q:"1Q",3c:"3c",2Q:"2Q",8u:"8t",26:"26",8s:"8r"}});E.N({1D:"a.12",8q:"15.4e(a,\'12\')",8p:"15.2I(a,2,\'2q\')",8n:"15.2I(a,2,\'4d\')",8l:"15.4e(a,\'2q\')",8k:"15.4e(a,\'4d\')",8j:"15.5d(a.12.1w,a)",8i:"15.5d(a.1w)",6q:"15.11(a,\'8h\')?a.8f||a.8e.U:15.2h(a.3j)"},G(i,n){E.1b[i]=G(a){H b=E.1X(6,n);9(a&&1m a=="1M")b=E.3m(a,b);I 6.2o(E.4V(b))}});E.N({5R:"3g",8c:"6j",3d:"6g",8b:"50",89:"6H"},G(i,n){E.1b[i]=G(){H a=1q;I 6.N(G(){L(H j=0,2c=a.K;j<2c;j++)E(a[j])[n](6)})}});E.N({5W:G(a){E.1x(6,a,"");6.53(a)},88:G(c){E.1o.1f(6,c)},87:G(c){E.1o.28(6,c)},86:G(c){E.1o[E.1o.3K(6,c)?"28":"1f"](6,c)},28:G(a){9(!a||E.1E(a,[6]).r.K){E.30(6);6.12.3b(6)}},4n:G(){E("*",6).N(G(){E.30(6)});1W(6.1w)6.3b(6.1w)}},G(i,n){E.1b[i]=G(){I 6.N(n,1q)}});E.N(["85","5Z"],G(i,a){H n=a.2p();E.1b[n]=G(h){I 6[0]==18?E.V.1N&&3y["84"+a]||E.5g&&38.33(U.2V["5a"+a],U.1G["5a"+a])||U.1G["5a"+a]:6[0]==U?38.33(U.1G["6n"+a],U.1G["6m"+a]):h==W?(6.K?E.17(6[0],n):S):6.17(n,h.1c==3X?h:h+"2T")}});H C=E.V.1N&&3x(E.V.4s)<83?"(?:[\\\\w*57-]|\\\\\\\\.)":"(?:[\\\\w\\82-\\81*57-]|\\\\\\\\.)",6k=1u 47("^>\\\\s*("+C+"+)"),6i=1u 47("^("+C+"+)(#)("+C+"+)"),6h=1u 47("^([#.]?)("+C+"*)");E.1k({55:{"":"m[2]==\'*\'||15.11(a,m[2])","#":"a.4p(\'22\')==m[2]",":":{80:"i<m[3]-0",7Z:"i>m[3]-0",2I:"m[3]-0==i",6E:"m[3]-0==i",3v:"i==0",3u:"i==r.K-1",6f:"i%2==0",6e:"i%2","3v-46":"a.12.4l(\'*\')[0]==a","3u-46":"15.2I(a.12.5p,1,\'4d\')==a","7X-46":"!15.2I(a.12.5p,2,\'4d\')",1D:"a.1w",4n:"!a.1w",7W:"(a.6s||a.7V||15(a).2g()||\'\').1g(m[3])>=0",3R:\'"1P"!=a.O&&15.17(a,"19")!="2s"&&15.17(a,"4C")!="1P"\',1P:\'"1P"==a.O||15.17(a,"19")=="2s"||15.17(a,"4C")=="1P"\',7U:"!a.3c",3c:"a.3c",2Q:"a.2Q",26:"a.26||15.1x(a,\'26\')",2g:"\'2g\'==a.O",4k:"\'4k\'==a.O",5j:"\'5j\'==a.O",54:"\'54\'==a.O",52:"\'52\'==a.O",51:"\'51\'==a.O",6d:"\'6d\'==a.O",6c:"\'6c\'==a.O",2r:\'"2r"==a.O||15.11(a,"2r")\',4t:"/4t|24|6b|2r/i.14(a.11)",3K:"15.1Y(m[3],a).K",7S:"/h\\\\d/i.14(a.11)",7R:"15.2W(15.32,G(1b){I a==1b.T;}).K"}},6a:[/^(\\[) *@?([\\w-]+) *([!*$^~=]*) *(\'?"?)(.*?)\\4 *\\]/,/^(:)([\\w-]+)\\("?\'?(.*?(\\(.*?\\))?[^(]*?)"?\'?\\)/,1u 47("^([:.#]*)("+C+"+)")],3m:G(a,c,b){H d,2b=[];1W(a&&a!=d){d=a;H f=E.1E(a,c,b);a=f.t.1p(/^\\s*,\\s*/,"");2b=b?c=f.r:E.1R(2b,f.r)}I 2b},1Y:G(t,o){9(1m t!="1M")I[t];9(o&&!o.1y)o=S;o=o||U;H d=[o],2f=[],3u;1W(t&&3u!=t){H r=[];3u=t;t=E.36(t);H l=P;H g=6k;H m=g.2S(t);9(m){H p=m[1].27();L(H i=0;d[i];i++)L(H c=d[i].1w;c;c=c.2q)9(c.1y==1&&(p=="*"||c.11.27()==p.27()))r.1a(c);d=r;t=t.1p(g,"");9(t.1g(" ")==0)6r;l=Q}J{g=/^([>+~])\\s*(\\w*)/i;9((m=g.2S(t))!=S){r=[];H p=m[2],1R={};m=m[1];L(H j=0,31=d.K;j<31;j++){H n=m=="~"||m=="+"?d[j].2q:d[j].1w;L(;n;n=n.2q)9(n.1y==1){H h=E.M(n);9(m=="~"&&1R[h])1T;9(!p||n.11.27()==p.27()){9(m=="~")1R[h]=Q;r.1a(n)}9(m=="+")1T}}d=r;t=E.36(t.1p(g,""));l=Q}}9(t&&!l){9(!t.1g(",")){9(o==d[0])d.44();2f=E.1R(2f,d);r=d=[o];t=" "+t.68(1,t.K)}J{H k=6i;H m=k.2S(t);9(m){m=[0,m[2],m[3],m[1]]}J{k=6h;m=k.2S(t)}m[2]=m[2].1p(/\\\\/g,"");H f=d[d.K-1];9(m[1]=="#"&&f&&f.3S&&!E.4a(f)){H q=f.3S(m[2]);9((E.V.1h||E.V.34)&&q&&1m q.22=="1M"&&q.22!=m[2])q=E(\'[@22="\'+m[2]+\'"]\',f)[0];d=r=q&&(!m[3]||E.11(q,m[3]))?[q]:[]}J{L(H i=0;d[i];i++){H a=m[1]=="#"&&m[3]?m[3]:m[1]!=""||m[0]==""?"*":m[2];9(a=="*"&&d[i].11.2p()=="5i")a="3a";r=E.1R(r,d[i].4l(a))}9(m[1]==".")r=E.4X(r,m[2]);9(m[1]=="#"){H e=[];L(H i=0;r[i];i++)9(r[i].4p("22")==m[2]){e=[r[i]];1T}r=e}d=r}t=t.1p(k,"")}}9(t){H b=E.1E(t,r);d=r=b.r;t=E.36(b.t)}}9(t)d=[];9(d&&o==d[0])d.44();2f=E.1R(2f,d);I 2f},4X:G(r,m,a){m=" "+m+" ";H c=[];L(H i=0;r[i];i++){H b=(" "+r[i].1o+" ").1g(m)>=0;9(!a&&b||a&&!b)c.1a(r[i])}I c},1E:G(t,r,h){H d;1W(t&&t!=d){d=t;H p=E.6a,m;L(H i=0;p[i];i++){m=p[i].2S(t);9(m){t=t.7O(m[0].K);m[2]=m[2].1p(/\\\\/g,"");1T}}9(!m)1T;9(m[1]==":"&&m[2]=="5V")r=E.1E(m[3],r,Q).r;J 9(m[1]==".")r=E.4X(r,m[2],h);J 9(m[1]=="["){H g=[],O=m[3];L(H i=0,31=r.K;i<31;i++){H a=r[i],z=a[E.5o[m[2]]||m[2]];9(z==S||/6C|3k|26/.14(m[2]))z=E.1x(a,m[2])||\'\';9((O==""&&!!z||O=="="&&z==m[5]||O=="!="&&z!=m[5]||O=="^="&&z&&!z.1g(m[5])||O=="$="&&z.68(z.K-m[5].K)==m[5]||(O=="*="||O=="~=")&&z.1g(m[5])>=0)^h)g.1a(a)}r=g}J 9(m[1]==":"&&m[2]=="2I-46"){H e={},g=[],14=/(\\d*)n\\+?(\\d*)/.2S(m[3]=="6f"&&"2n"||m[3]=="6e"&&"2n+1"||!/\\D/.14(m[3])&&"n+"+m[3]||m[3]),3v=(14[1]||1)-0,d=14[2]-0;L(H i=0,31=r.K;i<31;i++){H j=r[i],12=j.12,22=E.M(12);9(!e[22]){H c=1;L(H n=12.1w;n;n=n.2q)9(n.1y==1)n.4U=c++;e[22]=Q}H b=P;9(3v==1){9(d==0||j.4U==d)b=Q}J 9((j.4U+d)%3v==0)b=Q;9(b^h)g.1a(j)}r=g}J{H f=E.55[m[1]];9(1m f!="1M")f=E.55[m[1]][m[2]];f=3w("P||G(a,i){I "+f+"}");r=E.2W(r,f,h)}}I{r:r,t:t}},4e:G(b,c){H d=[];H a=b[c];1W(a&&a!=U){9(a.1y==1)d.1a(a);a=a[c]}I d},2I:G(a,e,c,b){e=e||1;H d=0;L(;a;a=a[c])9(a.1y==1&&++d==e)1T;I a},5d:G(n,a){H r=[];L(;n;n=n.2q){9(n.1y==1&&(!a||n!=a))r.1a(n)}I r}});E.1j={1f:G(g,e,c,h){9(E.V.1h&&g.4j!=W)g=18;9(!c.2u)c.2u=6.2u++;9(h!=W){H d=c;c=G(){I d.16(6,1q)};c.M=h;c.2u=d.2u}H i=e.2l(".");e=i[0];c.O=i[1];H b=E.M(g,"2P")||E.M(g,"2P",{});H f=E.M(g,"2t",G(){H a;9(1m E=="W"||E.1j.4T)I a;a=E.1j.2t.16(g,1q);I a});H j=b[e];9(!j){j=b[e]={};9(g.4S)g.4S(e,f,P);J g.7N("43"+e,f)}j[c.2u]=c;6.1Z[e]=Q},2u:1,1Z:{},28:G(d,c,b){H e=E.M(d,"2P"),2L,4I;9(1m c=="1M"){H a=c.2l(".");c=a[0]}9(e){9(c&&c.O){b=c.4Q;c=c.O}9(!c){L(c 1i e)6.28(d,c)}J 9(e[c]){9(b)2E e[c][b.2u];J L(b 1i e[c])9(!a[1]||e[c][b].O==a[1])2E e[c][b];L(2L 1i e[c])1T;9(!2L){9(d.4P)d.4P(c,E.M(d,"2t"),P);J d.7M("43"+c,E.M(d,"2t"));2L=S;2E e[c]}}L(2L 1i e)1T;9(!2L){E.30(d,"2P");E.30(d,"2t")}}},1F:G(d,b,e,c,f){b=E.2h(b||[]);9(!e){9(6.1Z[d])E("*").1f([18,U]).1F(d,b)}J{H a,2L,1b=E.1n(e[d]||S),4N=!b[0]||!b[0].2M;9(4N)b.4w(6.4M({O:d,2m:e}));b[0].O=d;9(E.1n(E.M(e,"2t")))a=E.M(e,"2t").16(e,b);9(!1b&&e["43"+d]&&e["43"+d].16(e,b)===P)a=P;9(4N)b.44();9(f&&f.16(e,b)===P)a=P;9(1b&&c!==P&&a!==P&&!(E.11(e,\'a\')&&d=="4L")){6.4T=Q;e[d]()}6.4T=P}I a},2t:G(d){H a;d=E.1j.4M(d||18.1j||{});H b=d.O.2l(".");d.O=b[0];H c=E.M(6,"2P")&&E.M(6,"2P")[d.O],3q=1B.3A.2J.2O(1q,1);3q.4w(d);L(H j 1i c){3q[0].4Q=c[j];3q[0].M=c[j].M;9(!b[1]||c[j].O==b[1]){H e=c[j].16(6,3q);9(a!==P)a=e;9(e===P){d.2M();d.3p()}}}9(E.V.1h)d.2m=d.2M=d.3p=d.4Q=d.M=S;I a},4M:G(c){H a=c;c=E.1k({},a);c.2M=G(){9(a.2M)a.2M();a.7L=P};c.3p=G(){9(a.3p)a.3p();a.7K=Q};9(!c.2m&&c.65)c.2m=c.65;9(E.V.1N&&c.2m.1y==3)c.2m=a.2m.12;9(!c.4K&&c.4J)c.4K=c.4J==c.2m?c.7H:c.4J;9(c.64==S&&c.63!=S){H e=U.2V,b=U.1G;c.64=c.63+(e&&e.2R||b.2R||0);c.7E=c.7D+(e&&e.2B||b.2B||0)}9(!c.3Y&&(c.61||c.60))c.3Y=c.61||c.60;9(!c.5F&&c.5D)c.5F=c.5D;9(!c.3Y&&c.2r)c.3Y=(c.2r&1?1:(c.2r&2?3:(c.2r&4?2:0)));I c}};E.1b.1k({3W:G(c,a,b){I c=="5Y"?6.2G(c,a,b):6.N(G(){E.1j.1f(6,c,b||a,b&&a)})},2G:G(d,b,c){I 6.N(G(){E.1j.1f(6,d,G(a){E(6).5X(a);I(c||b).16(6,1q)},c&&b)})},5X:G(a,b){I 6.N(G(){E.1j.28(6,a,b)})},1F:G(c,a,b){I 6.N(G(){E.1j.1F(c,a,6,Q,b)})},7x:G(c,a,b){9(6[0])I E.1j.1F(c,a,6[0],P,b)},25:G(){H a=1q;I 6.4L(G(e){6.4H=0==6.4H?1:0;e.2M();I a[6.4H].16(6,[e])||P})},7v:G(f,g){G 4G(e){H p=e.4K;1W(p&&p!=6)2a{p=p.12}29(e){p=6};9(p==6)I P;I(e.O=="4x"?f:g).16(6,[e])}I 6.4x(4G).5U(4G)},2d:G(f){5T();9(E.3T)f.16(U,[E]);J E.3l.1a(G(){I f.16(6,[E])});I 6}});E.1k({3T:P,3l:[],2d:G(){9(!E.3T){E.3T=Q;9(E.3l){E.N(E.3l,G(){6.16(U)});E.3l=S}9(E.V.35||E.V.34)U.4P("5S",E.2d,P);9(!18.7t.K)E(18).39(G(){E("#4E").28()})}}});E.N(("7s,7r,39,7q,6n,5Y,4L,7p,"+"7n,7m,7l,4x,5U,7k,24,"+"51,7j,7i,7h,3U").2l(","),G(i,o){E.1b[o]=G(f){I f?6.3W(o,f):6.1F(o)}});H x=P;G 5T(){9(x)I;x=Q;9(E.V.35||E.V.34)U.4S("5S",E.2d,P);J 9(E.V.1h){U.7f("<7d"+"7y 22=4E 7z=Q "+"3k=//:><\\/1J>");H a=U.3S("4E");9(a)a.62=G(){9(6.2C!="1l")I;E.2d()};a=S}J 9(E.V.1N)E.4B=4j(G(){9(U.2C=="5Q"||U.2C=="1l"){4A(E.4B);E.4B=S;E.2d()}},10);E.1j.1f(18,"39",E.2d)}E.1b.1k({39:G(g,d,c){9(E.1n(g))I 6.3W("39",g);H e=g.1g(" ");9(e>=0){H i=g.2J(e,g.K);g=g.2J(0,e)}c=c||G(){};H f="4z";9(d)9(E.1n(d)){c=d;d=S}J{d=E.3a(d);f="5P"}H h=6;E.3G({1d:g,O:f,M:d,1l:G(a,b){9(b=="1C"||b=="5O")h.4o(i?E("<1s/>").3g(a.40.1p(/<1J(.|\\s)*?\\/1J>/g,"")).1Y(i):a.40);56(G(){h.N(c,[a.40,b,a])},13)}});I 6},7a:G(){I E.3a(6.5M())},5M:G(){I 6.1X(G(){I E.11(6,"2Y")?E.2h(6.79):6}).1E(G(){I 6.2H&&!6.3c&&(6.2Q||/24|6b/i.14(6.11)||/2g|1P|52/i.14(6.O))}).1X(G(i,c){H b=E(6).3i();I b==S?S:b.1c==1B?E.1X(b,G(a,i){I{2H:c.2H,1Q:a}}):{2H:c.2H,1Q:b}}).21()}});E.N("5L,5K,6t,5J,5I,5H".2l(","),G(i,o){E.1b[o]=G(f){I 6.3W(o,f)}});H B=(1u 3D).3B();E.1k({21:G(d,b,a,c){9(E.1n(b)){a=b;b=S}I E.3G({O:"4z",1d:d,M:b,1C:a,1V:c})},78:G(b,a){I E.21(b,S,a,"1J")},77:G(c,b,a){I E.21(c,b,a,"45")},76:G(d,b,a,c){9(E.1n(b)){a=b;b={}}I E.3G({O:"5P",1d:d,M:b,1C:a,1V:c})},75:G(a){E.1k(E.59,a)},59:{1Z:Q,O:"4z",2z:0,5G:"74/x-73-2Y-72",6o:Q,3e:Q,M:S},49:{},3G:G(s){H f,2y=/=(\\?|%3F)/g,1v,M;s=E.1k(Q,s,E.1k(Q,{},E.59,s));9(s.M&&s.6o&&1m s.M!="1M")s.M=E.3a(s.M);9(s.1V=="4b"){9(s.O.2p()=="21"){9(!s.1d.1t(2y))s.1d+=(s.1d.1t(/\\?/)?"&":"?")+(s.4b||"5E")+"=?"}J 9(!s.M||!s.M.1t(2y))s.M=(s.M?s.M+"&":"")+(s.4b||"5E")+"=?";s.1V="45"}9(s.1V=="45"&&(s.M&&s.M.1t(2y)||s.1d.1t(2y))){f="4b"+B++;9(s.M)s.M=s.M.1p(2y,"="+f);s.1d=s.1d.1p(2y,"="+f);s.1V="1J";18[f]=G(a){M=a;1C();1l();18[f]=W;2a{2E 18[f]}29(e){}}}9(s.1V=="1J"&&s.1L==S)s.1L=P;9(s.1L===P&&s.O.2p()=="21")s.1d+=(s.1d.1t(/\\?/)?"&":"?")+"57="+(1u 3D()).3B();9(s.M&&s.O.2p()=="21"){s.1d+=(s.1d.1t(/\\?/)?"&":"?")+s.M;s.M=S}9(s.1Z&&!E.5b++)E.1j.1F("5L");9(!s.1d.1g("8g")&&s.1V=="1J"){H h=U.4l("9U")[0];H g=U.5B("1J");g.3k=s.1d;9(!f&&(s.1C||s.1l)){H j=P;g.9R=g.62=G(){9(!j&&(!6.2C||6.2C=="5Q"||6.2C=="1l")){j=Q;1C();1l();h.3b(g)}}}h.58(g);I}H k=P;H i=18.6X?1u 6X("9P.9O"):1u 6W();i.9M(s.O,s.1d,s.3e);9(s.M)i.5C("9J-9I",s.5G);9(s.5y)i.5C("9H-5x-9F",E.49[s.1d]||"9D, 9C 9B 9A 5v:5v:5v 9z");i.5C("X-9x-9v","6W");9(s.6U)s.6U(i);9(s.1Z)E.1j.1F("5H",[i,s]);H c=G(a){9(!k&&i&&(i.2C==4||a=="2z")){k=Q;9(d){4A(d);d=S}1v=a=="2z"&&"2z"||!E.6S(i)&&"3U"||s.5y&&E.6R(i,s.1d)&&"5O"||"1C";9(1v=="1C"){2a{M=E.6Q(i,s.1V)}29(e){1v="5k"}}9(1v=="1C"){H b;2a{b=i.5s("6P-5x")}29(e){}9(s.5y&&b)E.49[s.1d]=b;9(!f)1C()}J E.5r(s,i,1v);1l();9(s.3e)i=S}};9(s.3e){H d=4j(c,13);9(s.2z>0)56(G(){9(i){i.9q();9(!k)c("2z")}},s.2z)}2a{i.9o(s.M)}29(e){E.5r(s,i,S,e)}9(!s.3e)c();I i;G 1C(){9(s.1C)s.1C(M,1v);9(s.1Z)E.1j.1F("5I",[i,s])}G 1l(){9(s.1l)s.1l(i,1v);9(s.1Z)E.1j.1F("6t",[i,s]);9(s.1Z&&!--E.5b)E.1j.1F("5K")}},5r:G(s,a,b,e){9(s.3U)s.3U(a,b,e);9(s.1Z)E.1j.1F("5J",[a,s,e])},5b:0,6S:G(r){2a{I!r.1v&&9n.9l=="54:"||(r.1v>=6N&&r.1v<9j)||r.1v==6M||E.V.1N&&r.1v==W}29(e){}I P},6R:G(a,c){2a{H b=a.5s("6P-5x");I a.1v==6M||b==E.49[c]||E.V.1N&&a.1v==W}29(e){}I P},6Q:G(r,b){H c=r.5s("9i-O");H d=b=="6K"||!b&&c&&c.1g("6K")>=0;H a=d?r.9g:r.40;9(d&&a.2V.37=="5k")6G"5k";9(b=="1J")E.5f(a);9(b=="45")a=3w("("+a+")");I a},3a:G(a){H s=[];9(a.1c==1B||a.4c)E.N(a,G(){s.1a(3f(6.2H)+"="+3f(6.1Q))});J L(H j 1i a)9(a[j]&&a[j].1c==1B)E.N(a[j],G(){s.1a(3f(j)+"="+3f(6))});J s.1a(3f(j)+"="+3f(a[j]));I s.66("&").1p(/%20/g,"+")}});E.1b.1k({1A:G(b,a){I b?6.1U({1H:"1A",2N:"1A",1r:"1A"},b,a):6.1E(":1P").N(G(){6.R.19=6.3h?6.3h:"";9(E.17(6,"19")=="2s")6.R.19="2Z"}).2D()},1z:G(b,a){I b?6.1U({1H:"1z",2N:"1z",1r:"1z"},b,a):6.1E(":3R").N(G(){6.3h=6.3h||E.17(6,"19");9(6.3h=="2s")6.3h="2Z";6.R.19="2s"}).2D()},6J:E.1b.25,25:G(a,b){I E.1n(a)&&E.1n(b)?6.6J(a,b):a?6.1U({1H:"25",2N:"25",1r:"25"},a,b):6.N(G(){E(6)[E(6).3t(":1P")?"1A":"1z"]()})},9c:G(b,a){I 6.1U({1H:"1A"},b,a)},9b:G(b,a){I 6.1U({1H:"1z"},b,a)},99:G(b,a){I 6.1U({1H:"25"},b,a)},98:G(b,a){I 6.1U({1r:"1A"},b,a)},96:G(b,a){I 6.1U({1r:"1z"},b,a)},95:G(c,a,b){I 6.1U({1r:a},c,b)},1U:G(k,i,h,g){H j=E.6D(i,h,g);I 6[j.3L===P?"N":"3L"](G(){j=E.1k({},j);H f=E(6).3t(":1P"),3y=6;L(H p 1i k){9(k[p]=="1z"&&f||k[p]=="1A"&&!f)I E.1n(j.1l)&&j.1l.16(6);9(p=="1H"||p=="2N"){j.19=E.17(6,"19");j.2U=6.R.2U}}9(j.2U!=S)6.R.2U="1P";j.3M=E.1k({},k);E.N(k,G(c,a){H e=1u E.2j(3y,j,c);9(/25|1A|1z/.14(a))e[a=="25"?f?"1A":"1z":a](k);J{H b=a.3s().1t(/^([+-]=)?([\\d+-.]+)(.*)$/),1O=e.2b(Q)||0;9(b){H d=3I(b[2]),2i=b[3]||"2T";9(2i!="2T"){3y.R[c]=(d||1)+2i;1O=((d||1)/e.2b(Q))*1O;3y.R[c]=1O+2i}9(b[1])d=((b[1]=="-="?-1:1)*d)+1O;e.3N(1O,d,2i)}J e.3N(1O,a,"")}});I Q})},3L:G(a,b){9(E.1n(a)){b=a;a="2j"}9(!a||(1m a=="1M"&&!b))I A(6[0],a);I 6.N(G(){9(b.1c==1B)A(6,a,b);J{A(6,a).1a(b);9(A(6,a).K==1)b.16(6)}})},9f:G(){H a=E.32;I 6.N(G(){L(H i=0;i<a.K;i++)9(a[i].T==6)a.6I(i--,1)}).5n()}});H A=G(b,c,a){9(!b)I;H q=E.M(b,c+"3L");9(!q||a)q=E.M(b,c+"3L",a?E.2h(a):[]);I q};E.1b.5n=G(a){a=a||"2j";I 6.N(G(){H q=A(6,a);q.44();9(q.K)q[0].16(6)})};E.1k({6D:G(b,a,c){H d=b&&b.1c==8Z?b:{1l:c||!c&&a||E.1n(b)&&b,2e:b,3J:c&&a||a&&a.1c!=8Y&&a};d.2e=(d.2e&&d.2e.1c==4W?d.2e:{8X:8W,8V:6N}[d.2e])||8T;d.3r=d.1l;d.1l=G(){E(6).5n();9(E.1n(d.3r))d.3r.16(6)};I d},3J:{6B:G(p,n,b,a){I b+a*p},5q:G(p,n,b,a){I((-38.9s(p*38.8R)/2)+0.5)*a+b}},32:[],2j:G(b,c,a){6.Y=c;6.T=b;6.1e=a;9(!c.3P)c.3P={}}});E.2j.3A={4r:G(){9(6.Y.2F)6.Y.2F.16(6.T,[6.2v,6]);(E.2j.2F[6.1e]||E.2j.2F.6z)(6);9(6.1e=="1H"||6.1e=="2N")6.T.R.19="2Z"},2b:G(a){9(6.T[6.1e]!=S&&6.T.R[6.1e]==S)I 6.T[6.1e];H r=3I(E.3C(6.T,6.1e,a));I r&&r>-8O?r:3I(E.17(6.T,6.1e))||0},3N:G(c,b,e){6.5u=(1u 3D()).3B();6.1O=c;6.2D=b;6.2i=e||6.2i||"2T";6.2v=6.1O;6.4q=6.4i=0;6.4r();H f=6;G t(){I f.2F()}t.T=6.T;E.32.1a(t);9(E.32.K==1){H d=4j(G(){H a=E.32;L(H i=0;i<a.K;i++)9(!a[i]())a.6I(i--,1);9(!a.K)4A(d)},13)}},1A:G(){6.Y.3P[6.1e]=E.1x(6.T.R,6.1e);6.Y.1A=Q;6.3N(0,6.2b());9(6.1e=="2N"||6.1e=="1H")6.T.R[6.1e]="8N";E(6.T).1A()},1z:G(){6.Y.3P[6.1e]=E.1x(6.T.R,6.1e);6.Y.1z=Q;6.3N(6.2b(),0)},2F:G(){H t=(1u 3D()).3B();9(t>6.Y.2e+6.5u){6.2v=6.2D;6.4q=6.4i=1;6.4r();6.Y.3M[6.1e]=Q;H a=Q;L(H i 1i 6.Y.3M)9(6.Y.3M[i]!==Q)a=P;9(a){9(6.Y.19!=S){6.T.R.2U=6.Y.2U;6.T.R.19=6.Y.19;9(E.17(6.T,"19")=="2s")6.T.R.19="2Z"}9(6.Y.1z)6.T.R.19="2s";9(6.Y.1z||6.Y.1A)L(H p 1i 6.Y.3M)E.1x(6.T.R,p,6.Y.3P[p])}9(a&&E.1n(6.Y.1l))6.Y.1l.16(6.T);I P}J{H n=t-6.5u;6.4i=n/6.Y.2e;6.4q=E.3J[6.Y.3J||(E.3J.5q?"5q":"6B")](6.4i,n,0,1,6.Y.2e);6.2v=6.1O+((6.2D-6.1O)*6.4q);6.4r()}I Q}};E.2j.2F={2R:G(a){a.T.2R=a.2v},2B:G(a){a.T.2B=a.2v},1r:G(a){E.1x(a.T.R,"1r",a.2v)},6z:G(a){a.T.R[a.1e]=a.2v+a.2i}};E.1b.6m=G(){H c=0,3E=0,T=6[0],5t;9(T)8L(E.V){H b=E.17(T,"2X")=="4F",1D=T.12,23=T.23,2K=T.3H,4f=1N&&3x(4s)<8J;9(T.6V){5w=T.6V();1f(5w.1S+38.33(2K.2V.2R,2K.1G.2R),5w.3E+38.33(2K.2V.2B,2K.1G.2B));9(1h){H d=E("4o").17("8H");d=(d=="8G"||E.5g&&3x(4s)>=7)&&2||d;1f(-d,-d)}}J{1f(T.5l,T.5z);1W(23){1f(23.5l,23.5z);9(35&&/^t[d|h]$/i.14(1D.37)||!4f)d(23);9(4f&&!b&&E.17(23,"2X")=="4F")b=Q;23=23.23}1W(1D.37&&!/^1G|4o$/i.14(1D.37)){9(!/^8D|1I-9S.*$/i.14(E.17(1D,"19")))1f(-1D.2R,-1D.2B);9(35&&E.17(1D,"2U")!="3R")d(1D);1D=1D.12}9(4f&&b)1f(-2K.1G.5l,-2K.1G.5z)}5t={3E:3E,1S:c}}I 5t;G d(a){1f(E.17(a,"9T"),E.17(a,"8A"))}G 1f(l,t){c+=3x(l)||0;3E+=3x(t)||0}}})();',62,616,'||||||this|||if|||||||||||||||||||||||||||||||||function|var|return|else|length|for|data|each|type|false|true|style|null|elem|document|browser|undefined||options|||nodeName|parentNode||test|jQuery|apply|css|window|display|push|fn|constructor|url|prop|add|indexOf|msie|in|event|extend|complete|typeof|isFunction|className|replace|arguments|opacity|div|match|new|status|firstChild|attr|nodeType|hide|show|Array|success|parent|filter|trigger|body|height|table|script|tbody|cache|string|safari|start|hidden|value|merge|left|break|animate|dataType|while|map|find|global||get|id|offsetParent|select|toggle|selected|toUpperCase|remove|catch|try|cur|al|ready|duration|done|text|makeArray|unit|fx|swap|split|target||pushStack|toLowerCase|nextSibling|button|none|handle|guid|now|stack|tb|jsre|timeout|inArray|scrollTop|readyState|end|delete|step|one|name|nth|slice|doc|ret|preventDefault|width|call|events|checked|scrollLeft|exec|px|overflow|documentElement|grep|position|form|block|removeData|rl|timers|max|opera|mozilla|trim|tagName|Math|load|param|removeChild|disabled|insertBefore|async|encodeURIComponent|append|oldblock|val|childNodes|src|readyList|multiFilter|color|defaultView|stopPropagation|args|old|toString|is|last|first|eval|parseInt|self|domManip|prototype|getTime|curCSS|Date|top||ajax|ownerDocument|parseFloat|easing|has|queue|curAnim|custom|innerHTML|orig|currentStyle|visible|getElementById|isReady|error|static|bind|String|which|getComputedStyle|responseText|oWidth|oHeight|on|shift|json|child|RegExp|ol|lastModified|isXMLDoc|jsonp|jquery|previousSibling|dir|safari2|el|styleFloat|state|setInterval|radio|getElementsByTagName|tr|empty|html|getAttribute|pos|update|version|input|float|runtimeStyle|unshift|mouseover|getPropertyValue|GET|clearInterval|safariTimer|visibility|clean|__ie_init|absolute|handleHover|lastToggle|index|fromElement|relatedTarget|click|fix|evt|andSelf|removeEventListener|handler|cloneNode|addEventListener|triggered|nodeIndex|unique|Number|classFilter|prevObject|selectedIndex|after|submit|password|removeAttribute|file|expr|setTimeout|_|appendChild|ajaxSettings|client|active|win|sibling|deep|globalEval|boxModel|cssFloat|object|checkbox|parsererror|offsetLeft|wrapAll|dequeue|props|lastChild|swing|handleError|getResponseHeader|results|startTime|00|box|Modified|ifModified|offsetTop|evalScript|createElement|setRequestHeader|ctrlKey|callback|metaKey|contentType|ajaxSend|ajaxSuccess|ajaxError|ajaxStop|ajaxStart|serializeArray|init|notmodified|POST|loaded|appendTo|DOMContentLoaded|bindReady|mouseout|not|removeAttr|unbind|unload|Width|keyCode|charCode|onreadystatechange|clientX|pageX|srcElement|join|outerHTML|substr|zoom|parse|textarea|reset|image|odd|even|before|quickClass|quickID|prepend|quickChild|execScript|offset|scroll|processData|uuid|contents|continue|textContent|ajaxComplete|clone|setArray|webkit|nodeValue|fl|_default|100|linear|href|speed|eq|createTextNode|throw|replaceWith|splice|_toggle|xml|colgroup|304|200|alpha|Last|httpData|httpNotModified|httpSuccess|fieldset|beforeSend|getBoundingClientRect|XMLHttpRequest|ActiveXObject|col|br|abbr|pixelLeft|urlencoded|www|application|ajaxSetup|post|getJSON|getScript|elements|serialize|clientWidth|hasClass|scr|clientHeight|write|relative|keyup|keypress|keydown|change|mousemove|mouseup|mousedown|right|dblclick|resize|focus|blur|frames|instanceof|hover|offsetWidth|triggerHandler|ipt|defer|offsetHeight|border|padding|clientY|pageY|Left|Right|toElement|Bottom|Top|cancelBubble|returnValue|detachEvent|attachEvent|substring|line|weight|animated|header|font|enabled|innerText|contains|only|size|gt|lt|uFFFF|u0128|417|inner|Height|toggleClass|removeClass|addClass|replaceAll|noConflict|insertAfter|prependTo|wrap|contentWindow|contentDocument|http|iframe|children|siblings|prevAll|nextAll|wrapInner|prev|Boolean|next|parents|maxLength|maxlength|readOnly|readonly|class|htmlFor|CSS1Compat|compatMode|compatible|borderTopWidth|ie|ra|inline|it|rv|medium|borderWidth|userAgent|522|navigator|with|concat|1px|10000|array|ig|PI|NaN|400|reverse|fast|600|slow|Function|Object|setAttribute|changed|be|can|property|fadeTo|fadeOut|getAttributeNode|fadeIn|slideToggle|method|slideUp|slideDown|action|cssText|stop|responseXML|option|content|300|th|protocol|td|location|send|cap|abort|colg|cos|tfoot|thead|With|leg|Requested|opt|GMT|1970|Jan|01|Thu|area|Since|hr|If|Type|Content|meta|specified|open|link|XMLHTTP|Microsoft|img|onload|row|borderLeftWidth|head|attributes'.split('|'),0,{}); + +/* + Copyright (c) 2004-2007, The Dojo Foundation + All Rights Reserved. + + Licensed under the Academic Free License version 2.1 or above OR the + modified BSD license. For more information on Dojo licensing, see: + + http://dojotoolkit.org/community/licensing.shtml +*/ + +/* + This is a compiled version of Dojo, built for deployment and not for + development. To get an editable version, please visit: + + http://dojotoolkit.org + + for documentation and information on getting the source. +*/ + +var decompressedDojo = function(p,a,c,k,e,d){e=function(c){return(c<a?"":e(parseInt(c/a)))+((c=c%a)>35?String.fromCharCode(c+29):c.toString(36))};if(!''.replace(/^/,String)){while(c--)d[e(c)]=k[c]||e(c);k=[function(e){return d[e]}];e=function(){return'\\w+'};c=1};while(c--)if(k[c])p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c]);return p}('if(V z=="1k"){(B(){if(V D["1o"]=="1k"){D.1o={}}if((!D["1z"])||(!1z["ca"])){D.1z={}}A cn=["rA","rz","1K","ry","rx","9f","rw","rv","ru","rt","rs","rr","rq","ro","rn","rm"];A i=0,24;1s(24=cn[i++]){if(!1z[24]){1z[24]=B(){}}}if(V D["z"]=="1k"){D.z={}}z.1W=D;A d3={im:U,rl:U,rk:"",rj:"",ri:"",rh:K,rg:U};R(A 8z in d3){if(V 1o[8z]=="1k"){1o[8z]=d3[8z]}}A jK=["rf","rd","rc","rb"];A t;1s(t=jK.3a()){z["is"+t]=U}})();z.8h=1o.8h;z.cY={jJ:0,jI:9,jH:0,jG:"",jF:2V("$ra: r9 $".1f(/[0-9]+/)[0]),2i:B(){4G(z.cY){C jJ+"."+jI+"."+jH+jG+" ("+jF+")"}}};z.d1=B(jE,jD,1V){A 2h=1V||z.1W;R(A i=0,p;2h&&(p=jE[i]);i++){2h=(p in 2h?2h[p]:(jD?2h[p]={}:1k))}C 2h};z.88=B(jC,jA,jB){A d2=jC.1A("."),p=d2.8q(),M=z.d1(d2,K,jB);C(M&&p?(M[p]=jA):1k)};z.6q=B(jz,jy,jx){C z.d1(jz.1A("."),jy,jx)};z.r8=B(jw,M){C!!z.6q(jw,U,M)};z["3u"]=B(d0){C z.1W.3u?z.1W.3u(d0):3u(d0)};z.ia=B(jv,cZ,cX){A 8y="r7: "+jv;if(cZ){8y+=" "+cZ}if(cX){8y+=" -- r6 be r5 in cY: "+cX}1z.1K(8y)};z.r4=B(ju,cW){A cV="r3: "+ju+" -- r2 r1 4F r0 qZ qY.";if(cW){cV+=" "+cW}1z.1K(cV)};(B(){A cR={53:{},6p:0,1h:{},8k:{z:{1p:"z",1Z:"."},cU:{1p:"cU",1Z:"../qX/cU"},cT:{1p:"cT",1Z:"cT"}},cN:B(cS){A mp=D.8k;C jp(mp[cS]&&mp[cS].1Z)},jk:B(8x){A mp=D.8k;if(D.cN(8x)){C mp[8x].1Z}C 8x},8v:[],6t:U,56:[],8t:[],8u:U};R(A cQ in cR){z[cQ]=cR[cQ]}})();z.jg=B(8w,cP,cb){A 1g=(((8w.2s(0)=="/"||8w.1f(/^\\w+:/)))?"":D.51)+8w;if(1o.jt&&z.c8){1g+="?"+67(1o.jt).2f(/\\W+/g,"")}1u{C!cP?D.cO(1g,cb):D.jq(1g,cP,cb)}1y(e){1z.1K(e);C U}};z.cO=B(1g,cb){if(D.8v[1g]){C K}A 6u=D.iR(1g,K);if(!6u){C U}D.8v[1g]=K;D.8v.Y(1g);if(cb){6u="("+6u+")"}A jr=z["3u"](6u+"\\r\\n//@ qW="+1g);if(cb){cb(jr)}C K};z.jq=B(1g,jo,cb){A ok=U;1u{ok=D.cO(1g,cb)}1y(e){1z.1K("qV je ",1g," 4G 9f: ",e)}C jp(ok&&D.53[jo])};z.6m=B(){D.8u=K;D.6t=K;A 57=D.56;D.56=[];R(A x=0;x<57.G;x++){57[x]()}D.8u=U;if(z.6t&&z.6p==0&&D.56.G>0){z.8s()}};z.ck=B(){A 57=D.8t;1s(57.G){(57.8q())()}};z.qU=B(M,jn){A d=z;if(P.G==1){d.56.Y(M)}I{if(P.G>1){d.56.Y(B(){M[jn]()})}}if(d.6t&&d.6p==0&&!d.8u){d.8s()}};z.dW=B(M,jm){A d=z;if(P.G==1){d.8t.Y(M)}I{if(P.G>1){d.8t.Y(B(){M[jm]()})}}};z.iM=B(){if(D.6t){C}if(D.6p>0){1z.1K("qT qS in qR!");C}z.8s()};z.8s=B(){if(V 5c=="8b"||(1o["qQ"]&&z.2M)){5c("z.6m();",0)}I{z.6m()}};z.cF=B(jl){A 4v=jl.1A(".");R(A i=4v.G;i>0;i--){A 8r=4v.2w(0,i).22(".");if((i==1)&&!D.cN(8r)){4v[0]="../"+4v[0]}I{A cM=D.jk(8r);if(cM!=8r){4v.3S(0,i,cM);3f}}}C 4v};z.jj=U;z.8m=B(2T,qP,55){55=D.jj||55;A 54=D.53[2T];if(54){C 54}A cL=2T.1A(".");A 3L=D.cF(2T);A jh=((3L[0].2s(0)!="/")&&!3L[0].1f(/^\\w+:/));A ji=3L[3L.G-1];A 3m;if(ji=="*"){2T=cL.2w(0,-1).22(".");3L.8q();3m=3L.22("/")+"/"+(1o["qO"]||"qN")+".js";if(jh&&3m.2s(0)=="/"){3m=3m.2w(1)}}I{3m=3L.22("/")+".js";2T=cL.22(".")}A jf=(!55)?2T:L;A ok=D.jg(3m,jf);if((!ok)&&(!55)){2m S 1O("qM 3O 4E \'"+2T+"\'; 72 qL \'"+3m+"\'")}if((!55)&&(!D["qK"])){54=D.53[2T];if(!54){2m S 1O("qJ \'"+2T+"\' is 3O qI a8 je \'"+3m+"\'")}}C 54};z.8c=z.8m;z.1Q=B(cK){A cJ=cK+"";A 8p=cJ;A 6s=cK.1A(/\\./);if(6s[6s.G-1]=="*"){6s.8q();8p=6s.22(".")}A 8o=z.6q(8p,K);D.53[cJ]=8o;D.53[8p]=8o;C 8o};z.qH=B(8n){A jd=8n["qG"]||[];A cI=jd.3U(8n[z.j4]||8n["aY"]||[]);R(A x=0;x<cI.G;x++){A 8l=cI[x];if(8l.1P==4e){z.8m.14(z,8l)}I{z.8m(8l)}}};z.jb=B(jc,qF){if(jc===K){A cH=[];R(A i=1;i<P.G;i++){cH.Y(P[i])}z.8c.14(z,cH)}};z.qE=z.jb;z.io=B(cG,ja){D.8k[cG]={1p:cG,1Z:ja}};z.qD=B(qC,qB,qA,qz){z.8c("z.j9");z.j9.qy.14(z.qx,P)};(B(){A j7=S 9G("^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\\\\?([^#]*))?(#(.*))?$");A j6=S 9G("^((([^:]+:)?([^@]+))@)?([^:]*)(:([0-9]+))?$");z.4r=B(){A n=L;A 1V=P;A 1g=1V[0];R(A i=1;i<1V.G;i++){if(!1V[i]){6c}A 1t=S z.4r(1V[i]+"");A 4u=S z.4r(1g+"");if((1t.28=="")&&(!1t.4t)&&(!1t.3l)&&(!1t.1r)){if(1t.52!=n){4u.52=1t.52}1t=4u}I{if(!1t.4t){1t.4t=4u.4t;if(!1t.3l){1t.3l=4u.3l;if(1t.28.2s(0)!="/"){A j8=4u.28.21(0,4u.28.31("/")+1)+1t.28;A 1X=j8.1A("/");R(A j=0;j<1X.G;j++){if(1X[j]=="."){if(j==1X.G-1){1X[j]=""}I{1X.3S(j,1);j--}}I{if(j>0&&!(j==1&&1X[0]=="")&&1X[j]==".."&&1X[j-1]!=".."){if(j==(1X.G-1)){1X.3S(j,1);1X[j-1]=""}I{1X.3S(j-1,2);j-=2}}}}1t.28=1X.22("/")}}}}1g="";if(1t.4t){1g+=1t.4t+":"}if(1t.3l){1g+="//"+1t.3l}1g+=1t.28;if(1t.1r){1g+="?"+1t.1r}if(1t.52){1g+="#"+1t.52}}D.1g=1g.2i();A r=D.1g.1f(j7);D.4t=r[2]||(r[1]?"":n);D.3l=r[4]||(r[3]?"":n);D.28=r[5];D.1r=r[7]||(r[6]?"":n);D.52=r[9]||(r[8]?"":n);if(D.3l!=n){r=D.3l.1f(j6);D.8X=r[3]||n;D.8W=r[4]||n;D.qw=r[5];D.qv=r[7]||n}};z.4r.1C.2i=B(){C D.1g}})();z.qu=B(j5,2E){A 2B=z.cF(j5).22("/");if(!2B){C L}if(2B.31("/")!=2B.G-1){2B+="/"}A cE=2B.T(":");if(2B.2s(0)!="/"&&(cE==-1||cE>2B.T("/"))){2B=z.51+2B}C S z.4r(2B,2E)};if(V 26!="1k"){z.c8=K;z.j4="qt";(B(){A d=z;if(1q&&1q.4I){A 8j=1q.4I("ak");A j3=/z(\\.qs)?\\.js([\\?\\.]|$)/i;R(A i=0;i<8j.G;i++){A 4X=8j[i].5t("4X");if(!4X){6c}A m=4X.1f(j3);if(m){if(!1o["51"]){1o["51"]=4X.21(0,m.hK)}A cD=8j[i].5t("1o");if(cD){A cC=3u("({ "+cD+" })");R(A x in cC){1o[x]=cC[x]}}3f}}}d.51=1o["51"];A n=cq;A 8i=n.iL;A 4Z=n.qr;A 6r=2k(4Z);d.2M=(8i.T("qq")>=0)?6r:0;d.6B=(4Z.T("qo")>=0)||(4Z.T("j2")>=0)?6r:0;d.3o=(4Z.T("j2")>=0)?6r:0;A j1=8i.T("qn");d.gu=d.7B=((j1>=0)&&(!d.6B))?6r:0;d.j0=0;d.1l=0;d.iV=0;1u{if(d.7B){d.j0=2k(8i.1A("qm/")[1].1A(" ")[0])}if((1q.gx)&&(!d.2M)){d.1l=2k(4Z.1A("qk ")[1].1A(";")[0])}}1y(e){}if(z.1l&&(26.8f.cu==="9q:")){1o.iT=K}d.iX=B(){A 2A;A qj;A cB=d.6q("cz.cy");if(cB){C cB}if(V iZ!="1k"){2A=S iZ()}I{if(d.1l){1u{2A=S 9j("qi.qh")}1y(e){}}I{if(cq.qg["8Z/x-iY"]){2A=1q.a9("8b");2A.cA("Z","8Z/x-iY");2A.cA("3n",0);2A.cA("58",0);2A.1c.gq="7C";1q.5K.4c(2A)}}}if(!2A){C L}z.88("cz.cy.qf",2A);C z.6q("cz.cy")};A iW=d.iX();if(iW){d.iV=K}A cm=1q["aX"];d.qe=(cm=="aW")||(cm=="gr")||(d.1l<6);d.8h=1o.8h||(d.1l?n.qd:n.qc).1M();d.qb=1z.1K;d.cx=["iU.8g","em.8g","iU.8g.4.0"];d.9b=B(){A 4s=L;A cv=L;if(!z.1l||!1o.iT){1u{4s=S qa()}1y(e){}}if(!4s){R(A i=0;i<3;++i){A cw=z.cx[i];1u{4s=S 9j(cw)}1y(e){cv=e}if(4s){z.cx=[cw];3f}}}if(!4s){2m S 1O("8g 3O q9: "+cv)}C 4s};d.8Y=B(iS){A 4Y=iS.3N||0;C((4Y>=q8)&&(4Y<q7))||(4Y==q6)||(4Y==q5)||(!4Y&&(8f.cu=="9q:"||8f.cu=="q4:"))};A cs=1q.4I("q3");A iQ=(cs&&cs.G>0);d.iR=B(1g,iP){A 3K=D.9b();if(!iQ&&z.4r){1g=(S z.4r(26.8f,1g)).2i()}3K.dL("dD",1g,U);1u{3K.dI(L);if(!d.8Y(3K)){A 1G=1O("q2 4F 4E "+1g+" 3N:"+3K.3N);1G.3N=3K.3N;1G.2G=3K.2G;2m 1G}}1y(e){if(iP){C L}2m e}C 3K.2G}})();z.iO=U;z.6o=B(e){z.iO=K;A cr=(e&&e.Z)?e.Z.1M():"4E";if(P.2O.iN||(cr!="q1"&&cr!="4E")){C}P.2O.iN=K;if(V z["8e"]!="1k"){dX(z.8e);63 z.8e}if(z.6p==0){z.iM()}};if(1q.66){if(z.2M||(z.7B&&(1o["q0"]===K))){1q.66("pZ",z.6o,L)}26.66("4E",z.6o,L)}if(/(pY|pX)/i.6Z(cq.iL)){z.8e=dN(B(){if(/6m|iJ/.6Z(1q.6F)){z.6o()}},10)}(B(){A 3g=26;A 8d=B(cp,fp){A iK=3g[cp]||B(){};3g[cp]=B(){fp.14(3g,P);iK.14(3g,P)}};if(z.1l){1q.fJ("<iI"+"iH pW 4X=\\"//:\\" "+"pV=\\"if(D.6F==\'iJ\'){z.6o();}\\">"+"</iI"+"iH>");A co=K;8d("iG",B(){3g.5c(B(){co=U},0)});8d("pU",B(){if(co){z.ck()}});1u{1q.pT.2P("v","pS:pR-pQ-pP:pO");1q.pN().pM("v\\\\:*","pL:2E(#aY#pK)")}1y(e){}}I{8d("iG",B(){z.ck()})}})();z.pJ=B(){};z.1e=26["1q"]||L;z.3E=B(){C z.1e.3E||z.1e.4I("3E")[0]};z.ch=B(iF,iE){z.1W=iF;z.1e=iE};z.cf=B(4q,6n,iD){if((6n)&&((V 4q=="3c")||(4q 1N 67))){4q=6n[4q]}C(6n?4q.14(6n,iD||[]):4q())};z.pI=B(cj,iC,iB,iA){A cg;A iz=z.1W;A iy=z.1e;1u{z.ch(cj,cj.1q);cg=z.cf(iC,iB,iA)}ir{z.ch(iz,iy)}C cg};z.pH=B(ix,iw,iv,iu){A ce;A ip=z.1e;1u{z.1e=ix;ce=z.cf(iw,iv,iu)}ir{z.1e=ip}C ce};if(1o["cd"]){R(A cc in 1o["cd"]){z.io(cc,1o["cd"][cc])}}}if(1o.im){if(!1z.ca){z.8c("z.pG.ca")}}}if(!z.1h["z.X.c9"]){z.1h["z.X.c9"]=K;z.1Q("z.X.c9");z.1R=B(it){C(V it=="3c"||it 1N 67)};z.2l=B(it){C(it&&it 1N 4e||V it=="6a"||((V z["1H"]!="1k")&&(it 1N z.1H)))};if(z.c8&&z.3o){z.1Y=B(it){if((V(it)=="B")&&(it=="[8b 1H]")){C U}C(V it=="B"||it 1N bI)}}I{z.1Y=B(it){C(V it=="B"||it 1N bI)}}z.ib=B(it){if(V it=="1k"){C U}C(it===L||V it=="8b"||z.2l(it)||z.1Y(it))};z.pF=B(it){A d=z;if((!it)||(V it=="1k")){C U}if(d.1R(it)){C U}if(d.1Y(it)){C U}if(d.2l(it)){C K}if((it.5w)&&(it.5w.1M()=="3R")){C U}if(pE(it.G)){C K}C U};z.pD=B(it){if(!it){C U}C!z.1Y(it)&&/\\{\\s*\\[il 5h\\]\\s*\\}/.6Z(67(it))};z.c7=B(M,4W){A 8a={};R(A x in 4W){if((V 8a[x]=="1k")||(8a[x]!=4W[x])){M[x]=4W[x]}}if(z.1l){A p=4W.2i;if((V(p)=="B")&&(p!=M.2i)&&(p!=8a.2i)&&(p!="\\pC 2i() {\\n [il 5h]\\n}\\n")){M.2i=4W.2i}}C M};z.1x=B(M,pB){R(A i=1,l=P.G;i<l;i++){z.c7(M,P[i])}C M};z.4M=B(c6,pA){R(A i=1,l=P.G;i<l;i++){z.c7(c6.1C,P[i])}C c6};z.ig=B(c5,89){A ij=z.4d(P,2);A ik=z.1R(89);C B(){A ih=z.4d(P);A f=(ik?(c5||z.1W)[89]:89);C(f)&&(f.14(c5||D,ij.3U(ih)))}};z.2p=B(2z,3k){if(P.G>2){C z.ig.14(z,P)}if(!3k){3k=2z;2z=L}if(z.1R(3k)){2z=2z||z.1W;if(!2z[3k]){2m(["z.2p: ie[\\"",3k,"\\"] is L (ie=\\"",2z,"\\")"].22(""))}C B(){C 2z[3k].14(2z,P||[])}}I{C(!2z?3k:B(){C 3k.14(2z,P||[])})}};z.6j=B(M,c3){B c4(){};c4.1C=M;A c2=S c4();if(c3){z.1x(c2,c3)}C c2};z.7X=B(pz){A Q=[L];C z.2p.14(z,Q.3U(z.4d(P)))};z.4d=B(M,ic){A Q=[];R(A x=ic||0;x<M.G;x++){Q.Y(M[x])}C Q};z.c1=B(o){if(!o){C o}if(z.2l(o)){A r=[];R(A i=0;i<o.G;++i){r.Y(z.c1(o[i]))}C r}I{if(z.ib(o)){if(o.2t&&o.a7){C o.a7(K)}I{A r=S o.1P();R(A i in o){if(!(i in r)||r[i]!=o[i]){r[i]=z.c1(o[i])}}C r}}}C o};z.7g=B(2H){C 2H.2f(/^\\s\\s*/,"").2f(/\\s\\s*$/,"")}}if(!z.1h["z.X.2r"]){z.1h["z.X.2r"]=K;z.1Q("z.X.2r");z.2r=B(6l,4p,3j){if(z.1Y(3j)||(P.G>3)){z.ia("z.2r: R 9P \'"+6l+"\' py pw B as \'1P\' pv pu of as a pt i3.","","1.0");A c=3j;3j=P[3]||{};3j.1P=c}A dd=P.2O,4V=L;if(z.2l(4p)){4V=4p;4p=4V.3a()}if(4V){R(A i=0,m;i<4V.G;i++){m=4V[i];if(!m){2m("ps #"+i+" 4F pr of "+6l+" is L. pq\'s pp a po pl is 3O 6m.")}4p=dd.6j(4p,m)}}A i9=(3j||0).1P,6k=dd.6j(4p),fn;R(A i in 3j){if(z.1Y(fn=3j[i])&&(!0[i])){fn.i4=i}}z.4M(6k,{4o:6l,bY:i9,bZ:L},3j||0);6k.1C.1P=6k;C z.88(6l,6k)};z.1x(z.2r,{6j:B(c0,i8){A bp=(c0||0).1C,mp=(i8||0).1C;A 2S=z.2r.i7();z.1x(2S,{84:bp,1x:mp});if(c0){2S.1C=z.6j(bp)}z.4M(2S,z.2r.i6,mp||0,{bY:L});2S.1C.1P=2S;2S.1C.4o=(bp||0).4o+"pk"+(mp||0).4o;z.88(2S.1C.4o,2S);C 2S},i7:B(){C B(){D.i5(P)}},i6:{i5:B(86){A c=86.2O,s=c.84,ct=s&&s.1P,m=c.1x,87=m&&m.1P,a=86,ii,fn;if(a[0]){if((fn=a[0]["bZ"])){a=fn.14(D,a)||a}}if(fn=c.1C.bZ){a=fn.14(D,a)||a}if(ct&&ct.14){ct.14(D,a)}if(87&&87.14){87.14(D,a)}if(ii=c.1C.bY){ii.14(D,86)}},bX:B(85){A c=D.1P,p,m;1s(c){p=c.84;m=c.1x;if(m==85||(m 1N 85.1P)){C p}if(m&&(m=m.bX(85))){C m}c=p&&p.1P}},6h:B(83,82,bW,6i){A p=bW,c,m,f;do{c=p.1P;m=c.1x;if(m&&(m=D.6h(83,82,m,6i))){C m}if((f=p[83])&&(6i==(f==82))){C p}p=c.84}1s(p);C!6i&&(p=D.bX(bW))&&D.6h(83,82,p,6i)},bU:B(2R,4U,bV){A a=P;if(!z.1R(a[0])){bV=4U;4U=2R;2R=4U.2O.i4}A c=4U.2O,p=D.1P.1C,a=bV||4U,fn,mp;if(D[2R]!=c||p[2R]==c){mp=D.6h(2R,c,p,K);if(!mp){2m(D.4o+": 1p i3 (\\""+2R+"\\") 4F bU pj 1f 2O (2r.js)")}p=D.6h(2R,c,mp,U)}fn=p&&p[2R];if(!fn){1z.1K(mp.4o+": no bU \\""+2R+"\\" ph pg (2r.js)");C}C fn.14(D,a)}}})}if(!z.1h["z.X.2c"]){z.1h["z.X.2c"]=K;z.1Q("z.X.2c");z.3i={i2:B(){C B(){A ap=4e.1C,c=P.2O,ls=c.2b,t=c.5V;A r=t&&t.14(D,P);R(A i in ls){if(!(i in ap)){ls[i].14(D,P)}}C r}},2P:B(6g,bT,i1){6g=6g||z.1W;A f=6g[bT];if(!f||!f.2b){A d=z.3i.i2();d.5V=f;d.2b=[];f=6g[bT]=d}C f.2b.Y(i1)},3J:B(i0,hZ,bS){A f=(i0||z.1W)[hZ];if(f&&f.2b&&bS--){63 f.2b[bS]}}};z.2c=B(M,pd,pc,pa,p9){A a=P,F=[],i=0;F.Y(z.1R(a[0])?L:a[i++],a[i++]);A a1=a[i+1];F.Y(z.1R(a1)||z.1Y(a1)?a[i++]:L,a[i++]);R(A l=a.G;i<l;i++){F.Y(a[i])}C z.by.14(D,F)};z.by=B(M,bR,hY,hX){A l=z.3i,h=l.2P(M,bR,z.2p(hY,hX));C[M,bR,h,l]};z.p8=B(6f){if(6f&&6f[0]!==1k){z.bv.14(D,6f);63 6f[0]}};z.bv=B(M,hV,hU,hW){hW.3J(M,hV,hU)};z.80={};z.p7=B(bQ,hT,hS){C[bQ,z.3i.2P(z.80,bQ,z.2p(hT,hS))]};z.p6=B(81){if(81){z.3i.3J(z.80,81[0],81[1])}};z.hQ=B(hR,F){A f=z.80[hR];(f)&&(f.14(D,F||[]))};z.p5=B(hP,M,bP){A pf=B(){z.hQ(hP,P)};C(bP)?z.2c(M,bP,pf):z.2c(M,pf)}}if(!z.1h["z.X.30"]){z.1h["z.X.30"]=K;z.1Q("z.X.30");z.30=B(hO){D.bM=[];D.id=D.hN();D.2y=-1;D.3M=0;D.4R=[L,L];D.bO=hO;D.7Z=U};z.4M(z.30,{hN:(B(){A n=1;C B(){C n++}})(),4C:B(){if(D.2y==-1){if(D.bO){D.bO(D)}I{D.7Z=K}if(D.2y==-1){A 1G=S 1O("30 p4");1G.dY="4C";D.5i(1G)}}I{if((D.2y==0)&&(D.4R[0]1N z.30)){D.4R[0].4C()}}},7V:B(1v){D.2y=((1v 1N 1O)?1:0);D.4R[D.2y]=1v;D.7U()},bN:B(){if(D.2y!=-1){if(!D.7Z){2m S 1O("p3 p2!")}D.7Z=U;C}},dM:B(1v){D.bN();D.7V(1v)},5i:B(1v){D.bN();if(!(1v 1N 1O)){1v=S 1O(1v)}D.7V(1v)},9e:B(cb,4T){A 6e=z.2p(cb,4T);if(P.G>2){6e=z.7X(6e,P,2)}C D.5k(6e,6e)},ef:B(cb,4T){A 7Y=z.2p(cb,4T);if(P.G>2){7Y=z.7X(7Y,P,2)}C D.5k(7Y,L)},ed:B(cb,4T){A 7W=z.2p(cb,4T);if(P.G>2){7W=z.7X(7W,P,2)}C D.5k(L,7W)},5k:B(cb,eb){D.bM.Y([cb,eb]);if(D.2y>=0){D.7U()}C D},7U:B(){A bL=D.bM;A 4n=D.2y;A 1v=D.4R[4n];A 4S=D;A cb=L;1s((bL.G>0)&&(D.3M==0)){A f=bL.3a()[4n];if(!f){6c}1u{1v=f(1v);4n=((1v 1N 1O)?1:0);if(1v 1N z.30){cb=B(1v){4S.7V(1v);4S.3M--;if((4S.3M==0)&&(4S.2y>=0)){4S.7U()}};D.3M++}}1y(1G){1z.1K(1G);4n=1;1v=1G}}D.2y=4n;D.4R[4n]=1v;if((cb)&&(D.3M)){1v.9e(cb)}}})}if(!z.1h["z.X.2e"]){z.1h["z.X.2e"]=K;z.1Q("z.X.2e");z.5m=B(2e){1u{C 3u("("+2e+")")}1y(e){1z.1K(e);C 2e}};z.bK=B(2H){C("\\""+2H.2f(/(["\\\\])/g,"\\\\$1")+"\\"").2f(/[\\f]/g,"\\\\f").2f(/[\\b]/g,"\\\\b").2f(/[\\n]/g,"\\\\n").2f(/[\\t]/g,"\\\\t").2f(/[\\r]/g,"\\\\r")};z.hM="\\t";z.eq=B(it,4l,4P){4P=4P||"";A 4k=(4l?4P+z.hM:"");A 6b=(4l?"\\n":"");A 4Q=V(it);if(4Q=="1k"){C"1k"}I{if((4Q=="4J")||(4Q=="p1")){C it+""}I{if(it===L){C"L"}}}if(4Q=="3c"){C z.bK(it)}A 6d=P.2O;A 4m;if(V it.hL=="B"){4m=it.hL();if(it!==4m){C 6d(4m,4l,4k)}}if(V it.2e=="B"){4m=it.2e();if(it!==4m){C 6d(4m,4l,4k)}}if(z.2l(it)){A 1v=[];R(A i=0;i<it.G;i++){A 1U=6d(it[i],4l,4k);if(V(1U)!="3c"){1U="1k"}1v.Y(6b+4k+1U)}C"["+1v.22(", ")+6b+4P+"]"}if(4Q=="B"){C L}A bJ=[];R(A 1i in it){A 7T;if(V(1i)=="4J"){7T="\\""+1i+"\\""}I{if(V(1i)=="3c"){7T=z.bK(1i)}I{6c}}1U=6d(it[1i],4l,4k);if(V(1U)!="3c"){6c}bJ.Y(6b+4k+7T+": "+1U)}C"{"+bJ.22(", ")+6b+4P+"}"}}if(!z.1h["z.X.6a"]){z.1h["z.X.6a"]=K;z.1Q("z.X.6a");(B(){A 69=B(Q,M,cb){C[(z.1R(Q)?Q.1A(""):Q),(M||z.1W),(z.1R(cb)?(S bI("1m","hK","6a",cb)):cb)]};z.1x(z,{T:B(bH,hH,hI,hJ){A i=0,2q=1,1d=bH.G;if(hJ){i=1d-1;2q=1d=-1}R(i=hI||i;i!=1d;i+=2q){if(bH[i]==hH){C i}}C-1},31:B(hG,hF,hE){C z.T(hG,hF,hE,K)},1n:B(Q,hD,M){if(!Q||!Q.G){C}A 1I=69(Q,M,hD);Q=1I[0];R(A i=0,l=1I[0].G;i<l;i++){1I[2].2d(1I[1],Q[i],i,Q)}},bE:B(bF,Q,hC,M){A 1I=69(Q,M,hC);Q=1I[0];R(A i=0,l=Q.G;i<l;i++){A bG=!!1I[2].2d(1I[1],Q[i],i,Q);if(bF^bG){C bG}}C bF},ah:B(Q,hB,hA){C D.bE(K,Q,hB,hA)},ag:B(Q,hz,hy){C D.bE(U,Q,hz,hy)},23:B(Q,7t,M){A 1I=69(Q,M,7t);Q=1I[0];A bD=((P[3])?(S P[3]()):[]);R(A i=0;i<Q.G;++i){bD.Y(1I[2].2d(1I[1],Q[i],i,Q))}C bD},3T:B(Q,hx,M){A 1I=69(Q,M,hx);Q=1I[0];A bC=[];R(A i=0;i<Q.G;i++){if(1I[2].2d(1I[1],Q[i],i,Q)){bC.Y(Q[i])}}C bC}})})()}if(!z.1h["z.X.1J"]){z.1h["z.X.1J"]=K;z.1Q("z.X.1J");z.1J=B(bB){if(bB){D.hw(bB)}};z.1J.hp={p0:[0,0,0],oZ:[60,60,60],oY:[2j,2j,2j],oX:[1T,1T,1T],oW:[2j,0,0],oV:[1T,0,0],oU:[2j,0,2j],oT:[1T,0,1T],oS:[0,2j,0],oR:[0,1T,0],oQ:[2j,2j,0],oP:[1T,1T,0],oO:[0,0,2j],oN:[0,0,1T],oM:[0,2j,2j],oL:[0,1T,1T]};z.4M(z.1J,{r:1T,g:1T,b:1T,a:1,bz:B(r,g,b,a){A t=D;t.r=r;t.g=g;t.b=b;t.a=a},hw:B(2Q){A d=z;if(d.1R(2Q)){d.hq(2Q,D)}I{if(d.2l(2Q)){d.7P(2Q,D)}I{D.bz(2Q.r,2Q.g,2Q.b,2Q.a);if(!(2Q 1N d.1J)){D.7Q()}}}C D},7Q:B(){C D},oK:B(){A t=D;C[t.r,t.g,t.b]},oJ:B(){A t=D;C[t.r,t.g,t.b,t.a]},oI:B(){A Q=z.23(["r","g","b"],B(x){A s=D[x].2i(16);C s.G<2?"0"+s:s},D);C"#"+Q.22("")},8F:B(hv){A t=D,7S=t.r+", "+t.g+", "+t.b;C(hv?"hs("+7S+", "+t.a:"7S("+7S)+")"},2i:B(){C D.8F(K)}});z.d8=B(bA,1d,hu,M){A d=z,t=M||S z.1J();d.1n(["r","g","b","a"],B(x){t[x]=bA[x]+(1d[x]-bA[x])*hu;if(x!="a"){t[x]=2Y.oH(t[x])}});C t.7Q()};z.ho=B(ht,M){A m=ht.1M().1f(/^hs?\\(([\\s\\.,0-9]+)\\)/);C m&&z.7P(m[1].1A(/\\s*,\\s*/),M)};z.hn=B(4j,M){A d=z,t=M||S d.1J(),7R=(4j.G==4)?4:8,hr=(1<<7R)-1;4j=2V("oG"+4j.3b(1));if(2L(4j)){C L}d.1n(["b","g","r"],B(x){A c=4j&hr;4j>>=7R;t[x]=7R==4?17*c:c});t.a=1;C t};z.7P=B(a,M){A t=M||S z.1J();t.bz(2V(a[0]),2V(a[1]),2V(a[2]),2V(a[3]));if(2L(t.a)){t.a=1}C t.7Q()};z.hq=B(2H,M){A a=z.1J.hp[2H];C a&&z.7P(a,M)||z.ho(2H,M)||z.hn(2H,M)}}if(!z.1h["z.X"]){z.1h["z.X"]=K;z.1Q("z.X")}if(!z.1h["z.X.5Z"]){z.1h["z.X.5Z"]=K;z.1Q("z.X.5Z");(B(){A 1j=z.b2={2P:B(E,68,fp){if(!E){C}68=1j.4O(68);fp=1j.7G(68,fp);E.66(68,fp,U);C fp},3J:B(E,hm,hl){(E)&&(E.oF(1j.4O(hm),hl,U))},4O:B(1p){C(1p.2w(0,2)=="on"?1p.2w(2):1p)},7G:B(1p,fp){C(1p!="4b"?fp:B(e){C fp.2d(D,1j.4i(e,D))})},4i:B(H,oE){4w(H.Z){2X"4b":1j.7K(H);3f}C H},7K:B(H){H.oD=(H.3h?67.oC(H.3h):"")}};z.oB=B(H,hk){C 1j.4i(H,hk)};z.gY=B(H){H.7J();H.7I()};A 7O=z.3i;z.by=B(M,bx,hh,hg,hi){A hj=M&&(M.2t||M.oA||M.66);A bw=!hj?0:(!hi?1:2),l=[z.3i,1j,7O][bw];A h=l.2P(M,bx,z.2p(hh,hg));C[M,bx,h,bw]};z.bv=B(M,he,hd,hf){([z.3i,1j,7O][hf]).3J(M,he,hd)};z.5W={oz:8,gV:9,oy:12,ox:13,ow:16,ov:17,ou:18,gG:19,ot:20,os:27,or:32,b5:33,b4:34,gE:35,gF:36,b7:37,b9:38,b6:39,b8:40,gD:45,8S:46,oq:47,oo:91,om:92,ol:93,oj:96,oi:97,oh:98,og:99,oe:6D,od:oc,ob:oa,o9:o8,o7:o6,o5:o4,o3:bi,o2:o1,o0:nZ,nY:nX,nW:nV,nU:bk,gS:nT,gR:nS,gQ:nR,gP:nQ,gO:nP,gN:nO,gM:nN,gL:nM,gK:nL,gJ:nK,gI:nJ,gH:nI,nH:nG,nF:nE,nD:nC,gB:nB,gC:nA};if(z.1l){bf=B(e,5h){1u{C(e.3I=5h)}1y(e){C 0}};A 61=z.3i;if(!1o.nz){7O=61=z.gy={b3:[],2P:B(64,bu,hc){64=64||z.1W;A f=64[bu];if(!f||!f.2b){A d=z.gz();d.5V=f&&(7M.Y(f)-1);d.2b=[];f=64[bu]=d}C f.2b.Y(7M.Y(hc)-1)},3J:B(hb,ha,7N){A f=(hb||z.1W)[ha],l=f&&f.2b;if(f&&l&&7N--){63 7M[l[7N]];63 l[7N]}}};A 7M=61.b3}z.1x(1j,{2P:B(E,62,fp){if(!E){C}62=1j.4O(62);if(62=="h3"){A kd=E.bs;if(!kd||!kd.2b||!kd.h9){1j.2P(E,"bs",1j.h4);E.bs.h9=K}}C 61.2P(E,62,1j.7G(fp))},3J:B(E,h8,h7){61.3J(E,1j.4O(h8),h7)},4O:B(7L){C(7L.2w(0,2)!="on"?"on"+7L:7L)},ny:B(){},4i:B(H,4N){if(!H){A w=(4N)&&((4N.aD||4N.1q||4N).nx)||26;H=w.5Z}if(!H){C(H)}H.5V=H.br;H.bh=(4N||H.br);H.nw=H.nv;H.nu=H.nr;A bq=H.br,1e=(bq&&bq.aD)||1q;A bn=((z.1l<6)||(1e["aX"]=="aW"))?1e.3E:1e.5K;A bm=z.aB();H.nq=H.np+z.aH(bn.5I||0)-bm.x;H.nn=H.nm+(bn.5G||0)-bm.y;if(H.Z=="fk"){H.h6=H.nl}if(H.Z=="fj"){H.h6=H.nk}H.7I=1j.bc;H.7J=1j.ba;C 1j.h5(H)},h5:B(H){4w(H.Z){2X"4b":A c=("3h"in H?H.3h:H.3I);if(c==10){c=0;H.3I=13}I{if(c==13||c==27){c=0}I{if(c==3){c=99}}}H.3h=c;1j.7K(H);3f}C H},gZ:{bi:42,bk:47,h2:59,nj:43,ni:44,nh:45,ng:46,nf:47,60:96,h1:91,nb:92,na:93,h0:39},h4:B(H){A kp=H.bh.h3;if(!kp||!kp.2b){C}A k=H.3I;A bj=(k!=13)&&(k!=32)&&(k!=27)&&(k<48||k>90)&&(k<96||k>bk)&&(k<h2||k>60)&&(k<h1||k>h0);if(bj||H.5Y){A c=(bj?0:k);if(H.5Y){if(k==3||k==13){C}I{if(c>95&&c<bi){c-=48}I{if((!H.5X)&&(c>=65&&c<=90)){c+=32}I{c=1j.gZ[c]||c}}}}A 2x=1j.7H(H,{Z:"4b",2x:K,3h:c});kp.2d(H.bh,2x);H.bg=2x.bg;H.bd=2x.bd;bf(H,2x.3I)}},bc:B(){D.bg=K},ba:B(){D.n9=D.3I;if(D.5Y){bf(D,0)}D.bd=U}});z.gY=B(H){H=H||26.5Z;1j.bc.2d(H);1j.ba.2d(H)}}1j.7H=B(H,gX){A 2x=z.1x({},H,gX);1j.7K(2x);2x.7J=B(){H.7J()};2x.7I=B(){H.7I()};C 2x};if(z.2M){z.1x(1j,{4i:B(H,n8){4w(H.Z){2X"4b":A c=H.n7;if(c==3){c=99}c=((c<41)&&(!H.5X)?0:c);if((H.5Y)&&(!H.5X)&&(c>=65)&&(c<=90)){c+=32}C 1j.7H(H,{3h:c})}C H}})}if(z.3o){z.1x(1j,{4i:B(H,n6){4w(H.Z){2X"4b":A c=H.3h,s=H.5X,k=H.3I;k=k||gA[H.gW]||0;if(H.gW=="n5"){c=0}I{if((H.5Y)&&(c>0)&&(c<27)){c+=96}I{if(c==z.5W.gU){c=z.5W.gV;s=K}I{c=(c>=32&&c<gT?c:0)}}}C 1j.7H(H,{3h:c,5X:s,3I:k})}C H}});z.1x(z.5W,{gU:25,b9:gT,b8:n4,b7:n3,b6:n2,gS:n1,gR:n0,gQ:mZ,gP:mY,gO:mX,gN:mW,gM:mV,gL:mU,gK:mT,gJ:mS,gI:mR,gH:mQ,gG:mP,8S:mO,gF:mN,gE:mM,b5:mL,b4:mK,gD:mJ,mI:mH,gC:mG,gB:mF});A dk=z.5W,gA={"mE":dk.b9,"mD":dk.b8,"mC":dk.b7,"mB":dk.b6,"mA":dk.b5,"mz":dk.b4}}})();if(z.1l){z.gz=B(){C B(){A ap=4e.1C,h=z.gy.b3,c=P.2O,ls=c.2b,t=h[c.5V];A r=t&&t.14(D,P);R(A i in ls){if(!(i in ap)){h[ls[i]].14(D,P)}}C r}};z.b2.7G=B(fp){A f=z.b2.4i;C B(e){C fp.2d(D,f(e,D))}}}}if(!z.1h["z.X.b1"]){z.1h["z.X.b1"]=K;z.1Q("z.X.b1");1u{1q.my("mx",U,K)}1y(e){}if(z.1l||z.2M){z.1D=B(id,1e){if(z.1R(id)){A b0=(1e||z.1e);A 11=b0.gv(id);if((11)&&(11.gw.id.1Z==id)){C 11}I{A 5U=b0.gx[id];if(!5U){C}if(!5U.G){C 5U}A i=0;1s(11=5U[i++]){if(11.gw.id.1Z==id){C 11}}}}I{C id}}}I{z.1D=B(id,1e){if(z.1R(id)){C(1e||z.1e).gv(id)}I{C id}}}(B(){A 5T=L;z.mw=B(E){E=z.1D(E);1u{if(!5T){5T=1q.a9("mv")}5T.4c(E.1L?E.1L.fs(E):E);5T.9L=""}1y(e){}};z.mu=B(E,7F){1u{E=z.1D(E);7F=z.1D(7F);1s(E){if(E===7F){C K}E=E.1L}}1y(e){}C U};z.mt=B(E,5S){E=z.1D(E);if(z.gu){E.1c.ms=(5S)?"dg":"7C"}I{if(z.6B){E.1c.mr=(5S)?"8K":"7C"}I{if(z.1l){E.gs=(5S)?"":"on";z.1r("*",E).1n(B(gt){gt.gs=(5S)?"":"on"})}}}};A 5R=B(E,4h){4h.1L.mq(E,4h);C K};A aZ=B(E,4h){A pn=4h.1L;if(4h==pn.fm){pn.4c(E)}I{C 5R(E,4h.71)}C K};z.5E=B(E,2a,3H){if((!E)||(!2a)||(V 3H=="1k")){C U}E=z.1D(E);2a=z.1D(2a);if(V 3H=="4J"){A cn=2a.3W;if(((3H==0)&&(cn.G==0))||(cn.G==3H)){2a.4c(E);C K}if(3H==0){C 5R(E,2a.5A)}C aZ(E,cn[3H-1])}4w(3H.1M()){2X"mo":C 5R(E,2a);2X"a8":C aZ(E,2a);2X"9M":if(2a.5A){C 5R(E,2a.5A)}I{2a.4c(E);C K}3f;aY:2a.4c(E);C K}};z.aP="5g-3G";if(z.1l){A aV=1q.aX;z.aP=(aV=="aW")||(aV=="gr")||(z.1l<6)?"g5-3G":"5g-3G"}A 1E,dv=1q.mn;if(z.3o){1E=B(E){A s=dv.3F(E,L);if(!s&&E.1c){E.1c.gq="";s=dv.3F(E,L)}C s||{}}}I{if(z.1l){1E=B(E){C E.gn}}I{1E=B(E){C dv.3F(E,L)}}}z.3F=1E;if(!z.1l){z.4g=B(mm,gp){C 2k(gp)||0}}I{z.4g=B(go,2N){if(!2N){C 0}if(2N=="ml"){C 4}if(2N.2w&&(2N.2w(-2)=="px")){C 2k(2N)}4G(go){A gm=1c.2g;A gl=aU.2g;aU.2g=gn.2g;1u{1c.2g=2N;2N=1c.mk}1y(e){2N=0}1c.2g=gm;aU.2g=gl}C 2N}}z.ge=(z.1l?B(E){1u{C(E.mj.mi.2W/6D)}1y(e){C 1}}:B(E){C z.3F(E).2W});z.gf=(z.1l?B(E,7D){if(7D==1){E.1c.7E=E.1c.7E.2f(/gk:[^;]*;/i,"");if(E.gj.1M()=="gi"){z.1r("> gh",E).1n(B(i){i.1c.7E=i.1c.7E.2f(/gk:[^;]*;/i,"")})}}I{A o="mh(mg="+(7D*6D)+")";E.1c.3T=o}if(E.gj.1M()=="gi"){z.1r("> gh",E).1n(B(i){i.1c.3T=o})}C 7D}:B(E,gg){C E.1c.2W=gg});A 5Q={3n:K,58:K,2g:K,5J:K};A gd=B(E,Z,5P){Z=Z.1M();if(5Q[Z]===K){C z.4g(E,5P)}I{if(5Q[Z]===U){C 5P}I{if((Z.T("mf")>=0)||(Z.T("md")>=0)||(Z.T("3n")>=0)||(Z.T("58")>=0)||(Z.T("5q")>=0)||(Z.T("mc")>=0)||(Z.T("ma")>=0)){5Q[Z]=K;C z.4g(E,5P)}I{5Q[Z]=U;C 5P}}}};z.1c=B(E,5O,aT){A n=z.1D(E),F=P.G,op=(5O=="2W");if(F==3){C op?z.gf(n,aT):n.1c[5O]=aT}if(F==2&&op){C z.ge(n)}A s=z.3F(n);C(F==1)?s:gd(n,5O,s[5O])};z.7A=B(n,gc){A s=gc||1E(n),px=z.4g,l=px(n,s.m9),t=px(n,s.m8);C{l:l,t:t,w:l+px(n,s.m7),h:t+px(n,s.m6)}};z.5N=B(n,gb){A ne="7C",px=z.4g,s=gb||1E(n),bl=(s.m5!=ne?px(n,s.m4):0),bt=(s.m3!=ne?px(n,s.m2):0);C{l:bl,t:bt,w:bl+(s.m1!=ne?px(n,s.m0):0),h:bt+(s.lZ!=ne?px(n,s.lY):0)}};z.aN=B(n,ga){A s=ga||1E(n),p=z.7A(n,s),b=z.5N(n,s);C{l:p.l+b.l,t:p.t+b.t,w:p.w+b.w,h:p.h+b.h}};z.aM=B(n,g9){A s=g9||1E(n),px=z.4g,l=px(n,s.lX),t=px(n,s.lW),r=px(n,s.lV),b=px(n,s.lU);if(z.3o&&(s.ax!="fU")){r=l}C{l:l,t:t,w:l+r,h:t+b}};z.au=B(E,g8){A s=g8||1E(E),me=z.aM(E,s);A l=E.fT-me.l,t=E.fS-me.t;if(z.7B){A aS=2k(s.2g),aR=2k(s.5J);if(!2L(aS)&&!2L(aR)){l=aS,t=aR}I{A p=E.1L;if(p&&p.1c){A aQ=1E(p);if(aQ.lT!="lS"){A be=z.5N(p,aQ);l+=be.l,t+=be.t}}}}I{if(z.2M){A p=E.1L;if(p){A be=z.5N(p);l-=be.l,t-=be.t}}}C{l:l,t:t,w:E.6v+me.w,h:E.8D+me.h}};z.aK=B(E,g7){A s=g7||1E(E),pe=z.7A(E,s),be=z.5N(E,s),w=E.aF,h;if(!w){w=E.6v,h=E.8D}I{h=E.lR,be.w=be.h=0}if(z.2M){pe.l+=be.l;pe.t+=be.t}C{l:pe.l,t:pe.t,w:w-pe.w-be.w,h:h-pe.h-be.h}};z.lQ=B(E,g6){A s=g6||1E(E),pe=z.7A(E,s),cb=z.aK(E,s);C{l:cb.l-pe.l,t:cb.t-pe.t,w:cb.w+pe.w,h:cb.h+pe.h}};z.aL=B(E,l,t,w,h,u){u=u||"px";4G(E.1c){if(!2L(l)){2g=l+u}if(!2L(t)){5J=t+u}if(w>=0){3n=w+u}if(h>=0){58=h+u}}};z.aO=B(E){A n=E.5w;C(z.aP=="g5-3G")||(n=="lP")||(n=="lO")};z.fX=B(E,7z,7y,g4){A bb=z.aO(E);if(bb){A pb=z.aN(E,g4);if(7z>=0){7z+=pb.w}if(7y>=0){7y+=pb.h}}z.aL(E,g3,g3,7z,7y)};z.fY=B(E,g1,g0,5M,5L,g2){A s=g2||z.3F(E);A bb=z.aO(E),pb=bb?fZ:z.aN(E,s),mb=z.aM(E,s);if(5M>=0){5M=2Y.5q(5M-pb.w-mb.w,0)}if(5L>=0){5L=2Y.5q(5L-pb.h-mb.h,0)}z.aL(E,g1,g0,5M,5L)};A fZ={l:0,t:0,w:0,h:0};z.lN=B(E,3G){A n=z.1D(E),s=1E(n),b=3G;C!b?z.au(n,s):z.fY(n,b.l,b.t,b.w,b.h,s)};z.lM=B(E,3G){A n=z.1D(E),s=1E(n),b=3G;C!b?z.aK(n,s):z.fX(n,b.w,b.h,s)};A 5H=B(E,1a){if(!(E=(E||0).1L)){C 0}A 1U,aJ=0,2h=z.3E();1s(E&&E.1c){if(1E(E).ax=="lL"){C 0}1U=E[1a];if(1U){aJ+=1U-0;if(E==2h){3f}}E=E.1L}C aJ};z.fQ=B(){A 2h=z.3E();A 3g=z.1W;A de=z.1e.5K;C{y:(3g.lK||de.5G||2h.5G||0),x:(3g.lJ||z.aH(de.5I)||2h.5I||0)}};z.aG=B(){C V z.aI=="1k"?(z.aI=z.3F(z.3E()).lI=="lH"):z.aI};z.aB=B(){A de=z.1e.5K;if(z.1l>=7){C{x:de.aC().2g,y:de.aC().5J}}I{C{x:z.aG()||26.am==26?de.fW:de.6v-de.aF-de.fW,y:de.lG}}};z.aH=B(aE){if(z.1l&&!z.aG()){A de=z.1e.5K;C aE+de.aF-de.lF}C aE};z.fP=B(E,aw){A ay=E.aD;A J={x:0,y:0};A 7w=U;A db=z.3E();if(z.1l){A aA=E.aC();A az=z.aB();J.x=aA.2g-az.x;J.y=aA.5J-az.y}I{if(ay["fV"]){A bo=ay.fV(E);J.x=bo.x-5H(E,"5I");J.y=bo.y-5H(E,"5G")}I{if(E["fR"]){7w=K;A 7x;if(z.3o&&(1E(E).ax=="fU")&&(E.1L==db)){7x=db}I{7x=db.1L}if(E.1L!=db){A nd=E;if(z.2M){nd=db}J.x-=5H(nd,"5I");J.y-=5H(nd,"5G")}A 4f=E;do{A n=4f["fT"];if(!z.2M||n>0){J.x+=2L(n)?0:n}A m=4f["fS"];J.y+=2L(m)?0:m;4f=4f.fR}1s((4f!=7x)&&4f)}I{if(E["x"]&&E["y"]){J.x+=2L(E.x)?0:E.x;J.y+=2L(E.y)?0:E.y}}}}if(7w||aw){A av=z.fQ();A m=7w?(!aw?-1:0):1;J.y+=m*av.y;J.x+=m*av.x}C J};z.af=B(E,fO){A n=z.1D(E),s=1E(n),mb=z.au(n,s);A at=z.fP(n,fO);mb.x=at.x;mb.y=at.y;C mb}})();z.fL=B(E,fN){C((" "+E.3A+" ").T(" "+fN+" ")>=0)};z.7s=B(E,ar){A 7v=E.3A;if((" "+7v+" ").T(" "+ar+" ")<0){E.3A=7v+(7v?" ":"")+ar}};z.7r=B(E,fM){A t=z.7g((" "+E.3A+" ").2f(" "+fM+" "," "));if(E.3A!=t){E.3A=t}};z.lE=B(E,aq,7u){if(V 7u=="1k"){7u=!z.fL(E,aq)}z[7u?"7s":"7r"](E,aq)}}if(!z.1h["z.X.1H"]){z.1h["z.X.1H"]=K;z.1Q("z.X.1H");(B(){A d=z;z.1H=B(){A F=P;if((F.G==1)&&(V F[0]=="4J")){D.G=eK(F[0])}I{if(F.G){d.1n(F,B(i){D.Y(i)},D)}}};z.1H.1C=S 4e;if(d.1l){A fK=B(al){C("A a2 = am."+al+"; "+"A ap = 4e.1C; "+"A ao = a2.1C; "+"R(A x in ao){ ap[x] = ao[x]; } "+"am."+al+" = 4e; ")};A fI=fK("z.1H");A aj=26.lD();aj.1q.fJ("<ak>"+fI+"</ak>");aj.lC(1,1,1,1)}z.4M(z.1H,{T:B(fH,fG){C d.T(D,fH,fG)},31:B(lB,lA){A aa=d.4d(P);aa.ae(D);C d.31.14(d,aa)},ah:B(fF,fE){C d.ah(D,fF,fE)},ag:B(fD,fC){C d.ag(D,fD,fC)},1n:B(fB,fA){d.1n(D,fB,fA);C D},23:B(7t,M){C d.23(D,7t,M,d.1H)},af:B(){C d.23(D,d.af)},1c:B(lz,ly){A aa=d.4d(P);aa.ae(D[0]);A s=d.1c.14(d,aa);C(P.G>1)?D:s},lx:B(lw,lv){A aa=d.4d(P);aa.ae(L);A s=D.23(B(i){aa[0]=i;C d.1c.14(d,aa)});C(P.G>1)?D:s},7s:B(fz){C D.1n(B(i){z.7s(i,fz)})},7r:B(fy){C D.1n(B(i){z.7r(i,fy)})},5E:B(fw,7q){A 1m=d.1r(fw)[0];7q=7q||"72";R(A x=0;x<D.G;x++){d.5E(D[x],1m,7q)}C D},2c:B(fv,fu,ft){D.1n(B(1m){d.2c(1m,fv,fu,ft)});C D},lu:B(ad){A ac=(ad)?d.9t(D,ad):D;ac.1n(B(1m){if(1m["1L"]){1m.1L.fs(1m)}});C ac},lt:B(fr,fq){A 1m=D[0];C d.1r(fr).1n(B(ai){d.5E(ai,1m,(fq||"72"))})},1r:B(7p){7p=7p||"";A J=S d.1H();D.1n(B(1m){d.1r(7p,1m).1n(B(ab){if(V ab!="1k"){J.Y(ab)}})});C J},3T:B(fo){A 5F=D;A 1V=P;A r=S d.1H();A rp=B(t){if(V t!="1k"){r.Y(t)}};if(d.1R(fo)){5F=d.9t(D,1V[0]);if(1V.G==1){C 5F}d.1n(d.3T(5F,1V[1],1V[2]),rp);C r}d.1n(d.3T(5F,1V[0],1V[1]),rp);C r},lr:B(7o,7n){A 1S=d.1e.a9("lq");if(d.1R(7o)){1S.9L=7o}I{1S.4c(7o)}A ct=((7n=="9M")||(7n=="a8"))?"fm":"5A";D.1n(B(1m){A 24=1S.a7(K);1s(24[ct]){d.5E(24[ct],1m,7n)}});C D},7m:B(fl,F){A a5=[];F=F||{};D.1n(B(1m){A a6={E:1m};d.1x(a6,F);a5.Y(d[fl](a6))});C d.fx.lp(a5)},8I:B(F){C D.7m("8I",F)},8H:B(F){C D.7m("8H",F)},6y:B(F){C D.7m("6y",F)}});z.1n(["fk","lo","fj","fi","ln","lm","ll","fi","lk","lj","4b"],B(H){A a4="on"+H;z.1H.1C[a4]=B(a,b){C D.2c(a4,a,b)}})})()}if(!z.1h["z.X.1r"]){z.1h["z.X.1r"]=K;z.1Q("z.X.1r");(B(){A d=z;A 2I=B(q){C[q.T("#"),q.T("."),q.T("["),q.T(":")]};A a0=B(a3,fh){A ql=a3.G;A i=2I(a3);A 1d=ql;R(A x=fh;x<i.G;x++){if(i[x]>=0){if(i[x]<1d){1d=i[x]}}}C(1d<0)?ql:1d};A 6X=B(7l){A i=2I(7l);if(i[0]!=-1){C 7l.21(i[0]+1,a0(7l,1))}I{C""}};A 5r=B(7k){A 5D;A i=2I(7k);if((i[0]==0)||(i[1]==0)){5D=0}I{5D=a0(7k,0)}C((5D>0)?7k.3b(0,5D).1M():"*")};A fg=B(Q){A J=-1;R(A x=0;x<Q.G;x++){A 1S=Q[x];if(1S>=0){if((1S>J)||(J==-1)){J=1S}}}C J};A 9H=B(7i){A i=2I(7i);if(-1==i[1]){C""}A di=i[1]+1;A 7j=fg(i.2w(2));if(di<7j){C 7i.21(di,7j)}I{if(-1==7j){C 7i.3b(di)}I{C""}}};A f3=[{1i:"|=",1f:B(15,fe){C"[5z(3U(\' \',@"+15+",\' \'), \' "+fe+"-\')]"}},{1i:"~=",1f:B(15,fd){C"[5z(3U(\' \',@"+15+",\' \'), \' "+fd+" \')]"}},{1i:"^=",1f:B(15,fb){C"[li-4G(@"+15+", \'"+fb+"\')]"}},{1i:"*=",1f:B(15,fa){C"[5z(@"+15+", \'"+fa+"\')]"}},{1i:"$=",1f:B(15,9Z){C"[21(@"+15+", 3c-G(@"+15+")-"+(9Z.G-1)+")=\'"+9Z+"\']"}},{1i:"!=",1f:B(15,f9){C"[3O(@"+15+"=\'"+f9+"\')]"}},{1i:"=",1f:B(15,f8){C"[@"+15+"=\'"+f8+"\']"}}];A 9C=B(9Y,3Z,f7,f6){A 49;A i=2I(3Z);if(i[2]>=0){A 4L=3Z.T("]",i[2]);A 29=3Z.21(i[2]+1,4L);1s(29&&29.G){if(29.2s(0)=="@"){29=29.2w(1)}49=L;R(A x=0;x<9Y.G;x++){A 1S=9Y[x];A 7h=29.T(1S.1i);if(7h>=0){A 15=29.21(0,7h);A 4a=29.21(7h+1S.1i.G);if((4a.2s(0)=="\\"")||(4a.2s(0)=="\'")){4a=4a.21(1,4a.G-1)}49=1S.1f(d.7g(15),d.7g(4a));3f}}if((!49)&&(29.G)){49=f7(29)}if(49){f6(49)}29=L;A 7f=3Z.T("[",4L);if(0<=7f){4L=3Z.T("]",7f);if(0<=4L){29=3Z.21(7f+1,4L)}}}}};A f0=B(f5){A 4K=".";A 7e=f5.1A(" ");1s(7e.G){A 2K=7e.3a();A 7d;if(2K==">"){7d="/";2K=7e.3a()}I{7d="//"}A f4=5r(2K);4K+=7d+f4;A id=6X(2K);if(id.G){4K+="[@id=\'"+id+"\'][1]"}A cn=9H(2K);if(cn.G){A 9X=" ";if(cn.2s(cn.G-1)=="*"){9X="";cn=cn.3b(0,cn.G-1)}4K+="[5z(3U(\' \',@9P,\' \'), \' "+cn+9X+"\')]"}9C(f3,2K,B(f2){C"[@"+f2+"]"},B(f1){4K+=f1})}C 4K};A 7a={};A eC=B(28){if(7a[28]){C 7a[28]}A 1e=d.1e;A 9W=f0(28);A 4H=B(9V){A J=[];A 7b;1u{7b=1e.9x(9W,9V,L,lh.lg,L)}1y(e){1z.1K("lf in le:",9W,"lc:",9V);1z.1K(e)}A 7c=7b.eZ();1s(7c){J.Y(7c);7c=7b.eZ()}C J};C 7a[28]=4H};A 5x={};A 9B={};A 3y=B(79,78){if(!79){C 78}if(!78){C 79}C B(){C 79.14(26,P)&&78.14(26,P)}};A 75=B(9U,3Y,5B,2J){A 2v=2J+1;A 76=(3Y.G==2v);A 2K=3Y[2J];if(2K==">"){A 77=9U.3W;if(!77.G){C}2v++;76=(3Y.G==2v);A 4H=6O(3Y[2J+1]);R(A x=0,11;x<77.G,11=77[x];x++){if(4H(11)){if(76){5B.Y(11)}I{75(11,3Y,5B,2v)}}}}A 5C=6U(2K)(9U);if(76){1s(5C.G){5B.Y(5C.3a())}}I{1s(5C.G){75(5C.3a(),3Y,5B,2v)}}};A eE=B(9T,eY){A J=[];A x=9T.G-1,11;1s(11=9T[x--]){75(11,eY,J,0)}C J};A 6O=B(3D){if(5x[3D]){C 5x[3D]}A ff=L;A 9S=5r(3D);if(9S!="*"){ff=3y(ff,B(N){C((N.2t==1)&&(9S==N.5w.1M()))})}A 9R=6X(3D);if(9R.G){ff=3y(ff,B(N){C((N.2t==1)&&(N.id==9R))})}if(2Y.5q.14(D,2I(3D).2w(1))>=0){ff=3y(ff,9z(3D))}C 5x[3D]=ff};A 5y=B(E){A pn=E.1L;A 9Q=pn.3W;A 2v=-1;A 3C=pn.5A;if(!3C){C 2v}A ci=E["eW"];A cl=pn["eX"];if(((V cl=="4J")&&(cl!=9Q.G))||(V ci!="4J")){pn["eX"]=9Q.G;A 2J=1;do{if(3C===E){2v=2J}if(3C.2t==1){3C["eW"]=2J;2J++}3C=3C.71}1s(3C)}I{2v=ci}C 2v};A lb=0;A 3X=B(N,15){A 74="";if(15=="9P"){C N.3A||74}if(15=="R"){C N.la||74}C N.5t(15,2)||74};A eH=[{1i:"|=",1f:B(15,9O){A eV=" "+9O+"-";C B(N){A ea=" "+(N.5t(15,2)||"");C((ea==9O)||(ea.T(eV)==0))}}},{1i:"^=",1f:B(15,eU){C B(N){C(3X(N,15).T(eU)==0)}}},{1i:"*=",1f:B(15,eT){C B(N){C(3X(N,15).T(eT)>=0)}}},{1i:"~=",1f:B(15,eS){A 9N=" "+eS+" ";C B(N){A ea=" "+3X(N,15)+" ";C(ea.T(9N)>=0)}}},{1i:"$=",1f:B(15,73){A 9N=" "+73;C B(N){A ea=" "+3X(N,15);C(ea.31(73)==(ea.G-73.G))}}},{1i:"!=",1f:B(15,eR){C B(N){C(3X(N,15)!=eR)}}},{1i:"=",1f:B(15,eQ){C B(N){C(3X(N,15)==eQ)}}}];A 9E=[{1i:"9M-9K",1f:B(1p,l9){C B(N){if(N.2t!=1){C U}A fc=N.eP;1s(fc&&(fc.2t!=1)){fc=fc.eP}C(!fc)}}},{1i:"72-9K",1f:B(1p,l8){C B(N){if(N.2t!=1){C U}A nc=N.71;1s(nc&&(nc.2t!=1)){nc=nc.71}C(!nc)}}},{1i:"l7",1f:B(1p,l6){C B(N){A cn=N.3W;A eO=N.3W.G;R(A x=eO-1;x>=0;x--){A nt=cn[x].2t;if((nt==1)||(nt==3)){C U}}C K}}},{1i:"5z",1f:B(1p,eN){C B(N){C(N.9L.T(eN)>=0)}}},{1i:"3O",1f:B(1p,eM){A eL=6O(eM);C B(N){C(!eL(N))}}},{1i:"l5-9K",1f:B(1p,2u){A pi=eK;if(2u=="l4"){C B(N){C(((5y(N))%2)==1)}}I{if((2u=="2n")||(2u=="l3")){C B(N){C((5y(N)%2)==0)}}I{if(2u.T("l2+")==0){A 70=pi(2u.3b(3));C B(N){C(N.1L.3W[70-1]===N)}}I{if((2u.T("n+")>0)&&(2u.G>3)){A 9J=2u.1A("n+",2);A eJ=pi(9J[0]);A 2J=pi(9J[1]);C B(N){C((5y(N)%eJ)==2J)}}I{if(2u.T("n")==-1){A 70=pi(2u);C B(N){C(5y(N)==70)}}}}}}}}];A 9z=B(3e){A 9I=(9B[3e]||5x[3e]);if(9I){C 9I}A ff=L;A i=2I(3e);if(i[0]>=0){A 24=5r(3e);if(24!="*"){ff=3y(ff,B(N){C(N.5w.1M()==24)})}}A 5u;A 3B=9H(3e);if(3B.G){A 9F=3B.2s(3B.G-1)=="*";if(9F){3B=3B.3b(0,3B.G-1)}A re=S 9G("(?:^|\\\\s)"+3B+(9F?".*":"")+"(?:\\\\s|$)");ff=3y(ff,B(N){C re.6Z(N.3A)})}if(i[3]>=0){A 3z=3e.3b(i[3]+1);A 9D="";A 5v=3z.T("(");A 6Y=3z.31(")");if((0<=5v)&&(0<=6Y)&&(6Y>5v)){9D=3z.21(5v+1,6Y);3z=3z.3b(0,5v)}5u=L;R(A x=0;x<9E.G;x++){A 1S=9E[x];if(1S.1i==3z){5u=1S.1f(3z,9D);3f}}if(5u){ff=3y(ff,5u)}}A eG=(d.1l)?B(5s){A eI=5s.1M();C B(N){C N[5s]||N[eI]}}:B(5s){C B(N){C(N&&N.5t&&N.l1(5s))}};9C(eH,3e,eG,B(eF){ff=3y(ff,eF)});if(!ff){ff=B(){C K}}C 9B[3e]=ff};A 6W={};A 6U=B(3d,1B){A 9A=6W[3d];if(9A){C 9A}A i=2I(3d);A id=6X(3d);if(i[0]==0){C 6W[3d]=B(1B){C[d.1D(id)]}}A 9y=9z(3d);A 5p;if(i[0]>=0){5p=B(1B){A 11=d.1D(id);if(9y(11)){C[11]}}}I{A 3V;A 24=5r(3d);if(2Y.5q.14(D,2I(3d))==-1){5p=B(1B){A J=[];A 11,x=0,3V=1B.4I(24);1s(11=3V[x++]){J.Y(11)}C J}}I{5p=B(1B){A J=[];A 11,x=0,3V=1B.4I(24);1s(11=3V[x++]){if(9y(11)){J.Y(11)}}C J}}}C 6W[3d]=5p};A l0={};A 5o={">":B(1B){A J=[];A 11,x=0,3V=1B.3W;1s(11=3V[x++]){if(11.2t==1){J.Y(11)}}C J}};A 9w=B(6V){if(0>6V.T(" ")){C 6U(6V)}A eD=B(1B){A 6S=6V.1A(" ");A 6T;if(6S[0]==">"){6T=[1B]}I{6T=6U(6S.3a())(1B)}C eE(6T,6S)};C eD};A 9v=((1q["9x"]&&!d.3o)?B(3x){A 6R=3x.1A(" ");if((1q["9x"])&&(3x.T(":")==-1)&&((K))){if(((6R.G>2)&&(3x.T(">")==-1))||(6R.G>3)||(3x.T("[")>=0)||((1==6R.G)&&(0<=3x.T(".")))){C eC(3x)}}C 9w(3x)}:9w);A ey=B(3w){if(5o[3w]){C 5o[3w]}if(0>3w.T(",")){C 5o[3w]=9v(3w)}I{A eB=3w.1A(/\\s*,\\s*/);A 4H=B(1B){A eA=0;A J=[];A 6Q;1s(6Q=eB[eA++]){J=J.3U(9v(6Q,6Q.T(" "))(1B))}C J};C 5o[3w]=4H}};A 5n=0;A ez=B(Q){A J=S d.1H();if(!Q){C J}if(Q[0]){J.Y(Q[0])}if(Q.G<2){C J}5n++;Q[0]["9u"]=5n;R(A x=1,11;11=Q[x];x++){if(Q[x]["9u"]!=5n){J.Y(11)}11["9u"]=5n}C J};d.1r=B(6P,1B){if(V 6P!="3c"){C S d.1H(6P)}if(V 1B=="3c"){1B=d.1D(1B)}C ez(ey(6P)(1B||d.1e))};d.9t=B(ex,9s){A 9r=S d.1H();A ff=(9s)?6O(9s):B(){C K};R(A x=0,11;11=ex[x];x++){if(ff(11)){9r.Y(11)}}C 9r}})()}if(!z.1h["z.X.1b"]){z.1h["z.X.1b"]=K;z.1Q("z.X.1b");z.6K=B(ew){A J={};A iq="kZ[Z!=9q][Z!=kY][Z!=et][Z!=kX][Z!=kW], kV, kU";z.1r(iq,ew).3T(B(E){C(!E.kT)}).1n(B(1m){A 3v=1m.1p;A Z=(1m.Z||"").1M();if((Z=="kS")||(Z=="kR")){if(1m.kQ){J[3v]=1m.1Z}}I{if(1m.kP){A ev=J[3v]=[];z.1r("kO[kN]",1m).1n(B(eu){ev.Y(eu.1Z)})}I{J[3v]=1m.1Z;if(Z=="et"){J[3v+".x"]=J[3v+".y"]=J[3v].x=J[3v].y=0}}}});C J};z.9h=B(23){A ec=kM;A J="";A es={};R(A x in 23){if(23[x]!=es[x]){if(z.2l(23[x])){R(A y=0;y<23[x].G;y++){J+=ec(x)+"="+ec(23[x][y])+"&"}}I{J+=ec(x)+"="+ec(23[x])+"&"}}}if((J.G)&&(J.2s(J.G-1)=="&")){J=J.3b(0,J.G-1)}C J};z.kL=B(er){C z.9h(z.6K(er))};z.kK=B(ep){C z.eq(z.6K(ep))};z.kJ=B(2H){A J={};A qp=2H.1A("&");A dc=kI;z.1n(qp,B(1m){if(1m.G){A 9p=1m.1A("=");A 1p=dc(9p.3a());A 1U=dc(9p.22("="));if(z.1R(J[1p])){J[1p]=[J[1p]]}if(z.2l(J[1p])){J[1p].Y(1U)}I{J[1p]=1U}}});C J};z.e1=U;z.e6={"9g":B(1b){C 1b.2G},"2e":B(1b){if(!1o.eo){1z.1K("kH kG kF a kE of 9g/2e-6M-9m"+" 4F kD kC kB kA 4G en kz"+" (ky 1o.eo=K 4F kx kw D kv)")}C z.5m(1b.2G)},"2e-6M-ku":B(1b){A 6N=1b.2G;A 9o=6N.T("/*");A 9n=6N.31("*/");if((9o==-1)||(9n==-1)){C z.5m(1b.2G)}C z.5m(6N.21(9o+2,9n))},"2e-6M-9m":B(1b){A 6L=1b.2G;A 9l=6L.T("/*");A 9k=6L.31("*/");if((9l==-1)||(9k==-1)){1z.1K("kt en ks\'t 6M 9m!");C""}C z.5m(6L.21(9l+2,9k))},"kr":B(1b){C z.3u(1b.2G)},"kq":B(1b){if(z.1l&&!1b.el){z.1n(["ko","em","kn","km"],B(i){1u{A 1e=S 9j(kl[i]+".kk");1e.kj=U;1e.ki(1b.2G);C 1e}1y(e){}})}I{C 1b.el}}};(B(){z.e5=B(F,ej,ei,eh){A 2F={};2F.F=F;A 6J=L;if(F.3R){A 3R=z.1D(F.3R);A 9i=3R.kh("kg");2F.2E=F.2E||(9i?9i.1Z:L);6J=z.6K(3R)}I{2F.2E=F.2E}A 5l=[{}];if(6J){5l.Y(6J)}if(F.5g){5l.Y(F.5g)}if(F.ek){5l.Y({"z.ek":S 5d().8O()})}2F.1r=z.9h(z.1x.14(L,5l));2F.9d=F.9d||"9g";A d=S z.30(ej);d.5k(ei,B(eg){C eh(eg,d)});A ld=F.4E;if(ld&&z.1Y(ld)){d.ef(B(ee){C ld.2d(F,ee,2F)})}A 1G=F.9f;if(1G&&z.1Y(1G)){d.ed(B(e9){C 1G.2d(F,e9,2F)})}A 6I=F.kf;if(6I&&z.1Y(6I)){d.9e(B(e8){C 6I.2d(F,e8,2F)})}d.1F=2F;C d};A e4=B(O){O.e0=K;A 1b=O.1F.1b;if(V 1b.e7=="B"){1b.e7()}};A e3=B(O){C z.e6[O.1F.9d](O.1F.1b)};A e2=B(9c,O){1z.1K(9c);C 9c};A 3Q=B(F){A O=z.e5(F,e4,e3,e2);O.1F.1b=z.9b(O.1F.F);C O};A 5j=L;A 3t=[];A 94=B(){A dZ=(S 5d()).dU();if(!z.e1){z.1n(3t,B(4D,6H){if(!4D){C}A O=4D.O;1u{if(!O||O.e0||!4D.dT(O)){3t.3S(6H,1);C}if(4D.dR(O)){3t.3S(6H,1);4D.dP(O)}I{if(O.9a){if(O.9a+(O.1F.F.6G||0)<dZ){3t.3S(6H,1);A 1G=S 1O("6G ke");1G.dY="6G";O.5i(1G);O.4C()}}}}1y(e){1z.1K(e);O.5i(S 1O("kc!"))}})}if(!3t.G){dX(5j);5j=L;C}};z.dV=B(){1u{z.1n(3t,B(i){i.O.4C()})}1y(e){}};if(z.1l){z.dW(z.dV)}z.dH=B(O,dS,dQ,dO){if(O.1F.F.6G){O.9a=(S 5d()).dU()}3t.Y({O:O,dT:dS,dR:dQ,dP:dO});if(!5j){5j=dN(94,50)}94()};A dJ="8Z/x-kb-3R-ka";A dG=B(O){C O.1F.1b.6F};A dF=B(O){C 4==O.1F.1b.6F};A dE=B(O){if(z.8Y(O.1F.1b)){O.dM(O)}I{O.5i(S 1O("k9 k8 k7 5h:"+O.1F.1b.3N))}};A 3P=B(Z,O){A 3s=O.1F;A F=3s.F;3s.1b.dL(Z,3s.2E,(F.k6!==K),(F.8X?F.8X:1k),(F.8W?F.8W:1k));if(F.6E){R(A 5f in F.6E){if(5f.1M()==="5g-Z"&&!F.8V){F.8V=F.6E[5f]}I{3s.1b.dK(5f,F.6E[5f])}}}3s.1b.dK("k5-k4",(F.8V||dJ));1u{3s.1b.dI(3s.1r)}1y(e){O.4C()}z.dH(O,dG,dF,dE);C O};z.8T=B(4B){if(4B.1r.G){4B.2E+=(4B.2E.T("?")==-1?"?":"&")+4B.1r;4B.1r=L}};z.k3=B(F){A O=3Q(F);z.8T(O.1F);C 3P("dD",O)};z.k2=B(F){C 3P("dC",3Q(F))};z.k1=B(F){A O=3Q(F);O.1F.1r=F.k0;C 3P("dC",O)};z.jZ=B(F){C 3P("dA",3Q(F))};z.jY=B(F){A O=3Q(F);A dB=O.1F;if(F["8U"]){dB.1r=F.8U;F.8U=L}C 3P("dA",O)};z.jX=B(F){A O=3Q(F);z.8T(O.1F);C 3P("8S",O)};z.dz=B(jW){2m S 1O("z.dz 3O jV jU")}})()}if(!z.1h["z.X.fx"]){z.1h["z.X.fx"]=K;z.1Q("z.X.fx");z.dx=B(dy,1d){D.1w=dy;D.1d=1d;D.4x=B(n){C((D.1d-D.1w)*n)+D.1w}};z.2r("z.d6",L,{1P:B(F){z.1x(D,F);if(z.2l(D.2C)){D.2C=S z.dx(D.2C[0],D.2C[1])}},2C:L,8Q:jT,5a:L,4z:0,dj:10,du:L,6x:L,dt:L,8B:L,dh:L,ds:L,dr:L,dm:L,2D:U,2Z:U,4A:L,8N:L,3r:L,2o:0,4y:0,3q:B(H,F){if(D[H]){D[H].14(D,F||[])}C D},5b:B(dw,8R){if(8R){5e(D.3r);D.2D=D.2Z=U;D.2o=0}I{if(D.2D&&!D.2Z){C D}}D.3q("6x");A d=dw||D.du;if(d>0){5c(z.2p(D,B(){D.5b(L,8R)}),d);C D}D.4A=S 5d().8O();if(D.2Z){D.4A-=D.8Q*D.2o}D.8N=D.4A+D.8Q;D.2D=K;D.2Z=U;A 8P=D.2C.4x(D.2o);if(!D.2o){if(!D.4y){D.4y=D.4z}D.3q("dt",[8P])}D.3q("ds",[8P]);D.8M();C D},jS:B(){5e(D.3r);if(!D.2D){C D}D.2Z=K;D.3q("dr",[D.2C.4x(D.2o)]);C D},jR:B(dq,dp){5e(D.3r);D.2D=D.2Z=K;D.2o=dq*6D;if(dp){D.5b()}C D},jQ:B(dn){if(!D.3r){C}5e(D.3r);if(dn){D.2o=1}D.3q("dm",[D.2C.4x(D.2o)]);D.2D=D.2Z=U;C D},3N:B(){if(D.2D){C D.2Z?"3M":"jP"}C"jO"},8M:B(){5e(D.3r);if(D.2D){A dl=S 5d().8O();A 2q=(dl-D.4A)/(D.8N-D.4A);if(2q>=1){2q=1}D.2o=2q;if(D.5a){2q=D.5a(2q)}D.3q("8B",[D.2C.4x(2q)]);if(2q<1){D.3r=5c(z.2p(D,"8M"),D.dj)}I{D.2D=U;if(D.4z>0){D.4z--;D.5b(L,K)}I{if(D.4z==-1){D.5b(L,K)}I{if(D.4y){D.4z=D.4y;D.4y=0}}}D.2o=0;D.3q("dh")}}C D}});(B(){A df=B(E){if(z.1l){A ns=E.1c;if(!ns.8L.G&&z.1c(E,"8L")=="dg"){ns.8L="1"}if(!ns.3n.G&&z.1c(E,"3n")=="8K"){ns.3n="8K"}}};z.6C=B(F){if(V F.1d=="1k"){2m S 1O("z.6C jN an 1d 1Z")}F.E=z.1D(F.E);A 3p=z.1x({6w:{}},F);A 8J=(3p.6w.2W={});8J.1w=(V 3p.1w=="1k")?B(){C 2V(z.1c(3p.E,"2W"))}:3p.1w;8J.1d=3p.1d;A 2U=z.6y(3p);z.2c(2U,"6x",L,B(){df(3p.E)});C 2U};z.8I=B(F){C z.6C(z.1x({1d:1},F))};z.8H=B(F){C z.6C(z.1x({1d:0},F))};if(z.6B&&!z.3o){z.8E=B(n){C 2k("0.5")+((2Y.da((n+2k("1.5"))*2Y.d9))/2)}}I{z.8E=B(n){C 0.5+((2Y.da((n+1.5)*2Y.d9))/2)}}A d4=B(6A){D.8G=6A;R(A p in 6A){A 1a=6A[p];if(1a.1w 1N z.1J){1a.d7=S z.1J()}}D.4x=B(r){A J={};R(A p in D.8G){A 1a=D.8G[p];A 6z=L;if(1a.1w 1N z.1J){6z=z.d8(1a.1w,1a.1d,r,1a.d7).8F()}I{if(!z.2l(1a.1w)){6z=((1a.1d-1a.1w)*r)+1a.1w+(p!="2W"?1a.jM||"px":"")}}J[p]=6z}C J}};z.6y=B(F){F.E=z.1D(F.E);if(!F.5a){F.5a=z.8E}A 2U=S z.d6(F);z.2c(2U,"6x",2U,B(){A pm={};R(A p in D.6w){A 1a=pm[p]=z.1x({},D.6w[p]);if(z.1Y(1a.1w)){1a.1w=1a.1w()}if(z.1Y(1a.1d)){1a.1d=1a.1d()}A d5=(p.1M().T("jL")>=0);B 8C(E,p){4w(p){2X"58":C E.8D;2X"3n":C E.6v}A v=z.1c(E,p);C(p=="2W")?2V(v):2k(v)};if(V 1a.1d=="1k"){1a.1d=8C(D.E,p)}I{if(V 1a.1w=="1k"){1a.1w=8C(D.E,p)}}if(d5){1a.1w=S z.1J(1a.1w);1a.1d=S z.1J(1a.1d)}I{1a.1w=(p=="2W")?2V(1a.1w):2k(1a.1w)}}D.2C=S d4(pm)});z.2c(2U,"8B",2U,B(8A){R(A s in 8A){z.1c(D.E,s,8A[s])}});C 2U}})()}',62,1711,'|||||||||||||||||||||||||||||||||||dojo|var|function|return|this|node|args|length|evt|else|ret|true|null|obj|elem|dfd|arguments|arr|for|new|indexOf|false|typeof||_base|push|type||te|||apply|attr|||||prop|xhr|style|end|doc|match|uri|_hasResource|key|del|undefined|isIE|item|forEach|djConfig|name|document|query|while|_66|try|res|start|mixin|catch|console|split|root|prototype|byId|gcs|ioArgs|err|NodeList|_p|Color|debug|parentNode|toLowerCase|instanceof|Error|constructor|provide|isString|ta|255|val|_a|global|_69|isFunction|value||substring|join|map|tn||window||path|_343|_220|_listeners|connect|call|json|replace|left|_b|toString|128|parseFloat|isArray|throw||_percent|hitch|step|declare|charAt|nodeType|_3c3|nidx|slice|faux|fired|_c4|_7e|loc|curve|_active|url|_44c|responseText|str|_312|idx|tqp|isNaN|isOpera|_22d|callee|add|_18b|_f8|_e2|_41|anim|Number|opacity|case|Math|_paused|Deferred|lastIndexOf|||||||||shift|substr|string|_3e7|_3ce|break|_w|charCode|_listener|_d5|_c5|authority|_49|width|isSafari|_49e|fire|_timer|_47b|_465|eval|_in|_40c|_409|_362|_3d9|className|_3d5|_386|_37a|body|getComputedStyle|box|_221|keyCode|remove|_8d|_46|paused|status|not|_478|_461|form|splice|filter|concat|tret|childNodes|_38b|_367|_33d||||||||||_340|_348|keypress|appendChild|_toArray|Array|_2b0|_toPixelValue|ref|_fixEvent|_19f|_14c|_14a|_150|_141|declaredClass|_d4|_99|_Url|_83|scheme|_67|_3d|switch|getValue|_startRepeatCount|repeat|_startTime|_47e|cancel|tif|load|to|with|tf|getElementsByTagName|number|_34c|_342|extend|_1e3|_normalizeEventName|_14b|_14e|results|self|cbfn|_f9|_d8|_b2|src|_88|dav||baseUrl|fragment|_loadedModules|_44|_43|_loaders|mll|height||easing|play|setTimeout|Date|clearTimeout|hdr|content|code|errback|_464|addCallbacks|_450|fromJson|_413|_3fc|_3ee|max|_31e|cond|getAttribute|_3d4|obi|tagName|_360|_381|contains|firstChild|_368|_372|_320|place|_2fa|scrollTop|_299|scrollLeft|top|documentElement|_288|_287|_getBorderExtents|_23f|_23d|_239|_218|_216|_211|eles|target|keys|shiftKey|ctrlKey|event|192|iel|_1db|delete|_1cf||addEventListener|String|_1af|_157|array|_14d|continue|_14f|_137|_11f|_106|_findMethod|has|_delegate|_dc|_d3|loaded|_9a|_loadInit|_inFlightCount|getObject|tv|_4f|_postLoad|_2d|offsetWidth|properties|beforeBegin|animateProperty|_4ad|_4a6|isKhtml|_fade|100|headers|readyState|timeout|_469|_457|_44d|formToObject|_441|comment|_43d|_36f|_419|tp|_40a|_406|_407|_373|_403|_3e6|_31b|cbi|test|_3c7|nextSibling|last|_3a1|_38e|_365|_36b|ecn|_364|_363|_356|_35e|_35f|_34f|_34d|_349|trim|tci|_328|_32b|_31f|_31c|_anim|_300|_2ff|_2f5|_2e7|removeClass|addClass|func|_2c4|cls|_2a9|_2ae|_280|_27f|_getPadExtents|isMoz|none|_233|cssText|_214|_fixCallback|_synthesizeEvent|stopPropagation|preventDefault|_setKeyChar|_1e1|ieh|_1d7|_1be|colorFromArray|sanitize|bits|rgb|_156|_fire|_resback|_13d|partial|_13a|silentlyCancelled|_topics|_127|_f1|_f0|superclass|_ec|_e3|mct|setObject|_bf|_b3|object|require|_92|_khtmlTimer|location|XMLHTTP|locale|dua|_71|_modulePrefixes|_55|_loadModule|_51|_50|_4e|pop|_3f|_callLoaded|_unloaders|_loadNotifying|_loadedUrls|_27|_24|_1d|_5|_4b7|onAnimate|getStyle|offsetHeight|_defaultEasing|toCss|_properties|fadeOut|fadeIn|_49f|auto|zoom|_cycle|_endTime|valueOf|_494|duration|_492|DELETE|_ioAddQueryToUrl|putData|contentType|password|user|_isDocumentOk|application|||||_466||||||startTime|_xhrObj|_45f|handleAs|addBoth|error|text|objectToQuery|_44f|ActiveXObject|_443|_442|filtered|_43f|_43e|_437|file|tnl|_41c|_filterQueryResult|_zipIdx|_408|_402|evaluate|_3ed|_380|fHit|_361|_33b|_3da|_3ab|_3d6|RegExp|_327|_3cf|_3c9|child|innerHTML|first|tval|_391|class|pnc|_37e|_37c|_375|_366|_35c|_35a|_353|_33c|_336|_314|||_315|_oe|_307|_309|cloneNode|after|createElement||_2f8|_2ef|_2ee|unshift|coords|some|every||_2cb|script|_2c9|parent||a2p||_2c3|_2bd||abs|_getMarginBox|_2b3|_2a6|position|_2a7|_2ac|_2ab|_getIeDocumentElementOffset|getBoundingClientRect|ownerDocument|_2a3|clientWidth|_isBodyLtr|_fixIeBiDiScrollLeft|_bodyLtr|_29d|_getContentBox|_setBox|_getMarginExtents|_getPadBorderExtents|_usesBorderBox|boxModel|pcs|st|sl|_240|runtimeStyle|_dcm|BackCompat|compatMode|default|_21b|_d|html|_event_listener|handlers|PAGE_DOWN|PAGE_UP|RIGHT_ARROW|LEFT_ARROW|DOWN_ARROW|UP_ARROW|_preventDefault||_stopPropagation|returnValue||_trySetKeyCode|cancelBubble|currentTarget|106|_1ee|111||_1e8|_1e7|||se|srcElement|onkeydown||_1d0|_disconnect|lid|_1c0|_connect|_set|_195|_185|_183|_17d|_everyOrSome|_16b|_172|_15b|Function|_154|_escapeString|_140|chain|_check|canceller|_12d|_124|_11a|_10d|_107|inherited|_fa|_f2|_findMixin|_constructor|preamble|_de|clone|tmp|_c7|TMP|_be|_ba|_mixin|isBrowser|lang|firebug||param|modulePaths|_a7|_fireCallback|_a0|setContext||_9c|unloaded||||_96|_93|navigator|_90|_89||protocol|_84|_86|_XMLHTTP_PROGIDS|gears|google|setAttribute|_80|_77|cfg|_6f|_getModuleSymbols|_5a|_58|_53|_4d|_4c|_45|_40|_moduleHasPrefix|_loadUri|_28|_26|_21|_22|tests|doh|_20|_1f|_1c|version|_1b|_19|_getProp|_11|_4|_4a5|_4b3|_Animation|tempColor|blendColors|PI|sin|||||_49a|normal|onEnd||rate||curr|onStop|_497||_496|pct|onPause|onPlay|onBegin|delay||_491|_Line|_48b|wrapForm|PUT|_487|POST|GET|_476|_474|_472|_ioWatch|send|_471|setRequestHeader|open|callback|setInterval|_470|resHandle|_46f|ioCheck|_46e|validCheck|getTime|_ioCancelAll|addOnUnload|clearInterval|dojoType|now|canceled|_blockAsync|_45e|_45c|_459|_ioSetArgs|_contentHandlers|abort|_458|_456||||addErrback|_454|addCallback|_452|_44b|_44a|_449|preventCache|responseXML|Microsoft|JSON|usePlainJson|_431|toJson|_430|_42d|image|opt|ria|_421|_41b|_40b|_zip|_410|_40d|_357|sqf|_374|_3e5|_3df|_38f|clc|pred|parseInt|ntf|_3bf|_3bc|cnl|previousSibling|_3a9|_3a6|_39c|_399|_396|_392|__cachedIndex|__cachedLength|_376|iterateNext|_34a|_355|_354|_32c|_350|_34b|_33f|_33e|_33a|_338|_334|_332||_330|_32e||_322|_316|mousemove|mouseout|mouseover|_305|lastChild||_2f9||_2f2|_2f1|removeChild|_2ec|_2eb|_2ea|_2e6||_2e4|_2e2|_2d6|_2d5|_2d4|_2d3|_2d2|_2d1|_2cd|_2cc|scs|write|_2c8|hasClass|_2c0|_2bb|_2b5|_abs|_docScroll|offsetParent|offsetTop|offsetLeft|absolute|getBoxObjectFor|clientLeft|_setContentSize|_setMarginBox|_28d|_286|_285|_289|NaN|_281|border|_272|_26b|_260|_258|_253|_24c|_246|_23a|_getOpacity|_setOpacity|_238|td|tr|nodeName|FILTER|_22f|_22e|currentStyle|_22c|_22b|display|QuirksMode|unselectable|_217|isMozilla|getElementById|attributes|all|_ie_listener|_getIeDispatcher|_1fd|NUM_LOCK|SCROLL_LOCK|INSERT|END|HOME|PAUSE|F12|F11|F10|F9|F8|F7|F6|F5|F4|F3|F2|F1|63232|SHIFT_TAB|TAB|keyIdentifier|_1f3|stopEvent|_punctMap|222|219|186|onkeypress|_stealthKeyDown|_fixKeys|relatedTarget|_1e0|_1df|_stealthKeydown|_1d6|_1d5|_1d1|_1ca|_1c9|_1cb|_1c2|_1c1|_1c3|_1c4|_1bc|_1b3|_1b2|colorFromHex|colorFromRgb|named|colorFromString|mask|rgba|_19c|_197|_192|setColor|_180|_178|_177|_175|_174|_16d|_166|_164|_163|_162|_15c|_15d|_15e|index|__json__|toJsonIndentStr|_nextId|_12f|_12b|publish|_128|_126|_125|_122|_121|_123|_11c|_11b|_10c|_10b|_108|getDispatcher|argument|nom|_construct|_core|_makeCtor|_df|_db|deprecated|isObject|_cc||scope||_hitchArgs|_c2||pre|_c1|native|isDebug||registerModulePath|_a8||finally|||_a6|_a5|_a4|_a3|_a2|_a1|_9f|_9e|_9d|_9b|_98|_97|onbeforeunload|ipt|scr|complete|_95|userAgent|_modulesLoaded|initialized|_initFired|_8c|_8a|_getText|_87|ieForceActiveXXhr|Msxml2|isGears|_81|_gearsObject|googlegears|GearsFactory|isFF|_7d|Safari|_72|_name|_6c|ire|ore|_68|i18n|_5b|requireIf|_56|_52|loading|_4a|_loadPath|_47|_48|_global_omit_module_check|_getModulePrefix|_3c|_3a|_37|_30|Boolean|_loadUriAndCheck|_2e||cacheBust|_1e|_1a|_17|_16|_15|_14|_f|_10|_e|_9|_8|revision|flag|patch|minor|major|_6|color|units|needs|stopped|playing|stop|gotoPercent|pause|1000|implemented|yet|_48a|xhrDelete|rawXhrPut|xhrPut|postData|rawXhrPost|xhrPost|xhrGet|Type|Content|sync|response|http|bad|urlencoded|www|_watchInFlightError||exceeded|handle|action|getAttributeNode|loadXML|async|XMLDOM|prefixes|MSXML3|MSXML|MSXML2||xml|javascript|wasn|your|optional|message|off|turn|use|endpoints|issues|security|potential|avoid|mimetype|using|consider|please|decodeURIComponent|queryToObject|formToJson|formToQuery|encodeURIComponent|selected|option|multiple|checked|checkbox|radio|disabled|textarea|select|button|reset|submit|input|_3fb|hasAttribute|0n|even|odd|nth|_3b5|empty|_3b1|_3ad|htmlFor|_38a|under||exprssion|failure|ANY_TYPE|XPathResult|starts|keyup|keydown|mouseup|mousedown|blur|click|combine|span|addContent||adopt|orphan|_2de|_2dd|styles|_2da|_2d9|_2cf|_2ce|show|createPopup|toggleClass|scrollWidth|clientTop|ltr|direction|pageXOffset|pageYOffset|fixed|contentBox|marginBox|BUTTON|TABLE|_getBorderBox|clientHeight|visible|overflow|marginBottom|marginRight|marginTop|marginLeft|borderBottomWidth|borderBottomStyle|borderRightWidth|borderRightStyle|borderTopWidth|borderTopStyle|borderLeftWidth|borderLeftStyle|paddingBottom|paddingRight|paddingTop|paddingLeft|offset||min|padding||margin|Opacity|Alpha|alpha|filters|pixelLeft|medium|_22a|defaultView|before||insertBefore|KhtmlUserSelect|MozUserSelect|setSelectable|isDescendant|div|_destroyElement|BackgroundImageCache|execCommand|PageDown|PageUp|Right|Left|Down|Up|63289|63249|63248|PRINT_SCREEN|63302|63277|63276|63275|63273|63272|63250|63247|63246|63245|63244|63243|63242|63241|63240|63239|63238|63237|63236|63235|63234|63233|Enter|_1f9|which|_1f6|bubbledKeyCode|221|220||||191|190|189|188|187|toElement|fromElement|clientY|pageY||clientX|pageX|offsetY|||layerY|offsetX|layerX|parentWindow|_nop|_allow_leaks|145|144|126|F15|125|F14|124|F13|123|122|121|120|119|118|117|116|115|114|113|112|NUMPAD_DIVIDE|110|NUMPAD_PERIOD|109|NUMPAD_MINUS|108|NUMPAD_ENTER|107|NUMPAD_PLUS|NUMPAD_MULTIPLY|105|NUMPAD_9|104|NUMPAD_8|103|NUMPAD_7|102|NUMPAD_6|101|NUMPAD_5|NUMPAD_4||NUMPAD_3|NUMPAD_2|NUMPAD_1|NUMPAD_0||SELECT|RIGHT_WINDOW||LEFT_WINDOW||HELP|SPACE|ESCAPE|CAPS_LOCK|ALT|CTRL|SHIFT|ENTER|CLEAR|BACKSPACE|attachEvent|fixEvent|fromCharCode|keyChar|_1b9|removeEventListener|0x|round|toHex|toRgba|toRgb|aqua|teal|blue|navy|yellow|olive|lime|green|fuchsia|purple|red|maroon|white|gray|silver|black|boolean|called|already|Cancelled|connectPublisher|unsubscribe|subscribe|disconnect|_113|_112||_111|_110|||found|was||must|_|module|||required|likely|It|declaration|Mixin|separate|instead|property|initializer||pass|_c9|_bb|_b7|nfunction|isAlien|isFinite|isArrayLike|_firebug|withDoc|withGlobal|_writeIncludes|VML|behavior|addRule|createStyleSheet|vml|com|microsoft|schemas|urn|namespaces|onunload|onreadystatechange|defer|khtml|WebKit|DOMContentLoaded|enableMozDomContentLoaded|domcontentloaded|Unable|base|chrome|1223|304|300|200|available|XMLHttpRequest|_println|language|userLanguage|isQuirks|factory|mimeTypes|Factory|Gears|_7f|MSIE||Firefox|Gecko|Konqueror||Opera|appVersion|xd|browser|moduleUrl|port|host|hostenv|_requireLocalization|_5f|_5e|_5d|_5c|requireLocalization|requireAfterIf|_57|common|platformRequire|defined|symbol|_isXDomain|tried|Could|__package__|packageFileName|_42|useXDomain|flight|still|files|addOnLoad|failed|sourceURL|util|notice|without|change|subject|APIs|EXPERIMENTAL|experimental|removed|will|DEPRECATED|exists|10315|Rev|Mobile|Spidermonkey|Rhino||Browser|delayMozLoadingFix|preventBackButtonFix|libraryScriptUri|baseRelativePath|baseScriptUri|allowQueryConfig|warn|trace|timeEnd||time|profileEnd|profile|log|info|groupEnd|group|dirxml|dir|count|assert'.split('|'),0,{}); + + +/* + +Prototype 1.5 rc0 + - Adapted from Ruby on Rails - http://dev.rubyonrails.org/browser/spinoffs/prototype/src + - By Lunarmedia, 06 August, 2006 + - Available at (and packed with) JavascriptCompressor.com + +Please note this version is missing the selector.js component of the full Prototype library. +You can get the compressed version of selector at JavascriptCompressor.com + +*/ + +var decompressedPrototype = function(p,a,c,k,e,d){e=function(c){return(c<a?"":e(parseInt(c/a)))+((c=c%a)>35?String.fromCharCode(c+29):c.toString(36))};if(!''.replace(/^/,String)){while(c--){d[e(c)]=k[c]||e(c)}k=[(function(e){return d[e]})];e=(function(){return'\\w+'});c=1};while(c--){if(k[c]){p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c])}}return p}('d T={4l:\'1.5.8P\',3E:\'(?:<3G.*?>)((\\n|\\r|.)*?)(?:<\\/3G>)\',2v:7(){},K:7(x){c x}};d 1b={17:7(){c 7(){6.1I.2n(6,N)}}};d 1e=z q();q.u=7(5d,O){G(d 1G 2M O){5d[1G]=O[1G]}c 5d};q.1U=7(U){1j{f(U==1v)c\'1v\';f(U==1L)c\'1L\';c U.1U?U.1U():U.2C()}1s(e){f(e 8R 9l)c\'...\';25 e}};7j.v.1d=7(){d 43=6,23=$A(N),U=23.8S();c 7(){c 43.2n(U,23.3s($A(N)))}};7j.v.8U=7(U){d 43=6;c 7(C){c 43.8V(U,C||1W.C)}};q.u(8Q.v,{8W:7(){d 4Z=6.2C(16);f(6<16)c\'0\'+4Z;c 4Z},5j:7(){c 6+1},8Y:7(o){$R(0,6,11).V(o);c 6}});d 6s={6j:7(){d 48;G(d i=0;i<N.t;i++){d 6L=N[i];1j{48=6L();1y}1s(e){}}c 48}};d 6Q=1b.17();6Q.v={1I:7(1a,1J){6.1a=1a;6.1J=1J;6.41=Y;6.2A()},2A:7(){5Z(6.2D.1d(6),6.1J*4z)},2D:7(){f(!6.41){1j{6.41=11;6.1a()}8Z{6.41=Y}}}};q.u(4b.v,{2T:7(1A,1z){d L=\'\',O=6,I;1z=N.90.52(1z);1H(O.t>0){f(I=O.I(1A)){L+=O.47(0,I.w);L+=(1z(I)||\'\').2C();O=O.47(I.w+I[0].t)}1D{L+=O,O=\'\'}}c L},92:7(1A,1z,3i){1z=6.2T.52(1z);3i=3i===1v?1:3i;c 6.2T(1A,7(I){f(--3i<0)c I[0];c 1z(I)})},93:7(1A,o){6.2T(1A,o);c 6},94:7(t,2S){t=t||30;2S=2S===1v?\'...\':2S;c 6.t>t?6.47(0,t-2S.t)+2S:6},9F:7(){c 6.2y(/^\\s+/,\'\').2y(/\\s+$/,\'\')},71:7(){c 6.2y(/<\\/?[^>]+>/7Y,\'\')},2Q:7(){c 6.2y(z 3O(T.3E,\'5P\'),\'\')},70:7(){d 6Y=z 3O(T.3E,\'5P\');d 5p=z 3O(T.3E,\'98\');c(6.I(6Y)||[]).1C(7(5o){c(5o.I(5p)||[\'\',\'\'])[1]})},3q:7(){c 6.70().1C(7(3G){c 4q(3G)})},9E:7(){d 1q=J.4Y(\'1q\');d 1Y=J.9D(6);1q.75(1Y);c 1q.3h},9c:7(){d 1q=J.4Y(\'1q\');1q.3h=6.71();c 1q.2z[0]?1q.2z[0].6q:\'\'},78:7(){d 7i=6.I(/^\\??(.*)$/)[1].3j(\'&\');c 7i.36({},7(5b,72){d 1i=72.3j(\'=\');5b[1i[0]]=1i[1];c 5b})},1Z:7(){c 6.3j(\'\')},3P:7(){d 2l=6.3j(\'-\');f(2l.t==1)c 2l[0];d 54=6.5g(\'-\')==0?2l[0].7e(0).3Y()+2l[0].7g(1):2l[0];G(d i=1,73=2l.t;i<73;i++){d s=2l[i];54+=s.7e(0).3Y()+s.7g(1)}c 54},1U:7(){c"\'"+6.2y(/\\\\/g,\'\\\\\\\\\').2y(/\'/g,\'\\\\\\\'\')+"\'"}});4b.v.2T.52=7(1z){f(2i 1z==\'7\')c 1z;d 2U=z 3n(1z);c 7(I){c 2U.7a(I)}};4b.v.9h=4b.v.78;d 3n=1b.17();3n.79=/(^|.|\\r|\\n)(#\\{(.*?)\\})/;3n.v={1I:7(2U,1A){6.2U=2U.2C();6.1A=1A||3n.79},7a:7(U){c 6.2U.2T(6.1A,7(I){d 53=I[1];f(53==\'\\\\\')c I[2];c 53+(U[I[3]]||\'\').2C()})}};d $1y=z q();d $49=z q();d 1p={V:7(o){d w=0;1j{6.2m(7(h){1j{o(h,w++)}1s(e){f(e!=$49)25 e}})}1s(e){f(e!=$1y)25 e}},9n:7(o){d L=11;6.V(7(h,w){L=L&&!!(o||T.K)(h,w);f(!L)25 $1y});c L},9o:7(o){d L=11;6.V(7(h,w){f(L=!!(o||T.K)(h,w))25 $1y});c L},3e:7(o){d P=[];6.V(7(h,w){P.W(o(h,w))});c P},7n:7(o){d L;6.V(7(h,w){f(o(h,w)){L=h;25 $1y}});c L},7o:7(o){d P=[];6.V(7(h,w){f(o(h,w))P.W(h)});c P},9p:7(1A,o){d P=[];6.V(7(h,w){d 7c=h.2C();f(7c.I(1A))P.W((o||T.K)(h,w))});c P},1M:7(U){d 51=Y;6.V(7(h){f(h==U){51=11;25 $1y}});c 51},36:7(45,o){6.V(7(h,w){45=o(45,h,w)});c 45},9q:7(1F){d 23=$A(N).47(1);c 6.3e(7(h){c h[1F].2n(h,23)})},9s:7(o){d L;6.V(7(h,w){h=(o||T.K)(h,w);f(L==1v||h>=L)L=h});c L},9u:7(o){d L;6.V(7(h,w){h=(o||T.K)(h,w);f(L==1v||h<L)L=h});c L},9v:7(o){d 50=[],58=[];6.V(7(h,w){((o||T.K)(h,w)?50:58).W(h)});c[50,58]},3r:7(1G){d P=[];6.V(7(h,w){P.W(h[1G])});c P},9x:7(o){d P=[];6.V(7(h,w){f(!o(h,w))P.W(h)});c P},9y:7(o){c 6.3e(7(h,w){c{h:h,59:o(h,w)}}).9z(7(18,3U){d a=18.59,b=3U.59;c a<b?-1:a>b?1:0}).3r(\'h\')},1Z:7(){c 6.3e(T.K)},9B:7(){d o=T.K,23=$A(N);f(2i 23.5e()==\'7\')o=23.9C();d 7l=[6].3s(23).1C($A);c 6.1C(7(h,w){c o(7l.3r(w))})},1U:7(){c\'#<1p:\'+6.1Z().1U()+\'>\'}};q.u(1p,{1C:1p.3e,5v:1p.7n,1k:1p.7o,8M:1p.1M,7p:1p.1Z});d $A=1E.7q=7(2R){f(!2R)c[];f(2R.1Z){c 2R.1Z()}1D{d P=[];G(d i=0;i<2R.t;i++)P.W(2R[i]);c P}};q.u(1E.v,1p);f(!1E.v.4d)1E.v.4d=1E.v.4m;q.u(1E.v,{2m:7(o){G(d i=0;i<6.t;i++)o(6[i])},5i:7(){6.t=0;c 6},7r:7(){c 6[0]},5e:7(){c 6[6.t-1]},7s:7(){c 6.1k(7(h){c h!=1v||h!=1L})},6J:7(){c 6.36([],7(6H,h){c 6H.3s(h&&h.5D==1E?h.6J():[h])})},5s:7(){d 4N=$A(N);c 6.1k(7(h){c!4N.1M(h)})},5g:7(U){G(d i=0;i<6.t;i++)f(6[i]==U)c i;c-1},4m:7(5h){c(5h!==Y?6:6.1Z()).4d()},1U:7(){c\'[\'+6.1C(q.1U).1N(\', \')+\']\'}});d 4h={2m:7(o){G(d 1O 2M 6){d h=6[1O];f(2i h==\'7\')49;d 1i=[1O,h];1i.1O=1O;1i.h=h;o(1i)}},7t:7(){c 6.3r(\'1O\')},4N:7(){c 6.3r(\'h\')},7u:7(2N){c $H(2N).36($H(6),7(4Q,1i){4Q[1i.1O]=1i.h;c 4Q})},7w:7(){c 6.1C(7(1i){c 1i.1C(4n).1N(\'=\')}).1N(\'&\')},1U:7(){c\'#<4h:{\'+6.1C(7(1i){c 1i.1C(q.1U).1N(\': \')}).1N(\', \')+\'}>\'}};7 $H(U){d 2N=q.u({},U||{});q.u(2N,1p);q.u(2N,4h);c 2N};3L=1b.17();q.u(3L.v,1p);q.u(3L.v,{1I:7(22,2x,2H){6.22=22;6.2x=2x;6.2H=2H},2m:7(o){d h=6.22;2q{o(h);h=h.5j()}1H(6.1M(h))},1M:7(h){f(h<6.22)c Y;f(6.2H)c h<6.2x;c h<=6.2x}});d $R=7(22,2x,2H){c z 3L(22,2x,2H)};d M={4w:7(){c 6s.6j(7(){c z 5C()},7(){c z 5n(\'7y.6d\')},7(){c z 5n(\'7z.6d\')})||Y},4s:0};M.2W={3b:[],2m:7(o){6.3b.2m(o)},69:7(4F){f(!6.1M(4F))6.3b.W(4F)},7A:7(5t){6.3b=6.3b.5s(5t)},3y:7(1a,26,E,2Z){6.V(7(3o){f(3o[1a]&&2i 3o[1a]==\'7\'){1j{3o[1a].2n(3o,[26,E,2Z])}1s(e){}}})}};q.u(M.2W,1p);M.2W.69({5G:7(){M.4s++},1B:7(){M.4s--}});M.44=7(){};M.44.v={4a:7(m){6.m={1F:\'4j\',4p:11,5H:\'5E/x-86-Q-7C\',28:\'\'};q.u(6.m,m||{})},3l:7(){c 6.E.32==1v||6.E.32==0||(6.E.32>=84&&6.E.32<7E)},7G:7(){c!6.3l()}};M.3t=1b.17();M.3t.5L=[\'7H\',\'80\',\'7I\',\'7J\',\'4t\'];M.3t.v=q.u(z M.44(),{1I:7(1l,m){6.E=M.4w();6.4a(m);6.26(1l)},26:7(1l){d 28=6.m.28||\'\';f(28.t>0)28+=\'&7K=\';1j{6.1l=1l;f(6.m.1F==\'7L\'&&28.t>0)6.1l+=(6.1l.I(/\\?/)?\'&\':\'?\')+28;M.2W.3y(\'5G\',6,6.E);6.E.7N(6.m.1F,6.1l,6.m.4p);f(6.m.4p){6.E.5T=6.5J.1d(6);2Y((7(){6.4r(1)}).1d(6),10)}6.5A();d 1c=6.m.5V?6.m.5V:28;6.E.7O(6.m.1F==\'4j\'?1c:1L)}1s(e){6.3p(e)}},5A:7(){d 1P=[\'X-7P-7Q\',\'5C\',\'X-T-4l\',T.4l,\'7R\',\'1Y/7m, 1Y/2e, 5E/5F, 1Y/5F, */*\'];f(6.m.1F==\'4j\'){1P.W(\'5Q-2g\',6.m.5H);f(6.E.7S)1P.W(\'7T\',\'7U\')}f(6.m.1P)1P.W.2n(1P,6.m.1P);G(d i=0;i<1P.t;i+=2)6.E.7V(1P[i],1P[i+1])},5J:7(){d 2F=6.E.2F;f(2F!=1)6.4r(6.E.2F)},4A:7(B){1j{c 6.E.7W(B)}1s(e){}},5M:7(){1j{c 4q(\'(\'+6.4A(\'X-7X\')+\')\')}1s(e){}},5R:7(){1j{c 4q(6.E.3F)}1s(e){6.3p(e)}},4r:7(2F){d C=M.3t.5L[2F];d E=6.E,2Z=6.5M();f(C==\'4t\'){1j{(6.m[\'2I\'+6.E.32]||6.m[\'2I\'+(6.3l()?\'81\':\'82\')]||T.2v)(E,2Z)}1s(e){6.3p(e)}f((6.4A(\'5Q-2g\')||\'\').I(/^1Y\\/7m/i))6.5R()}1j{(6.m[\'2I\'+C]||T.2v)(E,2Z);M.2W.3y(\'2I\'+C,6,E,2Z)}1s(e){6.3p(e)}f(C==\'4t\')6.E.5T=T.2v},3p:7(57){(6.m.5W||T.2v)(6,57);M.2W.3y(\'5W\',6,57)}});M.4C=1b.17();q.u(q.u(M.4C.v,M.3t.v),{1I:7(1w,1l,m){6.4x={3m:1w.3m?$(1w.3m):$(1w),3z:1w.3z?$(1w.3z):(1w.3m?1L:$(1w))};6.E=M.4w();6.4a(m);d 1B=6.m.1B||T.2v;6.m.1B=(7(E,U){6.5Y();1B(E,U)}).1d(6);6.26(1l)},5Y:7(){d 3A=6.3l()?6.4x.3m:6.4x.3z;d 3k=6.E.3F;f(!6.m.3q)3k=3k.2Q();f(3A){f(6.m.60){z 6.m.60(3A,3k)}1D{k.6h(3A,3k)}}f(6.3l()){f(6.1B)2Y(6.1B.1d(6),10)}}});M.61=1b.17();M.61.v=q.u(z M.44(),{1I:7(1w,1l,m){6.4a(m);6.1B=6.m.1B;6.1J=(6.m.1J||2);6.2s=(6.m.2s||1);6.4B={};6.1w=1w;6.1l=1l;6.22()},22:7(){6.m.1B=6.63.1d(6);6.2D()},7b:7(){6.4B.1B=1v;89(6.65);(6.1B||T.2v).2n(6,N)},63:7(26){f(6.m.2s){6.2s=(26.3F==6.64?6.2s*6.m.2s:1);6.64=26.3F}6.65=2Y(6.2D.1d(6),6.2s*6.1J*4z)},2D:7(){6.4B=z M.4C(6.1w,6.1l,6.m)}});7 $(){d P=[],4;G(d i=0;i<N.t;i++){4=N[i];f(2i 4==\'8c\')4=J.8d(4);P.W(k.u(4))}c P.t<2?P[0]:P};J.8f=7(1f,6a){d 6b=($(6a)||J.1c).4D(\'*\');c $A(6b).36([],7(12,4E){f(4E.1f.I(z 3O("(^|\\\\s)"+1f+"(\\\\s|$)")))12.W(k.u(4E));c 12})};f(!1W.k)d k=z q();k.u=7(4){f(!4)c;f(4X)c 4;f(!4.6e&&4.1h&&4!=1W){d 2a=k.3d,2r=k.u.2r;G(d 1G 2M 2a){d h=2a[1G];f(2i h==\'7\')4[1G]=2r.4W(h)}}4.6e=11;c 4};k.u.2r={4W:7(h){c 6[h]=6[h]||7(){c h.2n(1L,[6].3s($A(N)))}}};k.3d={4U:7(4){c $(4).l.2B!=\'3Q\'},6N:7(){G(d i=0;i<N.t;i++){d 4=$(N[i]);k[k.4U(4)?\'6f\':\'6w\'](4)}},6f:7(){G(d i=0;i<N.t;i++){d 4=$(N[i]);4.l.2B=\'3Q\'}},6w:7(){G(d i=0;i<N.t;i++){d 4=$(N[i]);4.l.2B=\'\'}},42:7(4){4=$(4);4.1X.8h(4)},6h:7(4,2e){$(4).3h=2e.2Q();2Y(7(){2e.3q()},10)},2y:7(4,2e){4=$(4);f(4.6k){4.6k=2e.2Q()}1D{d 1K=4.6R.6S();1K.56(4);4.1X.8i(1K.6T(2e.2Q()),4)}2Y(7(){2e.3q()},10)},8k:7(4){4=$(4);c 4.2k},3K:7(4){c z k.3S(4)},8l:7(4,1f){f(!(4=$(4)))c;c k.3K(4).1M(1f)},8m:7(4,1f){f(!(4=$(4)))c;c k.3K(4).7k(1f)},8n:7(4,1f){f(!(4=$(4)))c;c k.3K(4).42(1f)},8p:7(4){4=$(4);G(d i=0;i<4.2z.t;i++){d 3M=4.2z[i];f(3M.8q==3&&!/\\S/.4v(3M.6q))k.42(3M)}},8r:7(4){c $(4).3h.I(/^\\s*$/)},8s:7(4,3I){4=$(4),3I=$(3I);1H(4=4.1X)f(4==3I)c 11;c Y},6t:7(4){4=$(4);d x=4.x?4.x:4.2f,y=4.y?4.y:4.29;1W.6t(x,y)},1R:7(4,l){4=$(4);d h=4.l[l.3P()];f(!h){f(J.4J&&J.4J.6v){d 4L=J.4J.6v(4,1L);h=4L?4L.8v(l):1L}1D f(4.6x){h=4.6x[l.3P()]}}f(1W.6E&&[\'18\',\'1n\',\'3U\',\'6G\'].1M(l))f(k.1R(4,\'14\')==\'4G\')h=\'6y\';c h==\'6y\'?1L:h},8x:7(4,l){4=$(4);G(d B 2M l)4.l[B.3P()]=l[B]},8y:7(4){4=$(4);f(k.1R(4,\'2B\')!=\'3Q\')c{21:4.2p,24:4.2k};d 20=4.l;d 6B=20.4O;d 6A=20.14;20.4O=\'31\';20.14=\'2o\';20.2B=\'\';d 6C=4.6m;d 6D=4.6p;20.2B=\'3Q\';20.14=6A;20.4O=6B;c{21:6C,24:6D}},8z:7(4){4=$(4);d 4R=k.1R(4,\'14\');f(4R==\'4G\'||!4R){4.4T=11;4.l.14=\'3T\';f(1W.6E){4.l.1n=0;4.l.18=0}}},8A:7(4){4=$(4);f(4.4T){4.4T=1v;4.l.14=4.l.1n=4.l.18=4.l.6G=4.l.3U=\'\'}},8B:7(4){4=$(4);f(4.3c)c;4.3c=4.l.3V;f((k.1R(4,\'3V\')||\'4U\')!=\'31\')4.l.3V=\'31\'},8D:7(4){4=$(4);f(4.3c)c;4.l.3V=4.3c;4.3c=1v}};q.u(k,k.3d);d 4X=Y;f(!3W&&/3x|3w|3u/.4v(33.62)){d 3W={}};k.6K=7(2a){q.u(k.3d,2a||{});f(2i 3W!=\'1v\'){d 2a=k.3d,2r=k.u.2r;G(d 1G 2M 2a){d h=2a[1G];f(2i h==\'7\')3W.v[1G]=2r.4W(h)}4X=11}};k.6K();d 6M=z q();6M.2B=k.6N;1e.1g=7(3f){6.3f=3f};1e.1g.v={1I:7(4,2t){6.4=$(4);6.2t=2t.2Q();f(6.3f&&6.4.6O){1j{6.4.6O(6.3f,6.2t)}1s(e){d 1h=6.4.1h.2w();f(1h==\'4V\'||1h==\'8N\'){6.2X(6.6U())}1D{25 e}}}1D{6.1K=6.4.6R.6S();f(6.2V)6.2V();6.2X([6.1K.6T(6.2t)])}2Y(7(){2t.3q()},10)},6U:7(){d 1q=J.4Y(\'1q\');1q.3h=\'<6V><4V>\'+6.2t+\'</4V></6V>\';c $A(1q.2z[0].2z[0].2z)}};d 1g=z q();1g.6W=1b.17();1g.6W.v=q.u(z 1e.1g(\'96\'),{2V:7(){6.1K.97(6.4)},2X:7(2h){2h.V((7(2j){6.4.1X.55(2j,6.4)}).1d(6))}});1g.5m=1b.17();1g.5m.v=q.u(z 1e.1g(\'99\'),{2V:7(){6.1K.56(6.4);6.1K.74(11)},2X:7(2h){2h.4m(Y).V((7(2j){6.4.55(2j,6.4.9a)}).1d(6))}});1g.7h=1b.17();1g.7h.v=q.u(z 1e.1g(\'9d\'),{2V:7(){6.1K.56(6.4);6.1K.74(6.4)},2X:7(2h){2h.V((7(2j){6.4.75(2j)}).1d(6))}});1g.76=1b.17();1g.76.v=q.u(z 1e.1g(\'9i\'),{2V:7(){6.1K.9m(6.4)},2X:7(2h){2h.V((7(2j){6.4.1X.55(2j,6.4.9t)}).1d(6))}});k.3S=1b.17();k.3S.v={1I:7(4){6.4=$(4)},2m:7(o){6.4.1f.3j(/\\s+/).1k(7(B){c B.t>0}).2m(o)},5c:7(1f){6.4.1f=1f},7k:7(5a){f(6.1M(5a))c;6.5c(6.1Z().3s(5a).1N(\' \'))},42:7(4c){f(!6.1M(4c))c;6.5c(6.1k(7(1f){c 1f!=4c}).1N(\' \'))},2C:7(){c 6.1Z().1N(\' \')}};q.u(k.3S.v,1p);d 5I={5i:7(){G(d i=0;i<N.t;i++)$(N[i]).h=\'\'},4f:7(4){$(4).4f()},7v:7(){G(d i=0;i<N.t;i++)f($(N[i]).h==\'\')c Y;c 11},1k:7(4){$(4).1k()},5y:7(4){4=$(4);4.4f();f(4.1k)4.1k()}};d D={3a:7(Q){d 12=D.2L($(Q));d 4I=z 1E();G(d i=0;i<12.t;i++){d 4g=D.k.3a(12[i]);f(4g)4I.W(4g)}c 4I.1N(\'&\')},2L:7(Q){Q=$(Q);d 12=z 1E();G(d 1h 2M D.k.2E){d 4H=Q.4D(1h);G(d j=0;j<4H.t;j++)12.W(4H[j])}c 12},7x:7(Q,3N,B){Q=$(Q);d 3H=Q.4D(\'2u\');f(!3N&&!B)c 3H;d 4y=z 1E();G(d i=0;i<3H.t;i++){d 2u=3H[i];f((3N&&2u.2g!=3N)||(B&&2u.B!=B))49;4y.W(2u)}c 4y},7B:7(Q){d 12=D.2L(Q);G(d i=0;i<12.t;i++){d 4=12[i];4.7D();4.4o=\'11\'}},7F:7(Q){d 12=D.2L(Q);G(d i=0;i<12.t;i++){d 4=12[i];4.4o=\'\'}},5z:7(Q){c D.2L(Q).5v(7(4){c 4.2g!=\'31\'&&!4.4o&&[\'2u\',\'1k\',\'3J\'].1M(4.1h.2w())})},7M:7(Q){5I.5y(D.5z(Q))},5w:7(Q){$(Q).5w()}};D.k={3a:7(4){4=$(4);d 1F=4.1h.2w();d 1S=D.k.2E[1F](4);f(1S){d 1O=4n(1S[0]);f(1O.t==0)c;f(1S[1].5D!=1E)1S[1]=[1S[1]];c 1S[1].1C(7(h){c 1O+\'=\'+4n(h)}).1N(\'&\')}},1x:7(4){4=$(4);d 1F=4.1h.2w();d 1S=D.k.2E[1F](4);f(1S)c 1S[1]}};D.k.2E={2u:7(4){6c(4.2g.2w()){1r\'7Z\':1r\'31\':1r\'6l\':1r\'1Y\':c D.k.2E.3J(4);1r\'6g\':1r\'6i\':c D.k.2E.5O(4)}c Y},5O:7(4){f(4.83)c[4.B,4.h]},3J:7(4){c[4.B,4.h]},1k:7(4){c D.k.2E[4.2g==\'1k-6n\'?\'5S\':\'5X\'](4)},5S:7(4){d h=\'\',2b,w=4.85;f(w>=0){2b=4.m[w];h=2b.h||2b.1Y}c[4.B,h]},5X:7(4){d h=[];G(d i=0;i<4.t;i++){d 2b=4.m[i];f(2b.87)h.W(2b.h||2b.1Y)}c[4.B,h]}};d $F=D.k.1x;1e.3D=7(){};1e.3D.v={1I:7(4,1J,1a){6.1J=1J;6.4=$(4);6.1a=1a;6.2K=6.1x();6.2A()},2A:7(){5Z(6.2D.1d(6),6.1J*4z)},2D:7(){d h=6.1x();f(6.2K!=h){6.1a(6.4,h);6.2K=h}}};D.k.3C=1b.17();D.k.3C.v=q.u(z 1e.3D(),{1x:7(){c D.k.1x(6.4)}});D.3C=1b.17();D.3C.v=q.u(z 1e.3D(),{1x:7(){c D.3a(6.4)}});1e.2c=7(){};1e.2c.v={1I:7(4,1a){6.4=$(4);6.1a=1a;6.2K=6.1x();f(6.4.1h.2w()==\'Q\')6.67();1D 6.2A(6.4)},4K:7(){d h=6.1x();f(6.2K!=h){6.1a(6.4,h);6.2K=h}},67:7(){d 12=D.2L(6.4);G(d i=0;i<12.t;i++)6.2A(12[i])},2A:7(4){f(4.2g){6c(4.2g.2w()){1r\'6g\':1r\'6i\':1o.3B(4,\'8j\',6.4K.1d(6));1y;1r\'6l\':1r\'1Y\':1r\'3J\':1r\'1k-6n\':1r\'1k-8t\':1o.3B(4,\'8u\',6.4K.1d(6));1y}}}};D.k.2c=1b.17();D.k.2c.v=q.u(z 1e.2c(),{1x:7(){c D.k.1x(6.4)}});D.2c=1b.17();D.2c.v=q.u(z 1e.2c(),{1x:7(){c D.3a(6.4)}});f(!1W.1o){d 1o=z q()}q.u(1o,{8C:8,8F:9,8H:13,8I:27,8J:37,8L:38,8O:39,8T:40,8X:46,4:7(C){c C.Z||C.91},95:7(C){c(((C.6X)&&(C.6X==1))||((C.6Z)&&(C.6Z==1)))},9b:7(C){c C.9e||(C.9f+(J.3R.2G||J.1c.2G))},9g:7(C){c C.9j||(C.9k+(J.3R.2O||J.1c.2O))},7b:7(C){f(C.7d){C.7d();C.9r()}1D{C.48=Y;C.9w=11}},9A:7(C,1h){d 4=1o.4(C);1H(4.1X&&(!4.1h||(4.1h.3Y()!=1h.3Y())))4=4.1X;c 4},1T:Y,5u:7(4,B,1V,1u){f(!6.1T)6.1T=[];f(4.5f){6.1T.W([4,B,1V,1u]);4.5f(B,1V,1u)}1D f(4.4i){6.1T.W([4,B,1V,1u]);4.4i(\'2I\'+B,1V)}},66:7(){f(!1o.1T)c;G(d i=0;i<1o.1T.t;i++){1o.5N.2n(6,1o.1T[i]);1o.1T[i][0]=1L}1o.1T=Y},3B:7(4,B,1V,1u){d 4=$(4);1u=1u||Y;f(B==\'5U\'&&(33.4u.I(/3x|3w|3u/)||4.4i))B=\'5K\';6.5u(4,B,1V,1u)},5N:7(4,B,1V,1u){d 4=$(4);1u=1u||Y;f(B==\'5U\'&&(33.4u.I(/3x|3w|3u/)||4.4k))B=\'5K\';f(4.5x){4.5x(B,1V,1u)}1D f(4.4k){1j{4.4k(\'2I\'+B,1V)}1s(e){}}}});f(33.4u.I(/\\88\\b/))1o.3B(1W,\'8a\',1o.66,Y);d 2d={6o:Y,4P:7(){6.6z=1W.8e||J.3R.2G||J.1c.2G||0;6.6F=1W.8g||J.3R.2O||J.1c.2O||0},6u:7(4){d 19=0,15=0;2q{19+=4.2O||0;15+=4.2G||0;4=4.1X}1H(4);c[15,19]},35:7(4){d 19=0,15=0;2q{19+=4.29||0;15+=4.2f||0;4=4.1Q}1H(4);c[15,19]},68:7(4){d 19=0,15=0;2q{19+=4.29||0;15+=4.2f||0;4=4.1Q;f(4){p=k.1R(4,\'14\');f(p==\'3T\'||p==\'2o\')1y}}1H(4);c[15,19]},1Q:7(4){f(4.1Q)c 4.1Q;f(4==J.1c)c 4;1H((4=4.1X)&&4!=J.1c)f(k.1R(4,\'14\')!=\'4G\')c 4;c J.1c},8o:7(4,x,y){f(6.6o)c 6.6r(4,x,y);6.3g=x;6.34=y;6.1t=6.35(4);c(y>=6.1t[1]&&y<6.1t[1]+4.2k&&x>=6.1t[0]&&x<6.1t[0]+4.2p)},6r:7(4,x,y){d 4S=6.6u(4);6.3g=x+4S[0]-6.6z;6.34=y+4S[1]-6.6F;6.1t=6.35(4);c(6.34>=6.1t[1]&&6.34<6.1t[1]+4.2k&&6.3g>=6.1t[0]&&6.3g<6.1t[0]+4.2p)},8E:7(3Z,4){f(!3Z)c 0;f(3Z==\'8G\')c((6.1t[1]+4.2k)-6.34)/4.2k;f(3Z==\'8K\')c((6.1t[0]+4.2p)-6.3g)/4.2p},77:7(O,Z){O=$(O);Z=$(Z);Z.l.14=\'2o\';d 2P=6.35(O);Z.l.1n=2P[1]+\'1m\';Z.l.18=2P[0]+\'1m\';Z.l.21=O.2p+\'1m\';Z.l.24=O.2k+\'1m\'},4e:7(4M){d 19=0,15=0;d 4=4M;2q{19+=4.29||0;15+=4.2f||0;f(4.1Q==J.1c)f(k.1R(4,\'14\')==\'2o\')1y}1H(4=4.1Q);4=4M;2q{19-=4.2O||0;15-=4.2G||0}1H(4=4.1X);c[15,19]},77:7(O,Z){d m=q.u({5l:11,5r:11,5B:11,5q:11,29:0,2f:0},N[2]||{});O=$(O);d p=2d.4e(O);Z=$(Z);d 2J=[0,0];d 3v=1L;f(k.1R(Z,\'14\')==\'2o\'){3v=2d.1Q(Z);2J=2d.4e(3v)}f(3v==J.1c){2J[0]-=J.1c.2f;2J[1]-=J.1c.29}f(m.5l)Z.l.18=(p[0]-2J[0]+m.2f)+\'1m\';f(m.5r)Z.l.1n=(p[1]-2J[1]+m.29)+\'1m\';f(m.5B)Z.l.21=O.2p+\'1m\';f(m.5q)Z.l.24=O.2k+\'1m\'},8b:7(4){4=$(4);f(4.l.14==\'2o\')c;2d.4P();d 2P=2d.68(4);d 1n=2P[1];d 18=2P[0];d 21=4.6m;d 24=4.6p;4.6P=18-3X(4.l.18||0);4.6I=1n-3X(4.l.1n||0);4.5k=4.l.21;4.7f=4.l.24;4.l.14=\'2o\';4.l.1n=1n+\'1m\';4.l.18=18+\'1m\';4.l.21=21+\'1m\';4.l.24=24+\'1m\'},8w:7(4){4=$(4);f(4.l.14==\'3T\')c;2d.4P();4.l.14=\'3T\';d 1n=3X(4.l.1n||0)-(4.6I||0);d 18=3X(4.l.18||0)-(4.6P||0);4.l.1n=1n+\'1m\';4.l.18=18+\'1m\';4.l.24=4.7f;4.l.21=4.5k}};f(/3x|3w|3u/.4v(33.62)){2d.35=7(4){d 19=0,15=0;2q{19+=4.29||0;15+=4.2f||0;f(4.1Q==J.1c)f(k.1R(4,\'14\')==\'2o\')1y;4=4.1Q}1H(4);c[15,19]}};',62,600,'||||element||this|function|||||return|var||if||value|||Element|style|options||iterator||Object|||length|extend|prototype|index|||new||name|event|Form|transport||for||match|document||result|Ajax|arguments|source|results|form|||Prototype|object|each|push||false|target||true|elements||position|valueL||create|left|valueT|callback|Class|body|bind|Abstract|className|Insertion|tagName|pair|try|select|url|px|top|Event|Enumerable|div|case|catch|offset|useCapture|undefined|container|getValue|break|replacement|pattern|onComplete|map|else|Array|method|property|while|initialize|frequency|range|null|include|join|key|requestHeaders|offsetParent|getStyle|parameter|observers|inspect|observer|window|parentNode|text|toArray|els|width|start|args|height|throw|request||parameters|offsetTop|methods|opt|EventObserver|Position|html|offsetLeft|type|fragments|typeof|fragment|offsetHeight|oStringList|_each|apply|absolute|offsetWidth|do|cache|decay|content|input|emptyFunction|toLowerCase|end|replace|childNodes|registerCallback|display|toString|onTimerEvent|Serializers|readyState|scrollLeft|exclusive|on|delta|lastValue|getElements|in|hash|scrollTop|offsets|stripScripts|iterable|truncation|gsub|template|initializeRange|Responders|insertContent|setTimeout|json||hidden|status|navigator|ycomp|cumulativeOffset|inject||||serialize|responders|_overflow|Methods|collect|adjacency|xcomp|innerHTML|count|split|response|responseIsSuccess|success|Template|responder|dispatchException|evalScripts|pluck|concat|Request|KHTML|parent|Safari|Konqueror|dispatch|failure|receiver|observe|Observer|TimedObserver|ScriptFragment|responseText|script|inputs|ancestor|textarea|classNames|ObjectRange|node|typeName|RegExp|camelize|none|documentElement|ClassNames|relative|right|overflow|HTMLElement|parseFloat|toUpperCase|mode||currentlyExecuting|remove|__method|Base|memo||slice|returnValue|continue|setOptions|String|classNameToRemove|_reverse|page|focus|queryComponent|Hash|attachEvent|post|detachEvent|Version|reverse|encodeURIComponent|disabled|asynchronous|eval|respondToReadyState|activeRequestCount|Complete|appVersion|test|getTransport|containers|matchingInputs|1000|header|updater|Updater|getElementsByTagName|child|responderToAdd|static|tagElements|queryComponents|defaultView|onElementEvent|css|forElement|values|visibility|prepare|mergedHash|pos|offsetcache|_madePositioned|visible|tbody|findOrStore|_nativeExtensions|createElement|digits|trues|found|prepareReplacement|before|camelizedString|insertBefore|selectNodeContents|exception|falses|criteria|classNameToAdd|params|set|destination|last|addEventListener|indexOf|inline|clear|succ|_originalWidth|setLeft|Top|ActiveXObject|scriptTag|matchOne|setHeight|setTop|without|responderToRemove|_observeAndCache|find|reset|removeEventListener|activate|findFirstElement|setRequestHeaders|setWidth|XMLHttpRequest|constructor|application|xml|onCreate|contentType|Field|onStateChange|keydown|Events|evalJSON|stopObserving|inputSelector|img|Content|evalResponse|selectOne|onreadystatechange|keypress|postBody|onException|selectMany|updateContent|setInterval|insertion|PeriodicalUpdater|userAgent|updateComplete|lastText|timer|unloadCache|registerFormCallbacks|positionedOffset|register|parentElement|children|switch|XMLHTTP|_extended|hide|checkbox|update|radio|these|outerHTML|password|clientWidth|one|includeScrollOffsets|clientHeight|nodeValue|withinIncludingScrolloffsets|Try|scrollTo|realOffset|getComputedStyle|show|currentStyle|auto|deltaX|originalPosition|originalVisibility|originalWidth|originalHeight|opera|deltaY|bottom|array|_originalTop|flatten|addMethods|lambda|Toggle|toggle|insertAdjacentHTML|_originalLeft|PeriodicalExecuter|ownerDocument|createRange|createContextualFragment|contentFromAnonymousTable|table|Before|which|matchAll|button|extractScripts|stripTags|pairString|len|collapse|appendChild|After|clone|toQueryParams|Pattern|evaluate|stop|stringValue|preventDefault|charAt|_originalHeight|substring|Bottom|pairs|Function|add|collections|javascript|detect|findAll|entries|from|first|compact|keys|merge|present|toQueryString|getInputs|Msxml2|Microsoft|unregister|disable|urlencoded|blur|300|enable|responseIsFailure|Uninitialized|Loaded|Interactive|_|get|focusFirstElement|open|send|Requested|With|Accept|overrideMimeType|Connection|close|setRequestHeader|getResponseHeader|JSON|gi|submit|Loading|Success|Failure|checked|200|selectedIndex|www|selected|bMSIE|clearTimeout|unload|absolutize|string|getElementById|pageXOffset|getElementsByClassName|pageYOffset|removeChild|replaceChild|click|getHeight|hasClassName|addClassName|removeClassName|within|cleanWhitespace|nodeType|empty|childOf|multiple|change|getPropertyValue|relativize|setStyle|getDimensions|makePositioned|undoPositioned|makeClipping|KEY_BACKSPACE|undoClipping|overlap|KEY_TAB|vertical|KEY_RETURN|KEY_ESC|KEY_LEFT|horizontal|KEY_UP|member|tr|KEY_RIGHT|0_RC_0|Number|instanceof|shift|KEY_DOWN|bindAsEventListener|call|toColorPart|KEY_DELETE|times|finally|callee|srcElement|sub|scan|truncate|isLeftClick|beforeBegin|setStartBefore|im|afterBegin|firstChild|pointerX|unescapeHTML|beforeEnd|pageX|clientX|pointerY|parseQuery|afterEnd|pageY|clientY|RangeError|setStartAfter|all|any|grep|invoke|stopPropagation|max|nextSibling|min|partition|cancelBubble|reject|sortBy|sort|findElement|zip|pop|createTextNode|escapeHTML|strip'.split('|'),0,{}) + +} + +var _sunSpiderInterval = new Date() - _sunSpiderStartDate; + +document.getElementById("console").innerHTML = _sunSpiderInterval; +</script> + + +</body> +</html> diff --git a/build/pgo/js-input/string-validate-input.html b/build/pgo/js-input/string-validate-input.html new file mode 100644 index 000000000..72cf920b2 --- /dev/null +++ b/build/pgo/js-input/string-validate-input.html @@ -0,0 +1,139 @@ +<!DOCTYPE html> +<head> +<!-- + Copyright (C) 2007 Apple Inc. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--> + +<title>SunSpider string-validate-input</title> + +</head> + +<body> +<h3>string-validate-input</h3> +<div id="console"> +</div> + +<script> + +var _sunSpiderStartDate = new Date(); + +letters = new Array("a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z"); +numbers = new Array(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26); +colors = new Array("FF","CC","99","66","33","00"); + +var endResult; + +function doTest() +{ + endResult = ""; + + // make up email address + for (var k=0;k<4000;k++) + { + name = makeName(6); + (k%2)?email=name+"@mac.com":email=name+"(at)mac.com"; + + // validate the email address + var pattern = /^[a-zA-Z0-9\-\._]+@[a-zA-Z0-9\-_]+(\.?[a-zA-Z0-9\-_]*)\.[a-zA-Z]{2,3}$/; + + if(pattern.test(email)) + { + var r = email + " appears to be a valid email address."; + addResult(r); + } + else + { + r = email + " does NOT appear to be a valid email address."; + addResult(r); + } + } + + // make up ZIP codes + for (var s=0;s<4000;s++) + { + var zipGood = true; + var zip = makeNumber(4); + (s%2)?zip=zip+"xyz":zip=zip.concat("7"); + + // validate the zip code + for (var i = 0; i < zip.length; i++) { + var ch = zip.charAt(i); + if (ch < "0" || ch > "9") { + zipGood = false; + r = zip + " contains letters."; + addResult(r); + } + } + if (zipGood && zip.length>5) + { + zipGood = false; + r = zip + " is longer than five characters."; + addResult(r); + } + if (zipGood) + { + r = zip + " appears to be a valid ZIP code."; + addResult(r); + } + } +} + +function makeName(n) +{ + var tmp = ""; + for (var i=0;i<n;i++) + { + var l = Math.floor(26*Math.random()); + tmp += letters[l]; + } + return tmp; +} + +function makeNumber(n) +{ + var tmp = ""; + for (var i=0;i<n;i++) + { + var l = Math.floor(9*Math.random()); + tmp = tmp.concat(l); + } + return tmp; +} + +function addResult(r) +{ + endResult += "\n" + r; +} + +doTest(); + + +var _sunSpiderInterval = new Date() - _sunSpiderStartDate; + +document.getElementById("console").innerHTML = _sunSpiderInterval; +</script> + + +</body> +</html> diff --git a/build/pgo/js-input/valid-xhtml10.png b/build/pgo/js-input/valid-xhtml10.png Binary files differnew file mode 100644 index 000000000..2275ee6ea --- /dev/null +++ b/build/pgo/js-input/valid-xhtml10.png diff --git a/build/pgo/profileserver.py b/build/pgo/profileserver.py new file mode 100644 index 000000000..fae1775e3 --- /dev/null +++ b/build/pgo/profileserver.py @@ -0,0 +1,71 @@ +#!/usr/bin/python +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +from mozprofile import FirefoxProfile, Profile, Preferences +from mozprofile.permissions import ServerLocations +from mozrunner import FirefoxRunner, CLI +from mozhttpd import MozHttpd +import json +import socket +import threading +import os +import sys +import shutil +import tempfile +from datetime import datetime +from mozbuild.base import MozbuildObject + +PORT = 8888 + +if __name__ == '__main__': + cli = CLI() + debug_args, interactive = cli.debugger_arguments() + + build = MozbuildObject.from_environment() + httpd = MozHttpd(port=PORT, + docroot=os.path.join(build.topsrcdir, "build", "pgo")) + httpd.start(block=False) + + locations = ServerLocations() + locations.add_host(host='127.0.0.1', + port=PORT, + options='primary,privileged') + + #TODO: mozfile.TemporaryDirectory + profilePath = tempfile.mkdtemp() + try: + #TODO: refactor this into mozprofile + prefpath = os.path.join(build.topsrcdir, "testing", "profiles", "prefs_general.js") + prefs = {} + prefs.update(Preferences.read_prefs(prefpath)) + interpolation = { "server": "%s:%d" % httpd.httpd.server_address, + "OOP": "false"} + prefs = json.loads(json.dumps(prefs) % interpolation) + for pref in prefs: + prefs[pref] = Preferences.cast(prefs[pref]) + profile = FirefoxProfile(profile=profilePath, + preferences=prefs, + #addons=[os.path.join(here, 'extension')], + locations=locations) + + env = os.environ.copy() + env["MOZ_CRASHREPORTER_NO_REPORT"] = "1" + env["XPCOM_DEBUG_BREAK"] = "warn" + jarlog = os.getenv("JARLOG_FILE") + if jarlog: + env["MOZ_JAR_LOG_FILE"] = os.path.abspath(jarlog) + print "jarlog: %s" % env["MOZ_JAR_LOG_FILE"] + + cmdargs = ["http://localhost:%d/index.html" % PORT] + runner = FirefoxRunner(profile=profile, + binary=build.get_binary_path(where="staged-package"), + cmdargs=cmdargs, + env=env) + runner.start(debug_args=debug_args, interactive=interactive) + runner.wait() + httpd.stop() + finally: + shutil.rmtree(profilePath) diff --git a/build/pgo/server-locations.txt b/build/pgo/server-locations.txt new file mode 100644 index 000000000..98c7d3f34 --- /dev/null +++ b/build/pgo/server-locations.txt @@ -0,0 +1,209 @@ +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +# +# This file defines the locations at which this HTTP server may be accessed. +# It is referred to by the following page, so if this file moves, that page must +# be modified accordingly: +# +# http://developer.mozilla.org/en/docs/Mochitest#How_do_I_test_issues_which_only_show_up_when_tests_are_run_across_domains.3F +# +# Empty lines and lines which begin with "#" are ignored and may be used for +# storing comments. All other lines consist of an origin followed by whitespace +# and a comma-separated list of options (if indeed any options are needed). +# +# The format of an origin is, referring to RFC 2396, a scheme (either "http" or +# "https"), followed by "://", followed by a host, followed by ":", followed by +# a port number. The colon and port number must be present even if the port +# number is the default for the protocol. +# +# Unrecognized options are ignored. Recognized options are "primary" and +# "privileged", "nocert", "cert=some_cert_nickname", "redir=hostname". +# +# "primary" denotes a location which is the canonical location of +# the server; this location is the one assumed for requests which don't +# otherwise identify a particular origin (e.g. HTTP/1.0 requests). +# +# "privileged" denotes a location which should have the ability to request +# elevated privileges; the default is no privileges. +# +# "nocert" makes sense only for https:// hosts and means there is not +# any certificate automatically generated for this host. +# +# "cert=nickname" tells the pgo server to use a particular certificate +# for this host. The certificate is referenced by its nickname that must +# not contain any spaces. The certificate key files (PKCS12 modules) +# for custom certification are loaded from build/pgo/certs +# directory. When new certificate is added to this dir pgo/ssltunnel +# must be built then. This is only necessary for cases where we really do +# want specific certs. +# +# "redir=hostname" tells the pgo server is only used for https:// +# hosts while processing the CONNECT tunnel request. It responds +# to the CONNECT with a 302 and redirection to the hostname instead +# of connecting to the real back end and replying with a 200. This +# mode exists primarily to ensure we don't allow a proxy to do that. +# + +# +# This is the primary location from which tests run. +# +http://mochi.test:8888 primary,privileged + +# +# These are a common set of prefixes scattered across one TLD with two ports and +# another TLD on a single port. +# +http://127.0.0.1:80 privileged +http://127.0.0.1:8888 privileged +http://test:80 privileged +http://mochi.test:8888 privileged +http://example.org:80 privileged +http://test1.example.org:80 privileged +http://test2.example.org:80 privileged +http://sub1.test1.example.org:80 privileged +http://sub1.test2.example.org:80 privileged +http://sub2.test1.example.org:80 privileged +http://sub2.test2.example.org:80 privileged +http://example.org:8000 privileged +http://test1.example.org:8000 privileged +http://test2.example.org:8000 privileged +http://sub1.test1.example.org:8000 privileged +http://sub1.test2.example.org:8000 privileged +http://sub2.test1.example.org:8000 privileged +http://sub2.test2.example.org:8000 privileged +http://example.com:80 privileged +http://www.example.com:80 privileged +http://test1.example.com:80 privileged +http://test2.example.com:80 privileged +http://sub1.test1.example.com:80 privileged +http://sub1.test2.example.com:80 privileged +http://sub2.test1.example.com:80 privileged +http://sub2.test2.example.com:80 privileged +http://noxul.example.com:80 privileged,noxul + +https://example.com:443 privileged +https://test1.example.com:443 privileged +https://test2.example.com:443 privileged +https://sub1.test1.example.com:443 privileged +https://sub1.test2.example.com:443 privileged +https://sub2.test1.example.com:443 privileged +https://sub2.test2.example.com:443 privileged +https://nocert.example.com:443 privileged,nocert +https://self-signed.example.com:443 privileged,cert=selfsigned +https://untrusted.example.com:443 privileged,cert=untrusted +https://expired.example.com:443 privileged,cert=expired +https://requestclientcert.example.com:443 privileged,clientauth=request +https://requireclientcert.example.com:443 privileged,clientauth=require +https://mismatch.expired.example.com:443 privileged,cert=expired +https://mismatch.untrusted.example.com:443 privileged,cert=untrusted +https://untrusted-expired.example.com:443 privileged,cert=untrustedandexpired +https://mismatch.untrusted-expired.example.com:443 privileged,cert=untrustedandexpired +https://ev-valid.example.com:443 privileged,cert=evvalid +https://ev-invalid.example.com:443 priviliged,cert=evinvalid + +# This is here so that we don't load the default live bookmark over +# the network in every test suite. +http://fxfeeds.mozilla.com:80 + +# Prevent safebrowsing tests from hitting the network for its-a-trap.html and +# its-an-attack.html. +http://www.mozilla.org:80 + +# +# These are subdomains of <ält.example.org>. +# +http://sub1.xn--lt-uia.example.org:8000 privileged +http://sub2.xn--lt-uia.example.org:80 privileged +http://xn--exmple-cua.test:80 privileged +http://sub1.xn--exmple-cua.test:80 privileged +http://xn--exaple-kqf.test:80 privileged +http://sub1.xn--exaple-kqf.test:80 privileged + +https://xn--hxajbheg2az3al.xn--jxalpdlp:443 privileged +https://sub1.xn--hxajbheg2az3al.xn--jxalpdlp:443 privileged + +# +# These are subdomains of <παράδειγμα.δοκιμή>, the Greek IDN for example.test. +# +http://xn--hxajbheg2az3al.xn--jxalpdlp:80 privileged +http://sub1.xn--hxajbheg2az3al.xn--jxalpdlp:80 privileged + +# Bug 413909 test host +https://bug413909.xn--hxajbheg2az3al.xn--jxalpdlp:443 privileged,cert=bug413909cert + +# +# These hosts are used in tests which exercise privilege-granting functionality; +# we could reuse some of the names above, but specific names make it easier to +# distinguish one from the other in tests (as well as what functionality is +# being tested). +# +http://sectest1.example.org:80 privileged +http://sub.sectest2.example.org:80 privileged +http://sectest2.example.org:80 +http://sub.sectest1.example.org:80 + +https://sectest1.example.org:443 privileged +https://sub.sectest2.example.org:443 privileged +https://sectest2.example.org:443 +https://sub.sectest1.example.org:443 + +# +# Used while testing the url-classifier +# +http://malware.example.com:80 + +# Bug 483437, 484111 +https://www.bank1.com:443 privileged,cert=escapeattack1 +https://www.bank2.com:443 privileged,cert=escapeattack2 + +# +# CONNECT for redirproxy results in a 302 redirect to +# test1.example.com +# +https://redirproxy.example.com:443 privileged,redir=test1.example.com + +# Host used for IndexedDB Quota testing +http://bug704464-1.example.com:80 privileged +http://bug704464-2.example.com:80 privileged +http://bug704464-3.example.com:80 privileged +http://bug702292.example.com:80 privileged + +# W3C hosts. +# See http://www.w3.org/wiki/Testing/Requirements#The_Web_test_server_must_be_available_through_different_domain_names +http://w3c-test.org:80 +http://w3c-test.org:81 +http://w3c-test.org:82 +http://w3c-test.org:83 +http://www.w3c-test.org:80 +http://www.w3c-test.org:81 +http://www.w3c-test.org:82 +http://www.w3c-test.org:83 +http://www1.w3c-test.org:80 +http://www1.w3c-test.org:81 +http://www1.w3c-test.org:82 +http://www1.w3c-test.org:83 +http://www2.w3c-test.org:80 +http://www2.w3c-test.org:81 +http://www2.w3c-test.org:82 +http://www2.w3c-test.org:83 +# http://天気の良い日.w3c-test.org +http://xn--n8j6ds53lwwkrqhv28a.w3c-test.org:80 +http://xn--n8j6ds53lwwkrqhv28a.w3c-test.org:81 +http://xn--n8j6ds53lwwkrqhv28a.w3c-test.org:82 +http://xn--n8j6ds53lwwkrqhv28a.w3c-test.org:83 +# http://élève.w3c-test.org +http://xn--lve-6lad.w3c-test.org:80 +http://xn--lve-6lad.w3c-test.org:81 +http://xn--lve-6lad.w3c-test.org:82 +http://xn--lve-6lad.w3c-test.org:83 +# HTTPS versions of the above +https://w3c-test.org:443 +https://www.w3c-test.org:443 +https://www1.w3c-test.org:443 +https://www2.w3c-test.org:443 +https://xn--n8j6ds53lwwkrqhv28a.w3c-test.org:443 +https://xn--lve-6lad.w3c-test.org:443 +http://test.w3.org:80 diff --git a/build/pymake/.hgignore b/build/pymake/.hgignore new file mode 100644 index 000000000..b36df153f --- /dev/null +++ b/build/pymake/.hgignore @@ -0,0 +1,4 @@ +\.pyc$ +\.pyo$ +tests/.*.(py|g)makelog$ +~$ diff --git a/build/pymake/LICENSE b/build/pymake/LICENSE new file mode 100644 index 000000000..04a7d641d --- /dev/null +++ b/build/pymake/LICENSE @@ -0,0 +1,21 @@ +The MIT License + +Copyright (c) 2009 The Mozilla Foundation <http://www.mozilla.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. diff --git a/build/pymake/README b/build/pymake/README new file mode 100644 index 000000000..4f0fdfea4 --- /dev/null +++ b/build/pymake/README @@ -0,0 +1,64 @@ +INTRODUCTION + +make.py (and the pymake modules that support it) are an implementation of the make tool +which are mostly compatible with makefiles written for GNU make. + +PURPOSE + +The Mozilla project inspired this tool with several goals: + +* Improve build speeds, especially on Windows. This can be done by reducing the total number + of processes that are launched, especially MSYS shell processes which are expensive. + +* Allow writing some complicated build logic directly in Python instead of in shell. + +* Allow computing dependencies for special targets, such as members within ZIP files. + +* Enable experiments with build system. By writing a makefile parser, we can experiment + with converting in-tree makefiles to another build system, such as SCons, waf, ant, ...insert + your favorite build tool here. Or we could experiment along the lines of makepp, keeping + our existing makefiles, but change the engine to build a global dependency graph. + +KNOWN INCOMPATIBILITIES + +* Order-only prerequisites are not yet supported + +* Secondary expansion is not yet supported. + +* Target-specific variables behave differently than in GNU make: in pymake, the target-specific + variable only applies to the specific target that is mentioned, and does not apply recursively + to all dependencies which are remade. This is an intentional change: the behavior of GNU make + is neither deterministic nor intuitive. + +* $(eval) is only supported during the parse phase. Any attempt to recursively expand + an $(eval) function during command execution will fail. This is an intentional incompatibility. + +* There is a subtle difference in execution order that can cause unexpected changes in the + following circumstance: +** A file `foo.c` exists on the VPATH +** A rule for `foo.c` exists with a dependency on `tool` and no commands +** `tool` is remade for some other reason earlier in the file + In this case, pymake resets the VPATH of `foo.c`, while GNU make does not. This shouldn't + happen in the real world, since a target found on the VPATH without commands is silly. But + mozilla/js/src happens to have a rule, which I'm patching. + +* pymake does not implement any of the builtin implicit rules or the related variables. Mozilla + only cares because pymake doesn't implicitly define $(RM), which I'm also fixing in the Mozilla + code. + +ISSUES + +* Speed is a problem. + +FUTURE WORK + +* implement a new type of command which is implemented in python. This would allow us +to replace the current `nsinstall` binary (and execution costs for the shell and binary) with an +in-process python solution. + +AUTHOR + +Initial code was written by Benjamin Smedberg <benjamin@smedbergs.us>. For future releases see +http://benjamin.smedbergs.us/pymake/ + +See the LICENSE file for license information (MIT license) diff --git a/build/pymake/make.py b/build/pymake/make.py new file mode 100644 index 000000000..3ce623d4e --- /dev/null +++ b/build/pymake/make.py @@ -0,0 +1,29 @@ +#!/usr/bin/env python + +""" +make.py + +A drop-in or mostly drop-in replacement for GNU make. +""" + +import sys, os +import pymake.command, pymake.process + +import gc + +if __name__ == '__main__': + sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 0) + sys.stderr = os.fdopen(sys.stderr.fileno(), 'w', 0) + + gc.disable() + + # This is meant as a temporary workaround until issues with many targets + # and prerequisites is addressed. Bug 874210 tracks. + try: + sys.setrecursionlimit(2 * sys.getrecursionlimit()) + except Exception: + print >>sys.stderr, 'Unable to increase Python recursion limit.' + + pymake.command.main(sys.argv[1:], os.environ, os.getcwd(), cb=sys.exit) + pymake.process.ParallelContext.spin() + assert False, "Not reached" diff --git a/build/pymake/mkformat.py b/build/pymake/mkformat.py new file mode 100644 index 000000000..41dd761b2 --- /dev/null +++ b/build/pymake/mkformat.py @@ -0,0 +1,13 @@ +#!/usr/bin/env python + +import sys +import pymake.parser + +filename = sys.argv[1] +source = None + +with open(filename, 'rU') as fh: + source = fh.read() + +statements = pymake.parser.parsestring(source, filename) +print statements.to_source() diff --git a/build/pymake/mkparse.py b/build/pymake/mkparse.py new file mode 100644 index 000000000..253683948 --- /dev/null +++ b/build/pymake/mkparse.py @@ -0,0 +1,12 @@ +#!/usr/bin/env python + +import sys +import pymake.parser + +for f in sys.argv[1:]: + print "Parsing %s" % f + fd = open(f, 'rU') + s = fd.read() + fd.close() + stmts = pymake.parser.parsestring(s, f) + print stmts diff --git a/build/pymake/pymake/__init__.py b/build/pymake/pymake/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/build/pymake/pymake/__init__.py diff --git a/build/pymake/pymake/builtins.py b/build/pymake/pymake/builtins.py new file mode 100644 index 000000000..eb6f2e11b --- /dev/null +++ b/build/pymake/pymake/builtins.py @@ -0,0 +1,120 @@ +# Basic commands implemented in Python +import errno, sys, os, shutil, time +from getopt import getopt, GetoptError + +from process import PythonException + +__all__ = ["mkdir", "rm", "sleep", "touch"] + +def mkdir(args): + """ + Emulate some of the behavior of mkdir(1). + Only supports the -p (--parents) argument. + """ + try: + opts, args = getopt(args, "p", ["parents"]) + except GetoptError, e: + raise PythonException, ("mkdir: %s" % e, 1) + parents = False + for o, a in opts: + if o in ('-p', '--parents'): + parents = True + for f in args: + try: + if parents: + os.makedirs(f) + else: + os.mkdir(f) + except OSError, e: + if e.errno == errno.EEXIST and parents: + pass + else: + raise PythonException, ("mkdir: %s" % e, 1) + +def rm(args): + """ + Emulate most of the behavior of rm(1). + Only supports the -r (--recursive) and -f (--force) arguments. + """ + try: + opts, args = getopt(args, "rRf", ["force", "recursive"]) + except GetoptError, e: + raise PythonException, ("rm: %s" % e, 1) + force = False + recursive = False + for o, a in opts: + if o in ('-f', '--force'): + force = True + elif o in ('-r', '-R', '--recursive'): + recursive = True + for f in args: + if os.path.isdir(f): + if not recursive: + raise PythonException, ("rm: cannot remove '%s': Is a directory" % f, 1) + else: + shutil.rmtree(f, force) + elif os.path.exists(f): + try: + os.unlink(f) + except: + if not force: + raise PythonException, ("rm: failed to remove '%s': %s" % (f, sys.exc_info()[0]), 1) + elif not force: + raise PythonException, ("rm: cannot remove '%s': No such file or directory" % f, 1) + +def sleep(args): + """ + Emulate the behavior of sleep(1). + """ + total = 0 + values = {'s': 1, 'm': 60, 'h': 3600, 'd': 86400} + for a in args: + multiplier = 1 + for k, v in values.iteritems(): + if a.endswith(k): + a = a[:-1] + multiplier = v + break + try: + f = float(a) + total += f * multiplier + except ValueError: + raise PythonException, ("sleep: invalid time interval '%s'" % a, 1) + time.sleep(total) + +def touch(args): + """ + Emulate the behavior of touch(1). + """ + try: + opts, args = getopt(args, "t:") + except GetoptError, e: + raise PythonException, ("touch: %s" % e, 1) + opts = dict(opts) + times = None + if '-t' in opts: + import re + from time import mktime, localtime + m = re.match('^(?P<Y>(?:\d\d)?\d\d)?(?P<M>\d\d)(?P<D>\d\d)(?P<h>\d\d)(?P<m>\d\d)(?:\.(?P<s>\d\d))?$', opts['-t']) + if not m: + raise PythonException, ("touch: invalid date format '%s'" % opts['-t'], 1) + def normalized_field(m, f): + if f == 'Y': + if m.group(f) is None: + return localtime()[0] + y = int(m.group(f)) + if y < 69: + y += 2000 + elif y < 100: + y += 1900 + return y + if m.group(f) is None: + return localtime()[0] if f == 'Y' else 0 + return int(m.group(f)) + time = [normalized_field(m, f) for f in ['Y', 'M', 'D', 'h', 'm', 's']] + [0, 0, -1] + time = mktime(time) + times = (time, time) + for f in args: + if not os.path.exists(f): + open(f, 'a').close() + os.utime(f, times) diff --git a/build/pymake/pymake/command.py b/build/pymake/pymake/command.py new file mode 100644 index 000000000..cd68e4fdb --- /dev/null +++ b/build/pymake/pymake/command.py @@ -0,0 +1,278 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +""" +Makefile execution. + +Multiple `makes` can be run within the same process. Each one has an entirely data.Makefile and .Target +structure, environment, and working directory. Typically they will all share a parallel execution context, +except when a submake specifies -j1 when the parent make is building in parallel. +""" + +import os, subprocess, sys, logging, time, traceback, re +from optparse import OptionParser +import data, parserdata, process, util + +# TODO: If this ever goes from relocatable package to system-installed, this may need to be +# a configured-in path. + +makepypath = util.normaljoin(os.path.dirname(__file__), '../make.py') + +_simpleopts = re.compile(r'^[a-zA-Z]+(\s|$)') +def parsemakeflags(env): + """ + Parse MAKEFLAGS from the environment into a sequence of command-line arguments. + """ + + makeflags = env.get('MAKEFLAGS', '') + makeflags = makeflags.strip() + + if makeflags == '': + return [] + + if _simpleopts.match(makeflags): + makeflags = '-' + makeflags + + opts = [] + curopt = '' + + i = 0 + while i < len(makeflags): + c = makeflags[i] + if c.isspace(): + opts.append(curopt) + curopt = '' + i += 1 + while i < len(makeflags) and makeflags[i].isspace(): + i += 1 + continue + + if c == '\\': + i += 1 + if i == len(makeflags): + raise data.DataError("MAKEFLAGS has trailing backslash") + c = makeflags[i] + + curopt += c + i += 1 + + if curopt != '': + opts.append(curopt) + + return opts + +def _version(*args): + print """pymake: GNU-compatible make program +Copyright (C) 2009 The Mozilla Foundation <http://www.mozilla.org/> +This is free software; see the source for copying conditions. +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.""" + +_log = logging.getLogger('pymake.execution') + +class _MakeContext(object): + def __init__(self, makeflags, makelevel, workdir, context, env, targets, options, ostmts, overrides, cb): + self.makeflags = makeflags + self.makelevel = makelevel + + self.workdir = workdir + self.context = context + self.env = env + self.targets = targets + self.options = options + self.ostmts = ostmts + self.overrides = overrides + self.cb = cb + + self.restarts = 0 + + self.remakecb(True) + + def remakecb(self, remade, error=None): + if error is not None: + print error + self.context.defer(self.cb, 2) + return + + if remade: + if self.restarts > 0: + _log.info("make.py[%i]: Restarting makefile parsing", self.makelevel) + + self.makefile = data.Makefile(restarts=self.restarts, + make='%s %s' % (sys.executable.replace('\\', '/'), makepypath.replace('\\', '/')), + makeflags=self.makeflags, + makeoverrides=self.overrides, + workdir=self.workdir, + context=self.context, + env=self.env, + makelevel=self.makelevel, + targets=self.targets, + keepgoing=self.options.keepgoing, + silent=self.options.silent, + justprint=self.options.justprint) + + self.restarts += 1 + + try: + self.ostmts.execute(self.makefile) + for f in self.options.makefiles: + self.makefile.include(f) + self.makefile.finishparsing() + self.makefile.remakemakefiles(self.remakecb) + except util.MakeError, e: + print e + self.context.defer(self.cb, 2) + + return + + if len(self.targets) == 0: + if self.makefile.defaulttarget is None: + print "No target specified and no default target found." + self.context.defer(self.cb, 2) + return + + _log.info("Making default target %s", self.makefile.defaulttarget) + self.realtargets = [self.makefile.defaulttarget] + self.tstack = ['<default-target>'] + else: + self.realtargets = self.targets + self.tstack = ['<command-line>'] + + self.makefile.gettarget(self.realtargets.pop(0)).make(self.makefile, self.tstack, cb=self.makecb) + + def makecb(self, error, didanything): + assert error in (True, False) + + if error: + self.context.defer(self.cb, 2) + return + + if not len(self.realtargets): + if self.options.printdir: + print "make.py[%i]: Leaving directory '%s'" % (self.makelevel, self.workdir) + sys.stdout.flush() + + self.context.defer(self.cb, 0) + else: + self.makefile.gettarget(self.realtargets.pop(0)).make(self.makefile, self.tstack, self.makecb) + +def main(args, env, cwd, cb): + """ + Start a single makefile execution, given a command line, working directory, and environment. + + @param cb a callback to notify with an exit code when make execution is finished. + """ + + try: + makelevel = int(env.get('MAKELEVEL', '0')) + + op = OptionParser() + op.add_option('-f', '--file', '--makefile', + action='append', + dest='makefiles', + default=[]) + op.add_option('-d', + action="store_true", + dest="verbose", default=False) + op.add_option('-k', '--keep-going', + action="store_true", + dest="keepgoing", default=False) + op.add_option('--debug-log', + dest="debuglog", default=None) + op.add_option('-C', '--directory', + dest="directory", default=None) + op.add_option('-v', '--version', action="store_true", + dest="printversion", default=False) + op.add_option('-j', '--jobs', type="int", + dest="jobcount", default=1) + op.add_option('-w', '--print-directory', action="store_true", + dest="printdir") + op.add_option('--no-print-directory', action="store_false", + dest="printdir", default=True) + op.add_option('-s', '--silent', action="store_true", + dest="silent", default=False) + op.add_option('-n', '--just-print', '--dry-run', '--recon', + action="store_true", + dest="justprint", default=False) + + options, arguments1 = op.parse_args(parsemakeflags(env)) + options, arguments2 = op.parse_args(args, values=options) + + op.destroy() + + arguments = arguments1 + arguments2 + + if options.printversion: + _version() + cb(0) + return + + shortflags = [] + longflags = [] + + if options.keepgoing: + shortflags.append('k') + + if options.printdir: + shortflags.append('w') + + if options.silent: + shortflags.append('s') + options.printdir = False + + if options.justprint: + shortflags.append('n') + + loglevel = logging.WARNING + if options.verbose: + loglevel = logging.DEBUG + shortflags.append('d') + + logkwargs = {} + if options.debuglog: + logkwargs['filename'] = options.debuglog + longflags.append('--debug-log=%s' % options.debuglog) + + if options.directory is None: + workdir = cwd + else: + workdir = util.normaljoin(cwd, options.directory) + + if options.jobcount != 1: + longflags.append('-j%i' % (options.jobcount,)) + + makeflags = ''.join(shortflags) + if len(longflags): + makeflags += ' ' + ' '.join(longflags) + + logging.basicConfig(level=loglevel, **logkwargs) + + context = process.getcontext(options.jobcount) + + if options.printdir: + print "make.py[%i]: Entering directory '%s'" % (makelevel, workdir) + sys.stdout.flush() + + if len(options.makefiles) == 0: + if os.path.exists(util.normaljoin(workdir, 'Makefile')): + options.makefiles.append('Makefile') + else: + print "No makefile found" + cb(2) + return + + ostmts, targets, overrides = parserdata.parsecommandlineargs(arguments) + + _MakeContext(makeflags, makelevel, workdir, context, env, targets, options, ostmts, overrides, cb) + except (util.MakeError), e: + print e + if options.printdir: + print "make.py[%i]: Leaving directory '%s'" % (makelevel, workdir) + sys.stdout.flush() + cb(2) + return diff --git a/build/pymake/pymake/data.py b/build/pymake/pymake/data.py new file mode 100644 index 000000000..7100c0dcf --- /dev/null +++ b/build/pymake/pymake/data.py @@ -0,0 +1,1824 @@ +""" +A representation of makefile data structures. +""" + +import logging, re, os, sys +import parserdata, parser, functions, process, util, implicit +from cStringIO import StringIO + +if sys.version_info[0] < 3: + str_type = basestring +else: + str_type = str + +_log = logging.getLogger('pymake.data') + +class DataError(util.MakeError): + pass + +class ResolutionError(DataError): + """ + Raised when dependency resolution fails, either due to recursion or to missing + prerequisites.This is separately catchable so that implicit rule search can try things + without having to commit. + """ + pass + +def withoutdups(it): + r = set() + for i in it: + if not i in r: + r.add(i) + yield i + +def mtimeislater(deptime, targettime): + """ + Is the mtime of the dependency later than the target? + """ + + if deptime is None: + return True + if targettime is None: + return False + # int(1000*x) because of http://bugs.python.org/issue10148 + return int(1000 * deptime) > int(1000 * targettime) + +def getmtime(path): + try: + s = os.stat(path) + return s.st_mtime + except OSError: + return None + +def stripdotslash(s): + if s.startswith('./'): + st = s[2:] + return st if st != '' else '.' + return s + +def stripdotslashes(sl): + for s in sl: + yield stripdotslash(s) + +def getindent(stack): + return ''.ljust(len(stack) - 1) + +def _if_else(c, t, f): + if c: + return t() + return f() + + +class BaseExpansion(object): + """Base class for expansions. + + A make expansion is the parsed representation of a string, which may + contain references to other elements. + """ + + @property + def is_static_string(self): + """Returns whether the expansion is composed of static string content. + + This is always True for StringExpansion. It will be True for Expansion + only if all elements of that Expansion are static strings. + """ + raise Exception('Must be implemented in child class.') + + def functions(self, descend=False): + """Obtain all functions inside this expansion. + + This is a generator for pymake.functions.Function instances. + + By default, this only returns functions existing as the primary + elements of this expansion. If `descend` is True, it will descend into + child expansions and extract all functions in the tree. + """ + # An empty generator. Yeah, it's weird. + for x in []: + yield x + + def variable_references(self, descend=False): + """Obtain all variable references in this expansion. + + This is a generator for pymake.functionsVariableRef instances. + + To retrieve the names of variables, simply query the `vname` field on + the returned instances. Most of the time these will be StringExpansion + instances. + """ + for f in self.functions(descend=descend): + if not isinstance(f, functions.VariableRef): + continue + + yield f + + @property + def is_filesystem_dependent(self): + """Whether this expansion may query the filesystem for evaluation. + + This effectively asks "is any function in this expansion dependent on + the filesystem. + """ + for f in self.functions(descend=True): + if f.is_filesystem_dependent: + return True + + return False + + @property + def is_shell_dependent(self): + """Whether this expansion may invoke a shell for evaluation.""" + + for f in self.functions(descend=True): + if isinstance(f, functions.ShellFunction): + return True + + return False + + +class StringExpansion(BaseExpansion): + """An Expansion representing a static string. + + This essentially wraps a single str instance. + """ + + __slots__ = ('loc', 's',) + simple = True + + def __init__(self, s, loc): + assert isinstance(s, str_type) + self.s = s + self.loc = loc + + def lstrip(self): + self.s = self.s.lstrip() + + def rstrip(self): + self.s = self.s.rstrip() + + def isempty(self): + return self.s == '' + + def resolve(self, i, j, fd, k=None): + fd.write(self.s) + + def resolvestr(self, i, j, k=None): + return self.s + + def resolvesplit(self, i, j, k=None): + return self.s.split() + + def clone(self): + e = Expansion(self.loc) + e.appendstr(self.s) + return e + + @property + def is_static_string(self): + return True + + def __len__(self): + return 1 + + def __getitem__(self, i): + assert i == 0 + return self.s, False + + def __repr__(self): + return "Exp<%s>(%r)" % (self.loc, self.s) + + def __eq__(self, other): + """We only compare the string contents.""" + return self.s == other + + def __ne__(self, other): + return not self.__eq__(other) + + def to_source(self, escape_variables=False, escape_comments=False): + s = self.s + + if escape_comments: + s = s.replace('#', '\\#') + + if escape_variables: + return s.replace('$', '$$') + + return s + + +class Expansion(BaseExpansion, list): + """A representation of expanded data. + + This is effectively an ordered list of StringExpansion and + pymake.function.Function instances. Every item in the collection appears in + the same context in a make file. + """ + + __slots__ = ('loc',) + simple = False + + def __init__(self, loc=None): + # A list of (element, isfunc) tuples + # element is either a string or a function + self.loc = loc + + @staticmethod + def fromstring(s, path): + return StringExpansion(s, parserdata.Location(path, 1, 0)) + + def clone(self): + e = Expansion() + e.extend(self) + return e + + def appendstr(self, s): + assert isinstance(s, str_type) + if s == '': + return + + self.append((s, False)) + + def appendfunc(self, func): + assert isinstance(func, functions.Function) + self.append((func, True)) + + def concat(self, o): + """Concatenate the other expansion on to this one.""" + if o.simple: + self.appendstr(o.s) + else: + self.extend(o) + + def isempty(self): + return (not len(self)) or self[0] == ('', False) + + def lstrip(self): + """Strip leading literal whitespace from this expansion.""" + while True: + i, isfunc = self[0] + if isfunc: + return + + i = i.lstrip() + if i != '': + self[0] = i, False + return + + del self[0] + + def rstrip(self): + """Strip trailing literal whitespace from this expansion.""" + while True: + i, isfunc = self[-1] + if isfunc: + return + + i = i.rstrip() + if i != '': + self[-1] = i, False + return + + del self[-1] + + def finish(self): + # Merge any adjacent literal strings: + strings = [] + elements = [] + for (e, isfunc) in self: + if isfunc: + if strings: + s = ''.join(strings) + if s: + elements.append((s, False)) + strings = [] + elements.append((e, True)) + else: + strings.append(e) + + if not elements: + # This can only happen if there were no function elements. + return StringExpansion(''.join(strings), self.loc) + + if strings: + s = ''.join(strings) + if s: + elements.append((s, False)) + + if len(elements) < len(self): + self[:] = elements + + return self + + def resolve(self, makefile, variables, fd, setting=[]): + """ + Resolve this variable into a value, by interpolating the value + of other variables. + + @param setting (Variable instance) the variable currently + being set, if any. Setting variables must avoid self-referential + loops. + """ + assert isinstance(makefile, Makefile) + assert isinstance(variables, Variables) + assert isinstance(setting, list) + + for e, isfunc in self: + if isfunc: + e.resolve(makefile, variables, fd, setting) + else: + assert isinstance(e, str_type) + fd.write(e) + + def resolvestr(self, makefile, variables, setting=[]): + fd = StringIO() + self.resolve(makefile, variables, fd, setting) + return fd.getvalue() + + def resolvesplit(self, makefile, variables, setting=[]): + return self.resolvestr(makefile, variables, setting).split() + + @property + def is_static_string(self): + """An Expansion is static if all its components are strings, not + functions.""" + for e, is_func in self: + if is_func: + return False + + return True + + def functions(self, descend=False): + for e, is_func in self: + if is_func: + yield e + + if descend: + for exp in e.expansions(descend=True): + for f in exp.functions(descend=True): + yield f + + def __repr__(self): + return "<Expansion with elements: %r>" % ([e for e, isfunc in self],) + + def to_source(self, escape_variables=False, escape_comments=False): + parts = [] + for e, is_func in self: + if is_func: + parts.append(e.to_source()) + continue + + if escape_variables: + parts.append(e.replace('$', '$$')) + continue + + parts.append(e) + + return ''.join(parts) + + def __eq__(self, other): + if not isinstance(other, (Expansion, StringExpansion)): + return False + + # Expansions are equivalent if adjacent string literals normalize to + # the same value. So, we must normalize before any comparisons are + # made. + a = self.clone().finish() + + if isinstance(other, StringExpansion): + if isinstance(a, StringExpansion): + return a == other + + # A normalized Expansion != StringExpansion. + return False + + b = other.clone().finish() + + # b could be a StringExpansion now. + if isinstance(b, StringExpansion): + if isinstance(a, StringExpansion): + return a == b + + # Our normalized Expansion != normalized StringExpansion. + return False + + if len(a) != len(b): + return False + + for i in xrange(len(self)): + e1, is_func1 = a[i] + e2, is_func2 = b[i] + + if is_func1 != is_func2: + return False + + if type(e1) != type(e2): + return False + + if e1 != e2: + return False + + return True + + def __ne__(self, other): + return not self.__eq__(other) + +class Variables(object): + """ + A mapping from variable names to variables. Variables have flavor, source, and value. The value is an + expansion object. + """ + + __slots__ = ('parent', '_map') + + FLAVOR_RECURSIVE = 0 + FLAVOR_SIMPLE = 1 + FLAVOR_APPEND = 2 + + SOURCE_OVERRIDE = 0 + SOURCE_COMMANDLINE = 1 + SOURCE_MAKEFILE = 2 + SOURCE_ENVIRONMENT = 3 + SOURCE_AUTOMATIC = 4 + SOURCE_IMPLICIT = 5 + + def __init__(self, parent=None): + self._map = {} # vname -> flavor, source, valuestr, valueexp + self.parent = parent + + def readfromenvironment(self, env): + for k, v in env.iteritems(): + self.set(k, self.FLAVOR_RECURSIVE, self.SOURCE_ENVIRONMENT, v) + + def get(self, name, expand=True): + """ + Get the value of a named variable. Returns a tuple (flavor, source, value) + + If the variable is not present, returns (None, None, None) + + @param expand If true, the value will be returned as an expansion. If false, + it will be returned as an unexpanded string. + """ + flavor, source, valuestr, valueexp = self._map.get(name, (None, None, None, None)) + if flavor is not None: + if expand and flavor != self.FLAVOR_SIMPLE and valueexp is None: + d = parser.Data.fromstring(valuestr, parserdata.Location("Expansion of variables '%s'" % (name,), 1, 0)) + valueexp, t, o = parser.parsemakesyntax(d, 0, (), parser.iterdata) + self._map[name] = flavor, source, valuestr, valueexp + + if flavor == self.FLAVOR_APPEND: + if self.parent: + pflavor, psource, pvalue = self.parent.get(name, expand) + else: + pflavor, psource, pvalue = None, None, None + + if pvalue is None: + flavor = self.FLAVOR_RECURSIVE + # fall through + else: + if source > psource: + # TODO: log a warning? + return pflavor, psource, pvalue + + if not expand: + return pflavor, psource, pvalue + ' ' + valuestr + + pvalue = pvalue.clone() + pvalue.appendstr(' ') + pvalue.concat(valueexp) + + return pflavor, psource, pvalue + + if not expand: + return flavor, source, valuestr + + if flavor == self.FLAVOR_RECURSIVE: + val = valueexp + else: + val = Expansion.fromstring(valuestr, "Expansion of variable '%s'" % (name,)) + + return flavor, source, val + + if self.parent is not None: + return self.parent.get(name, expand) + + return (None, None, None) + + def set(self, name, flavor, source, value): + assert flavor in (self.FLAVOR_RECURSIVE, self.FLAVOR_SIMPLE) + assert source in (self.SOURCE_OVERRIDE, self.SOURCE_COMMANDLINE, self.SOURCE_MAKEFILE, self.SOURCE_ENVIRONMENT, self.SOURCE_AUTOMATIC, self.SOURCE_IMPLICIT) + assert isinstance(value, str_type), "expected str, got %s" % type(value) + + prevflavor, prevsource, prevvalue = self.get(name) + if prevsource is not None and source > prevsource: + # TODO: give a location for this warning + _log.info("not setting variable '%s', set by higher-priority source to value '%s'" % (name, prevvalue)) + return + + self._map[name] = flavor, source, value, None + + def append(self, name, source, value, variables, makefile): + assert source in (self.SOURCE_OVERRIDE, self.SOURCE_MAKEFILE, self.SOURCE_AUTOMATIC) + assert isinstance(value, str_type) + + if name not in self._map: + self._map[name] = self.FLAVOR_APPEND, source, value, None + return + + prevflavor, prevsource, prevvalue, valueexp = self._map[name] + if source > prevsource: + # TODO: log a warning? + return + + if prevflavor == self.FLAVOR_SIMPLE: + d = parser.Data.fromstring(value, parserdata.Location("Expansion of variables '%s'" % (name,), 1, 0)) + valueexp, t, o = parser.parsemakesyntax(d, 0, (), parser.iterdata) + + val = valueexp.resolvestr(makefile, variables, [name]) + self._map[name] = prevflavor, prevsource, prevvalue + ' ' + val, None + return + + newvalue = prevvalue + ' ' + value + self._map[name] = prevflavor, prevsource, newvalue, None + + def merge(self, other): + assert isinstance(other, Variables) + for k, flavor, source, value in other: + self.set(k, flavor, source, value) + + def __iter__(self): + for k, (flavor, source, value, valueexp) in self._map.iteritems(): + yield k, flavor, source, value + + def __contains__(self, item): + return item in self._map + +class Pattern(object): + """ + A pattern is a string, possibly with a % substitution character. From the GNU make manual: + + '%' characters in pattern rules can be quoted with precending backslashes ('\'). Backslashes that + would otherwise quote '%' charcters can be quoted with more backslashes. Backslashes that + quote '%' characters or other backslashes are removed from the pattern before it is compared t + file names or has a stem substituted into it. Backslashes that are not in danger of quoting '%' + characters go unmolested. For example, the pattern the\%weird\\%pattern\\ has `the%weird\' preceding + the operative '%' character, and 'pattern\\' following it. The final two backslashes are left alone + because they cannot affect any '%' character. + + This insane behavior probably doesn't matter, but we're compatible just for shits and giggles. + """ + + __slots__ = ('data') + + def __init__(self, s): + r = [] + i = 0 + while i < len(s): + c = s[i] + if c == '\\': + nc = s[i + 1] + if nc == '%': + r.append('%') + i += 1 + elif nc == '\\': + r.append('\\') + i += 1 + else: + r.append(c) + elif c == '%': + self.data = (''.join(r), s[i+1:]) + return + else: + r.append(c) + i += 1 + + # This is different than (s,) because \% and \\ have been unescaped. Parsing patterns is + # context-sensitive! + self.data = (''.join(r),) + + def ismatchany(self): + return self.data == ('','') + + def ispattern(self): + return len(self.data) == 2 + + def __hash__(self): + return self.data.__hash__() + + def __eq__(self, o): + assert isinstance(o, Pattern) + return self.data == o.data + + def gettarget(self): + assert not self.ispattern() + return self.data[0] + + def hasslash(self): + return self.data[0].find('/') != -1 or self.data[1].find('/') != -1 + + def match(self, word): + """ + Match this search pattern against a word (string). + + @returns None if the word doesn't match, or the matching stem. + If this is a %-less pattern, the stem will always be '' + """ + d = self.data + if len(d) == 1: + if word == d[0]: + return word + return None + + d0, d1 = d + l1 = len(d0) + l2 = len(d1) + if len(word) >= l1 + l2 and word.startswith(d0) and word.endswith(d1): + if l2 == 0: + return word[l1:] + return word[l1:-l2] + + return None + + def resolve(self, dir, stem): + if self.ispattern(): + return dir + self.data[0] + stem + self.data[1] + + return self.data[0] + + def subst(self, replacement, word, mustmatch): + """ + Given a word, replace the current pattern with the replacement pattern, a la 'patsubst' + + @param mustmatch If true and this pattern doesn't match the word, throw a DataError. Otherwise + return word unchanged. + """ + assert isinstance(replacement, str_type) + + stem = self.match(word) + if stem is None: + if mustmatch: + raise DataError("target '%s' doesn't match pattern" % (word,)) + return word + + if not self.ispattern(): + # if we're not a pattern, the replacement is not parsed as a pattern either + return replacement + + return Pattern(replacement).resolve('', stem) + + def __repr__(self): + return "<Pattern with data %r>" % (self.data,) + + _backre = re.compile(r'[%\\]') + def __str__(self): + if not self.ispattern(): + return self._backre.sub(r'\\\1', self.data[0]) + + return self._backre.sub(r'\\\1', self.data[0]) + '%' + self.data[1] + +class RemakeTargetSerially(object): + __slots__ = ('target', 'makefile', 'indent', 'rlist') + + def __init__(self, target, makefile, indent, rlist): + self.target = target + self.makefile = makefile + self.indent = indent + self.rlist = rlist + self.commandscb(False) + + def resolvecb(self, error, didanything): + assert error in (True, False) + + if didanything: + self.target.didanything = True + + if error: + self.target.error = True + self.makefile.error = True + if not self.makefile.keepgoing: + self.target.notifydone(self.makefile) + return + else: + # don't run the commands! + del self.rlist[0] + self.commandscb(error=False) + else: + self.rlist.pop(0).runcommands(self.indent, self.commandscb) + + def commandscb(self, error): + assert error in (True, False) + + if error: + self.target.error = True + self.makefile.error = True + + if self.target.error and not self.makefile.keepgoing: + self.target.notifydone(self.makefile) + return + + if not len(self.rlist): + self.target.notifydone(self.makefile) + else: + self.rlist[0].resolvedeps(True, self.resolvecb) + +class RemakeTargetParallel(object): + __slots__ = ('target', 'makefile', 'indent', 'rlist', 'rulesremaining', 'currunning') + + def __init__(self, target, makefile, indent, rlist): + self.target = target + self.makefile = makefile + self.indent = indent + self.rlist = rlist + + self.rulesremaining = len(rlist) + self.currunning = False + + for r in rlist: + makefile.context.defer(self.doresolve, r) + + def doresolve(self, r): + if self.makefile.error and not self.makefile.keepgoing: + r.error = True + self.resolvecb(True, False) + else: + r.resolvedeps(False, self.resolvecb) + + def resolvecb(self, error, didanything): + assert error in (True, False) + + if error: + self.target.error = True + + if didanything: + self.target.didanything = True + + self.rulesremaining -= 1 + + # commandscb takes care of the details if we're currently building + # something + if self.currunning: + return + + self.runnext() + + def runnext(self): + assert not self.currunning + + if self.makefile.error and not self.makefile.keepgoing: + self.rlist = [] + else: + while len(self.rlist) and self.rlist[0].error: + del self.rlist[0] + + if not len(self.rlist): + if not self.rulesremaining: + self.target.notifydone(self.makefile) + return + + if self.rlist[0].depsremaining != 0: + return + + self.currunning = True + self.rlist.pop(0).runcommands(self.indent, self.commandscb) + + def commandscb(self, error): + assert error in (True, False) + if error: + self.target.error = True + self.makefile.error = True + + assert self.currunning + self.currunning = False + self.runnext() + +class RemakeRuleContext(object): + def __init__(self, target, makefile, rule, deps, + targetstack, avoidremakeloop): + self.target = target + self.makefile = makefile + self.rule = rule + self.deps = deps + self.targetstack = targetstack + self.avoidremakeloop = avoidremakeloop + + self.running = False + self.error = False + self.depsremaining = len(deps) + 1 + self.remake = False + + def resolvedeps(self, serial, cb): + self.resolvecb = cb + self.didanything = False + if serial: + self._resolvedepsserial() + else: + self._resolvedepsparallel() + + def _weakdepfinishedserial(self, error, didanything): + if error: + self.remake = True + self._depfinishedserial(False, didanything) + + def _depfinishedserial(self, error, didanything): + assert error in (True, False) + + if didanything: + self.didanything = True + + if error: + self.error = True + if not self.makefile.keepgoing: + self.resolvecb(error=True, didanything=self.didanything) + return + + if len(self.resolvelist): + dep, weak = self.resolvelist.pop(0) + self.makefile.context.defer(dep.make, + self.makefile, self.targetstack, weak and self._weakdepfinishedserial or self._depfinishedserial) + else: + self.resolvecb(error=self.error, didanything=self.didanything) + + def _resolvedepsserial(self): + self.resolvelist = list(self.deps) + self._depfinishedserial(False, False) + + def _startdepparallel(self, d): + if self.makefile.error: + depfinished(True, False) + else: + dep, weak = d + dep.make(self.makefile, self.targetstack, weak and self._weakdepfinishedparallel or self._depfinishedparallel) + + def _weakdepfinishedparallel(self, error, didanything): + if error: + self.remake = True + self._depfinishedparallel(False, didanything) + + def _depfinishedparallel(self, error, didanything): + assert error in (True, False) + + if error: + print "<%s>: Found error" % self.target.target + self.error = True + if didanything: + self.didanything = True + + self.depsremaining -= 1 + if self.depsremaining == 0: + self.resolvecb(error=self.error, didanything=self.didanything) + + def _resolvedepsparallel(self): + self.depsremaining -= 1 + if self.depsremaining == 0: + self.resolvecb(error=self.error, didanything=self.didanything) + return + + self.didanything = False + + for d in self.deps: + self.makefile.context.defer(self._startdepparallel, d) + + def _commandcb(self, error): + assert error in (True, False) + + if error: + self.runcb(error=True) + return + + if len(self.commands): + self.commands.pop(0)(self._commandcb) + else: + self.runcb(error=False) + + def runcommands(self, indent, cb): + assert not self.running + self.running = True + + self.runcb = cb + + if self.rule is None or not len(self.rule.commands): + if self.target.mtime is None: + self.target.beingremade() + else: + for d, weak in self.deps: + if mtimeislater(d.mtime, self.target.mtime): + if d.mtime is None: + self.target.beingremade() + else: + _log.info("%sNot remaking %s ubecause it would have no effect, even though %s is newer.", indent, self.target.target, d.target) + break + cb(error=False) + return + + if self.rule.doublecolon: + if len(self.deps) == 0: + if self.avoidremakeloop: + _log.info("%sNot remaking %s using rule at %s because it would introduce an infinite loop.", indent, self.target.target, self.rule.loc) + cb(error=False) + return + + remake = self.remake + if remake: + _log.info("%sRemaking %s using rule at %s: weak dependency was not found.", indent, self.target.target, self.rule.loc) + else: + if self.target.mtime is None: + remake = True + _log.info("%sRemaking %s using rule at %s: target doesn't exist or is a forced target", indent, self.target.target, self.rule.loc) + + if not remake: + if self.rule.doublecolon: + if len(self.deps) == 0: + _log.info("%sRemaking %s using rule at %s because there are no prerequisites listed for a double-colon rule.", indent, self.target.target, self.rule.loc) + remake = True + + if not remake: + for d, weak in self.deps: + if mtimeislater(d.mtime, self.target.mtime): + _log.info("%sRemaking %s using rule at %s because %s is newer.", indent, self.target.target, self.rule.loc, d.target) + remake = True + break + + if remake: + self.target.beingremade() + self.target.didanything = True + try: + self.commands = [c for c in self.rule.getcommands(self.target, self.makefile)] + except util.MakeError, e: + print e + sys.stdout.flush() + cb(error=True) + return + + self._commandcb(False) + else: + cb(error=False) + +MAKESTATE_NONE = 0 +MAKESTATE_FINISHED = 1 +MAKESTATE_WORKING = 2 + +class Target(object): + """ + An actual (non-pattern) target. + + It holds target-specific variables and a list of rules. It may also point to a parent + PatternTarget, if this target is being created by an implicit rule. + + The rules associated with this target may be Rule instances or, in the case of static pattern + rules, PatternRule instances. + """ + + wasremade = False + + def __init__(self, target, makefile): + assert isinstance(target, str_type) + self.target = target + self.vpathtarget = None + self.rules = [] + self.variables = Variables(makefile.variables) + self.explicit = False + self._state = MAKESTATE_NONE + + def addrule(self, rule): + assert isinstance(rule, (Rule, PatternRuleInstance)) + if len(self.rules) and rule.doublecolon != self.rules[0].doublecolon: + raise DataError("Cannot have single- and double-colon rules for the same target. Prior rule location: %s" % self.rules[0].loc, rule.loc) + + if isinstance(rule, PatternRuleInstance): + if len(rule.prule.targetpatterns) != 1: + raise DataError("Static pattern rules must only have one target pattern", rule.prule.loc) + if rule.prule.targetpatterns[0].match(self.target) is None: + raise DataError("Static pattern rule doesn't match target '%s'" % self.target, rule.loc) + + self.rules.append(rule) + + def isdoublecolon(self): + return self.rules[0].doublecolon + + def isphony(self, makefile): + """Is this a phony target? We don't check for existence of phony targets.""" + return makefile.gettarget('.PHONY').hasdependency(self.target) + + def hasdependency(self, t): + for rule in self.rules: + if t in rule.prerequisites: + return True + + return False + + def resolveimplicitrule(self, makefile, targetstack, rulestack): + """ + Try to resolve an implicit rule to build this target. + """ + # The steps in the GNU make manual Implicit-Rule-Search.html are very detailed. I hope they can be trusted. + + indent = getindent(targetstack) + + _log.info("%sSearching for implicit rule to make '%s'", indent, self.target) + + dir, s, file = util.strrpartition(self.target, '/') + dir = dir + s + + candidates = [] # list of PatternRuleInstance + + hasmatch = util.any((r.hasspecificmatch(file) for r in makefile.implicitrules)) + + for r in makefile.implicitrules: + if r in rulestack: + _log.info("%s %s: Avoiding implicit rule recursion", indent, r.loc) + continue + + if not len(r.commands): + continue + + for ri in r.matchesfor(dir, file, hasmatch): + candidates.append(ri) + + newcandidates = [] + + for r in candidates: + depfailed = None + for p in r.prerequisites: + t = makefile.gettarget(p) + t.resolvevpath(makefile) + if not t.explicit and t.mtime is None: + depfailed = p + break + + if depfailed is not None: + if r.doublecolon: + _log.info("%s Terminal rule at %s doesn't match: prerequisite '%s' not mentioned and doesn't exist.", indent, r.loc, depfailed) + else: + newcandidates.append(r) + continue + + _log.info("%sFound implicit rule at %s for target '%s'", indent, r.loc, self.target) + self.rules.append(r) + return + + # Try again, but this time with chaining and without terminal (double-colon) rules + + for r in newcandidates: + newrulestack = rulestack + [r.prule] + + depfailed = None + for p in r.prerequisites: + t = makefile.gettarget(p) + try: + t.resolvedeps(makefile, targetstack, newrulestack, True) + except ResolutionError: + depfailed = p + break + + if depfailed is not None: + _log.info("%s Rule at %s doesn't match: prerequisite '%s' could not be made.", indent, r.loc, depfailed) + continue + + _log.info("%sFound implicit rule at %s for target '%s'", indent, r.loc, self.target) + self.rules.append(r) + return + + _log.info("%sCouldn't find implicit rule to remake '%s'", indent, self.target) + + def ruleswithcommands(self): + "The number of rules with commands" + return reduce(lambda i, rule: i + (len(rule.commands) > 0), self.rules, 0) + + def resolvedeps(self, makefile, targetstack, rulestack, recursive): + """ + Resolve the actual path of this target, using vpath if necessary. + + Recursively resolve dependencies of this target. This means finding implicit + rules which match the target, if appropriate. + + Figure out whether this target needs to be rebuild, and set self.outofdate + appropriately. + + @param targetstack is the current stack of dependencies being resolved. If + this target is already in targetstack, bail to prevent infinite + recursion. + @param rulestack is the current stack of implicit rules being used to resolve + dependencies. A rule chain cannot use the same implicit rule twice. + """ + assert makefile.parsingfinished + + if self.target in targetstack: + raise ResolutionError("Recursive dependency: %s -> %s" % ( + " -> ".join(targetstack), self.target)) + + targetstack = targetstack + [self.target] + + indent = getindent(targetstack) + + _log.info("%sConsidering target '%s'", indent, self.target) + + self.resolvevpath(makefile) + + # Sanity-check our rules. If we're single-colon, only one rule should have commands + ruleswithcommands = self.ruleswithcommands() + if len(self.rules) and not self.isdoublecolon(): + if ruleswithcommands > 1: + # In GNU make this is a warning, not an error. I'm going to be stricter. + # TODO: provide locations + raise DataError("Target '%s' has multiple rules with commands." % self.target) + + if ruleswithcommands == 0: + self.resolveimplicitrule(makefile, targetstack, rulestack) + + # If a target is mentioned, but doesn't exist, has no commands and no + # prerequisites, it is special and exists just to say that targets which + # depend on it are always out of date. This is like .FORCE but more + # compatible with other makes. + # Otherwise, we don't know how to make it. + if not len(self.rules) and self.mtime is None and not util.any((len(rule.prerequisites) > 0 + for rule in self.rules)): + raise ResolutionError("No rule to make target '%s' needed by %r" % (self.target, + targetstack)) + + if recursive: + for r in self.rules: + newrulestack = rulestack + [r] + for d in r.prerequisites: + dt = makefile.gettarget(d) + if dt.explicit: + continue + + dt.resolvedeps(makefile, targetstack, newrulestack, True) + + for v in makefile.getpatternvariablesfor(self.target): + self.variables.merge(v) + + def resolvevpath(self, makefile): + if self.vpathtarget is not None: + return + + if self.isphony(makefile): + self.vpathtarget = self.target + self.mtime = None + return + + if self.target.startswith('-l'): + stem = self.target[2:] + f, s, e = makefile.variables.get('.LIBPATTERNS') + if e is not None: + libpatterns = [Pattern(stripdotslash(s)) for s in e.resolvesplit(makefile, makefile.variables)] + if len(libpatterns): + searchdirs = [''] + searchdirs.extend(makefile.getvpath(self.target)) + + for lp in libpatterns: + if not lp.ispattern(): + raise DataError('.LIBPATTERNS contains a non-pattern') + + libname = lp.resolve('', stem) + + for dir in searchdirs: + libpath = util.normaljoin(dir, libname).replace('\\', '/') + fspath = util.normaljoin(makefile.workdir, libpath) + mtime = getmtime(fspath) + if mtime is not None: + self.vpathtarget = libpath + self.mtime = mtime + return + + self.vpathtarget = self.target + self.mtime = None + return + + search = [self.target] + if not os.path.isabs(self.target): + search += [util.normaljoin(dir, self.target).replace('\\', '/') + for dir in makefile.getvpath(self.target)] + + targetandtime = self.searchinlocs(makefile, search) + if targetandtime is not None: + (self.vpathtarget, self.mtime) = targetandtime + return + + self.vpathtarget = self.target + self.mtime = None + + def searchinlocs(self, makefile, locs): + """ + Look in the given locations relative to the makefile working directory + for a file. Return a pair of the target and the mtime if found, None + if not. + """ + for t in locs: + fspath = util.normaljoin(makefile.workdir, t).replace('\\', '/') + mtime = getmtime(fspath) +# _log.info("Searching %s ... checking %s ... mtime %r" % (t, fspath, mtime)) + if mtime is not None: + return (t, mtime) + + return None + + def beingremade(self): + """ + When we remake ourself, we have to drop any vpath prefixes. + """ + self.vpathtarget = self.target + self.wasremade = True + + def notifydone(self, makefile): + assert self._state == MAKESTATE_WORKING, "State was %s" % self._state + # If we were remade then resolve mtime again + if self.wasremade: + targetandtime = self.searchinlocs(makefile, [self.target]) + if targetandtime is not None: + (_, self.mtime) = targetandtime + else: + self.mtime = None + + self._state = MAKESTATE_FINISHED + for cb in self._callbacks: + makefile.context.defer(cb, error=self.error, didanything=self.didanything) + del self._callbacks + + def make(self, makefile, targetstack, cb, avoidremakeloop=False, printerror=True): + """ + If we are out of date, asynchronously make ourself. This is a multi-stage process, mostly handled + by the helper objects RemakeTargetSerially, RemakeTargetParallel, + RemakeRuleContext. These helper objects should keep us from developing + any cyclical dependencies. + + * resolve dependencies (synchronous) + * gather a list of rules to execute and related dependencies (synchronous) + * for each rule (in parallel) + ** remake dependencies (asynchronous) + ** build list of commands to execute (synchronous) + ** execute each command (asynchronous) + * asynchronously notify when all rules are complete + + @param cb A callback function to notify when remaking is finished. It is called + thusly: callback(error=True/False, didanything=True/False) + If there is no asynchronous activity to perform, the callback may be called directly. + """ + + serial = makefile.context.jcount == 1 + + if self._state == MAKESTATE_FINISHED: + cb(error=self.error, didanything=self.didanything) + return + + if self._state == MAKESTATE_WORKING: + assert not serial + self._callbacks.append(cb) + return + + assert self._state == MAKESTATE_NONE + + self._state = MAKESTATE_WORKING + self._callbacks = [cb] + self.error = False + self.didanything = False + + indent = getindent(targetstack) + + try: + self.resolvedeps(makefile, targetstack, [], False) + except util.MakeError, e: + if printerror: + print e + self.error = True + self.notifydone(makefile) + return + + assert self.vpathtarget is not None, "Target was never resolved!" + if not len(self.rules): + self.notifydone(makefile) + return + + if self.isdoublecolon(): + rulelist = [RemakeRuleContext(self, makefile, r, [(makefile.gettarget(p), False) for p in r.prerequisites], targetstack, avoidremakeloop) for r in self.rules] + else: + alldeps = [] + + commandrule = None + for r in self.rules: + rdeps = [(makefile.gettarget(p), r.weakdeps) for p in r.prerequisites] + if len(r.commands): + assert commandrule is None + commandrule = r + # The dependencies of the command rule are resolved before other dependencies, + # no matter the ordering of the other no-command rules + alldeps[0:0] = rdeps + else: + alldeps.extend(rdeps) + + rulelist = [RemakeRuleContext(self, makefile, commandrule, alldeps, targetstack, avoidremakeloop)] + + targetstack = targetstack + [self.target] + + if serial: + RemakeTargetSerially(self, makefile, indent, rulelist) + else: + RemakeTargetParallel(self, makefile, indent, rulelist) + +def dirpart(p): + d, s, f = util.strrpartition(p, '/') + if d == '': + return '.' + + return d + +def filepart(p): + d, s, f = util.strrpartition(p, '/') + return f + +def setautomatic(v, name, plist): + v.set(name, Variables.FLAVOR_SIMPLE, Variables.SOURCE_AUTOMATIC, ' '.join(plist)) + v.set(name + 'D', Variables.FLAVOR_SIMPLE, Variables.SOURCE_AUTOMATIC, ' '.join((dirpart(p) for p in plist))) + v.set(name + 'F', Variables.FLAVOR_SIMPLE, Variables.SOURCE_AUTOMATIC, ' '.join((filepart(p) for p in plist))) + +def setautomaticvariables(v, makefile, target, prerequisites): + prtargets = [makefile.gettarget(p) for p in prerequisites] + prall = [pt.vpathtarget for pt in prtargets] + proutofdate = [pt.vpathtarget for pt in withoutdups(prtargets) + if target.mtime is None or mtimeislater(pt.mtime, target.mtime)] + + setautomatic(v, '@', [target.vpathtarget]) + if len(prall): + setautomatic(v, '<', [prall[0]]) + + setautomatic(v, '?', proutofdate) + setautomatic(v, '^', list(withoutdups(prall))) + setautomatic(v, '+', prall) + +def splitcommand(command): + """ + Using the esoteric rules, split command lines by unescaped newlines. + """ + start = 0 + i = 0 + while i < len(command): + c = command[i] + if c == '\\': + i += 1 + elif c == '\n': + yield command[start:i] + i += 1 + start = i + continue + + i += 1 + + if i > start: + yield command[start:i] + +def findmodifiers(command): + """ + Find any of +-@% prefixed on the command. + @returns (command, isHidden, isRecursive, ignoreErrors, isNative) + """ + + isHidden = False + isRecursive = False + ignoreErrors = False + isNative = False + + realcommand = command.lstrip(' \t\n@+-%') + modset = set(command[:-len(realcommand)]) + return realcommand, '@' in modset, '+' in modset, '-' in modset, '%' in modset + +class _CommandWrapper(object): + def __init__(self, cline, ignoreErrors, loc, context, **kwargs): + self.ignoreErrors = ignoreErrors + self.loc = loc + self.cline = cline + self.kwargs = kwargs + self.context = context + + def _cb(self, res): + if res != 0 and not self.ignoreErrors: + print "%s: command '%s' failed, return code %i" % (self.loc, self.cline, res) + self.usercb(error=True) + else: + self.usercb(error=False) + + def __call__(self, cb): + self.usercb = cb + process.call(self.cline, loc=self.loc, cb=self._cb, context=self.context, **self.kwargs) + +class _NativeWrapper(_CommandWrapper): + def __init__(self, cline, ignoreErrors, loc, context, + pycommandpath, **kwargs): + _CommandWrapper.__init__(self, cline, ignoreErrors, loc, context, + **kwargs) + if pycommandpath: + self.pycommandpath = re.split('[%s\s]+' % os.pathsep, + pycommandpath) + else: + self.pycommandpath = None + + def __call__(self, cb): + # get the module and method to call + parts, badchar = process.clinetoargv(self.cline, self.kwargs['cwd']) + if parts is None: + raise DataError("native command '%s': shell metacharacter '%s' in command line" % (self.cline, badchar), self.loc) + if len(parts) < 2: + raise DataError("native command '%s': no method name specified" % self.cline, self.loc) + module = parts[0] + method = parts[1] + cline_list = parts[2:] + self.usercb = cb + process.call_native(module, method, cline_list, + loc=self.loc, cb=self._cb, context=self.context, + pycommandpath=self.pycommandpath, **self.kwargs) + +def getcommandsforrule(rule, target, makefile, prerequisites, stem): + v = Variables(parent=target.variables) + setautomaticvariables(v, makefile, target, prerequisites) + if stem is not None: + setautomatic(v, '*', [stem]) + + env = makefile.getsubenvironment(v) + + for c in rule.commands: + cstring = c.resolvestr(makefile, v) + for cline in splitcommand(cstring): + cline, isHidden, isRecursive, ignoreErrors, isNative = findmodifiers(cline) + if (isHidden or makefile.silent) and not makefile.justprint: + echo = None + else: + echo = "%s$ %s" % (c.loc, cline) + if not isNative: + yield _CommandWrapper(cline, ignoreErrors=ignoreErrors, env=env, cwd=makefile.workdir, loc=c.loc, context=makefile.context, + echo=echo, justprint=makefile.justprint) + else: + f, s, e = v.get("PYCOMMANDPATH", True) + if e: + e = e.resolvestr(makefile, v, ["PYCOMMANDPATH"]) + yield _NativeWrapper(cline, ignoreErrors=ignoreErrors, + env=env, cwd=makefile.workdir, + loc=c.loc, context=makefile.context, + echo=echo, justprint=makefile.justprint, + pycommandpath=e) + +class Rule(object): + """ + A rule contains a list of prerequisites and a list of commands. It may also + contain rule-specific variables. This rule may be associated with multiple targets. + """ + + def __init__(self, prereqs, doublecolon, loc, weakdeps): + self.prerequisites = prereqs + self.doublecolon = doublecolon + self.commands = [] + self.loc = loc + self.weakdeps = weakdeps + + def addcommand(self, c): + assert isinstance(c, (Expansion, StringExpansion)) + self.commands.append(c) + + def getcommands(self, target, makefile): + assert isinstance(target, Target) + + return getcommandsforrule(self, target, makefile, self.prerequisites, stem=None) + # TODO: $* in non-pattern rules? + +class PatternRuleInstance(object): + weakdeps = False + + """ + A pattern rule instantiated for a particular target. It has the same API as Rule, but + different internals, forwarding most information on to the PatternRule. + """ + def __init__(self, prule, dir, stem, ismatchany): + assert isinstance(prule, PatternRule) + + self.dir = dir + self.stem = stem + self.prule = prule + self.prerequisites = prule.prerequisitesforstem(dir, stem) + self.doublecolon = prule.doublecolon + self.loc = prule.loc + self.ismatchany = ismatchany + self.commands = prule.commands + + def getcommands(self, target, makefile): + assert isinstance(target, Target) + return getcommandsforrule(self, target, makefile, self.prerequisites, stem=self.dir + self.stem) + + def __str__(self): + return "Pattern rule at %s with stem '%s', matchany: %s doublecolon: %s" % (self.loc, + self.dir + self.stem, + self.ismatchany, + self.doublecolon) + +class PatternRule(object): + """ + An implicit rule or static pattern rule containing target patterns, prerequisite patterns, + and a list of commands. + """ + + def __init__(self, targetpatterns, prerequisites, doublecolon, loc): + self.targetpatterns = targetpatterns + self.prerequisites = prerequisites + self.doublecolon = doublecolon + self.loc = loc + self.commands = [] + + def addcommand(self, c): + assert isinstance(c, (Expansion, StringExpansion)) + self.commands.append(c) + + def ismatchany(self): + return util.any((t.ismatchany() for t in self.targetpatterns)) + + def hasspecificmatch(self, file): + for p in self.targetpatterns: + if not p.ismatchany() and p.match(file) is not None: + return True + + return False + + def matchesfor(self, dir, file, skipsinglecolonmatchany): + """ + Determine all the target patterns of this rule that might match target t. + @yields a PatternRuleInstance for each. + """ + + for p in self.targetpatterns: + matchany = p.ismatchany() + if matchany: + if skipsinglecolonmatchany and not self.doublecolon: + continue + + yield PatternRuleInstance(self, dir, file, True) + else: + stem = p.match(dir + file) + if stem is not None: + yield PatternRuleInstance(self, '', stem, False) + else: + stem = p.match(file) + if stem is not None: + yield PatternRuleInstance(self, dir, stem, False) + + def prerequisitesforstem(self, dir, stem): + return [p.resolve(dir, stem) for p in self.prerequisites] + +class _RemakeContext(object): + def __init__(self, makefile, cb): + self.makefile = makefile + self.included = [(makefile.gettarget(f), required) + for f, required in makefile.included] + self.toremake = list(self.included) + self.cb = cb + + self.remakecb(error=False, didanything=False) + + def remakecb(self, error, didanything): + assert error in (True, False) + + if error and self.required: + print "Error remaking makefiles (ignored)" + + if len(self.toremake): + target, self.required = self.toremake.pop(0) + target.make(self.makefile, [], avoidremakeloop=True, cb=self.remakecb, printerror=False) + else: + for t, required in self.included: + if t.wasremade: + _log.info("Included file %s was remade, restarting make", t.target) + self.cb(remade=True) + return + elif required and t.mtime is None: + self.cb(remade=False, error=DataError("No rule to remake missing include file %s" % t.target)) + return + + self.cb(remade=False) + +class Makefile(object): + """ + The top-level data structure for makefile execution. It holds Targets, implicit rules, and other + state data. + """ + + def __init__(self, workdir=None, env=None, restarts=0, make=None, + makeflags='', makeoverrides='', + makelevel=0, context=None, targets=(), keepgoing=False, + silent=False, justprint=False): + self.defaulttarget = None + + if env is None: + env = os.environ + self.env = env + + self.variables = Variables() + self.variables.readfromenvironment(env) + + self.context = context + self.exportedvars = {} + self._targets = {} + self.keepgoing = keepgoing + self.silent = silent + self.justprint = justprint + self._patternvariables = [] # of (pattern, variables) + self.implicitrules = [] + self.parsingfinished = False + + self._patternvpaths = [] # of (pattern, [dir, ...]) + + if workdir is None: + workdir = os.getcwd() + workdir = os.path.realpath(workdir) + self.workdir = workdir + self.variables.set('CURDIR', Variables.FLAVOR_SIMPLE, + Variables.SOURCE_AUTOMATIC, workdir.replace('\\','/')) + + # the list of included makefiles, whether or not they existed + self.included = [] + + self.variables.set('MAKE_RESTARTS', Variables.FLAVOR_SIMPLE, + Variables.SOURCE_AUTOMATIC, restarts > 0 and str(restarts) or '') + + self.variables.set('.PYMAKE', Variables.FLAVOR_SIMPLE, + Variables.SOURCE_MAKEFILE, "1") + if make is not None: + self.variables.set('MAKE', Variables.FLAVOR_SIMPLE, + Variables.SOURCE_MAKEFILE, make) + + if makeoverrides != '': + self.variables.set('-*-command-variables-*-', Variables.FLAVOR_SIMPLE, + Variables.SOURCE_AUTOMATIC, makeoverrides) + makeflags += ' -- $(MAKEOVERRIDES)' + + self.variables.set('MAKEOVERRIDES', Variables.FLAVOR_RECURSIVE, + Variables.SOURCE_ENVIRONMENT, + '${-*-command-variables-*-}') + + self.variables.set('MAKEFLAGS', Variables.FLAVOR_RECURSIVE, + Variables.SOURCE_MAKEFILE, makeflags) + self.exportedvars['MAKEFLAGS'] = True + + self.makelevel = makelevel + self.variables.set('MAKELEVEL', Variables.FLAVOR_SIMPLE, + Variables.SOURCE_MAKEFILE, str(makelevel)) + + self.variables.set('MAKECMDGOALS', Variables.FLAVOR_SIMPLE, + Variables.SOURCE_AUTOMATIC, ' '.join(targets)) + + for vname, val in implicit.variables.iteritems(): + self.variables.set(vname, + Variables.FLAVOR_SIMPLE, + Variables.SOURCE_IMPLICIT, val) + + def foundtarget(self, t): + """ + Inform the makefile of a target which is a candidate for being the default target, + if there isn't already a default target. + """ + flavor, source, value = self.variables.get('.DEFAULT_GOAL') + if self.defaulttarget is None and t != '.PHONY' and value is None: + self.defaulttarget = t + self.variables.set('.DEFAULT_GOAL', Variables.FLAVOR_SIMPLE, + Variables.SOURCE_AUTOMATIC, t) + + def getpatternvariables(self, pattern): + assert isinstance(pattern, Pattern) + + for p, v in self._patternvariables: + if p == pattern: + return v + + v = Variables() + self._patternvariables.append( (pattern, v) ) + return v + + def getpatternvariablesfor(self, target): + for p, v in self._patternvariables: + if p.match(target): + yield v + + def hastarget(self, target): + return target in self._targets + + _globcheck = re.compile('[[*?]') + def gettarget(self, target): + assert isinstance(target, str_type) + + target = target.rstrip('/') + + assert target != '', "empty target?" + + assert not self._globcheck.match(target) + + t = self._targets.get(target, None) + if t is None: + t = Target(target, self) + self._targets[target] = t + return t + + def appendimplicitrule(self, rule): + assert isinstance(rule, PatternRule) + self.implicitrules.append(rule) + + def finishparsing(self): + """ + Various activities, such as "eval", are not allowed after parsing is + finished. In addition, various warnings and errors can only be issued + after the parsing data model is complete. All dependency resolution + and rule execution requires that parsing be finished. + """ + self.parsingfinished = True + + flavor, source, value = self.variables.get('GPATH') + if value is not None and value.resolvestr(self, self.variables, ['GPATH']).strip() != '': + raise DataError('GPATH was set: pymake does not support GPATH semantics') + + flavor, source, value = self.variables.get('VPATH') + if value is None: + self._vpath = [] + else: + self._vpath = filter(lambda e: e != '', + re.split('[%s\s]+' % os.pathsep, + value.resolvestr(self, self.variables, ['VPATH']))) + + targets = list(self._targets.itervalues()) + for t in targets: + t.explicit = True + for r in t.rules: + for p in r.prerequisites: + self.gettarget(p).explicit = True + + np = self.gettarget('.NOTPARALLEL') + if len(np.rules): + self.context = process.getcontext(1) + + flavor, source, value = self.variables.get('.DEFAULT_GOAL') + if value is not None: + self.defaulttarget = value.resolvestr(self, self.variables, ['.DEFAULT_GOAL']).strip() + + self.error = False + + def include(self, path, required=True, weak=False, loc=None): + """ + Include the makefile at `path`. + """ + self.included.append((path, required)) + fspath = util.normaljoin(self.workdir, path) + if os.path.exists(fspath): + if weak: + stmts = parser.parsedepfile(fspath) + else: + stmts = parser.parsefile(fspath) + self.variables.append('MAKEFILE_LIST', Variables.SOURCE_AUTOMATIC, path, None, self) + stmts.execute(self, weak=weak) + self.gettarget(path).explicit = True + + def addvpath(self, pattern, dirs): + """ + Add a directory to the vpath search for the given pattern. + """ + self._patternvpaths.append((pattern, dirs)) + + def clearvpath(self, pattern): + """ + Clear vpaths for the given pattern. + """ + self._patternvpaths = [(p, dirs) + for p, dirs in self._patternvpaths + if not p.match(pattern)] + + def clearallvpaths(self): + self._patternvpaths = [] + + def getvpath(self, target): + vp = list(self._vpath) + for p, dirs in self._patternvpaths: + if p.match(target): + vp.extend(dirs) + + return withoutdups(vp) + + def remakemakefiles(self, cb): + mlist = [] + for f, required in self.included: + t = self.gettarget(f) + t.explicit = True + t.resolvevpath(self) + oldmtime = t.mtime + + mlist.append((t, oldmtime)) + + _RemakeContext(self, cb) + + def getsubenvironment(self, variables): + env = dict(self.env) + for vname, v in self.exportedvars.iteritems(): + if v: + flavor, source, val = variables.get(vname) + if val is None: + strval = '' + else: + strval = val.resolvestr(self, variables, [vname]) + env[vname] = strval + else: + env.pop(vname, None) + + makeflags = '' + + env['MAKELEVEL'] = str(self.makelevel + 1) + return env diff --git a/build/pymake/pymake/functions.py b/build/pymake/pymake/functions.py new file mode 100644 index 000000000..85dbafb96 --- /dev/null +++ b/build/pymake/pymake/functions.py @@ -0,0 +1,869 @@ +""" +Makefile functions. +""" + +import parser, util +import subprocess, os, logging, sys +from globrelative import glob +from cStringIO import StringIO + +log = logging.getLogger('pymake.data') + +def emit_expansions(descend, *expansions): + """Helper function to emit all expansions within an input set.""" + for expansion in expansions: + yield expansion + + if not descend or not isinstance(expansion, list): + continue + + for e, is_func in expansion: + if is_func: + for exp in e.expansions(True): + yield exp + else: + yield e + +class Function(object): + """ + An object that represents a function call. This class is always subclassed + with the following methods and attributes: + + minargs = minimum # of arguments + maxargs = maximum # of arguments (0 means unlimited) + + def resolve(self, makefile, variables, fd, setting) + Calls the function + calls fd.write() with strings + """ + + __slots__ = ('_arguments', 'loc') + + def __init__(self, loc): + self._arguments = [] + self.loc = loc + assert self.minargs > 0 + + def __getitem__(self, key): + return self._arguments[key] + + def setup(self): + argc = len(self._arguments) + + if argc < self.minargs: + raise data.DataError("Not enough arguments to function %s, requires %s" % (self.name, self.minargs), self.loc) + + assert self.maxargs == 0 or argc <= self.maxargs, "Parser screwed up, gave us too many args" + + def append(self, arg): + assert isinstance(arg, (data.Expansion, data.StringExpansion)) + self._arguments.append(arg) + + def to_source(self): + """Convert the function back to make file "source" code.""" + if not hasattr(self, 'name'): + raise Exception("%s must implement to_source()." % self.__class__) + + # The default implementation simply prints the function name and all + # the arguments joined by a comma. + # According to the GNU make manual Section 8.1, whitespace around + # arguments is *not* part of the argument's value. So, we trim excess + # white space so we have consistent behavior. + args = [] + curly = False + for i, arg in enumerate(self._arguments): + arg = arg.to_source() + + if i == 0: + arg = arg.lstrip() + + # Are balanced parens even OK? + if arg.count('(') != arg.count(')'): + curly = True + + args.append(arg) + + if curly: + return '${%s %s}' % (self.name, ','.join(args)) + + return '$(%s %s)' % (self.name, ','.join(args)) + + def expansions(self, descend=False): + """Obtain all expansions contained within this function. + + By default, only expansions directly part of this function are + returned. If descend is True, we will descend into child expansions and + return all of the composite parts. + + This is a generator for pymake.data.BaseExpansion instances. + """ + # Our default implementation simply returns arguments. More advanced + # functions like variable references may need their own implementation. + return emit_expansions(descend, *self._arguments) + + @property + def is_filesystem_dependent(self): + """Exposes whether this function depends on the filesystem for results. + + If True, the function touches the filesystem as part of evaluation. + + This only tests whether the function itself uses the filesystem. If + this function has arguments that are functions that touch the + filesystem, this will return False. + """ + return False + + def __len__(self): + return len(self._arguments) + + def __repr__(self): + return "%s<%s>(%r)" % ( + self.__class__.__name__, self.loc, + ','.join([repr(a) for a in self._arguments]), + ) + + def __eq__(self, other): + if not hasattr(self, 'name'): + raise Exception("%s must implement __eq__." % self.__class__) + + if type(self) != type(other): + return False + + if self.name != other.name: + return False + + if len(self._arguments) != len(other._arguments): + return False + + for i in xrange(len(self._arguments)): + # According to the GNU make manual Section 8.1, whitespace around + # arguments is *not* part of the argument's value. So, we do a + # whitespace-agnostic comparison. + if i == 0: + a = self._arguments[i] + a.lstrip() + + b = other._arguments[i] + b.lstrip() + + if a != b: + return False + + continue + + if self._arguments[i] != other._arguments[i]: + return False + + return True + + def __ne__(self, other): + return not self.__eq__(other) + +class VariableRef(Function): + AUTOMATIC_VARIABLES = set(['@', '%', '<', '?', '^', '+', '|', '*']) + + __slots__ = ('vname', 'loc') + + def __init__(self, loc, vname): + self.loc = loc + assert isinstance(vname, (data.Expansion, data.StringExpansion)) + self.vname = vname + + def setup(self): + assert False, "Shouldn't get here" + + def resolve(self, makefile, variables, fd, setting): + vname = self.vname.resolvestr(makefile, variables, setting) + if vname in setting: + raise data.DataError("Setting variable '%s' recursively references itself." % (vname,), self.loc) + + flavor, source, value = variables.get(vname) + if value is None: + log.debug("%s: variable '%s' was not set" % (self.loc, vname)) + return + + value.resolve(makefile, variables, fd, setting + [vname]) + + def to_source(self): + if isinstance(self.vname, data.StringExpansion): + if self.vname.s in self.AUTOMATIC_VARIABLES: + return '$%s' % self.vname.s + + return '$(%s)' % self.vname.s + + return '$(%s)' % self.vname.to_source() + + def expansions(self, descend=False): + return emit_expansions(descend, self.vname) + + def __repr__(self): + return "VariableRef<%s>(%r)" % (self.loc, self.vname) + + def __eq__(self, other): + if not isinstance(other, VariableRef): + return False + + return self.vname == other.vname + +class SubstitutionRef(Function): + """$(VARNAME:.c=.o) and $(VARNAME:%.c=%.o)""" + + __slots__ = ('loc', 'vname', 'substfrom', 'substto') + + def __init__(self, loc, varname, substfrom, substto): + self.loc = loc + self.vname = varname + self.substfrom = substfrom + self.substto = substto + + def setup(self): + assert False, "Shouldn't get here" + + def resolve(self, makefile, variables, fd, setting): + vname = self.vname.resolvestr(makefile, variables, setting) + if vname in setting: + raise data.DataError("Setting variable '%s' recursively references itself." % (vname,), self.loc) + + substfrom = self.substfrom.resolvestr(makefile, variables, setting) + substto = self.substto.resolvestr(makefile, variables, setting) + + flavor, source, value = variables.get(vname) + if value is None: + log.debug("%s: variable '%s' was not set" % (self.loc, vname)) + return + + f = data.Pattern(substfrom) + if not f.ispattern(): + f = data.Pattern('%' + substfrom) + substto = '%' + substto + + fd.write(' '.join([f.subst(substto, word, False) + for word in value.resolvesplit(makefile, variables, setting + [vname])])) + + def to_source(self): + return '$(%s:%s=%s)' % ( + self.vname.to_source(), + self.substfrom.to_source(), + self.substto.to_source()) + + def expansions(self, descend=False): + return emit_expansions(descend, self.vname, self.substfrom, + self.substto) + + def __repr__(self): + return "SubstitutionRef<%s>(%r:%r=%r)" % ( + self.loc, self.vname, self.substfrom, self.substto,) + + def __eq__(self, other): + if not isinstance(other, SubstitutionRef): + return False + + return self.vname == other.vname and self.substfrom == other.substfrom \ + and self.substto == other.substto + +class SubstFunction(Function): + name = 'subst' + minargs = 3 + maxargs = 3 + + __slots__ = Function.__slots__ + + def resolve(self, makefile, variables, fd, setting): + s = self._arguments[0].resolvestr(makefile, variables, setting) + r = self._arguments[1].resolvestr(makefile, variables, setting) + d = self._arguments[2].resolvestr(makefile, variables, setting) + fd.write(d.replace(s, r)) + +class PatSubstFunction(Function): + name = 'patsubst' + minargs = 3 + maxargs = 3 + + __slots__ = Function.__slots__ + + def resolve(self, makefile, variables, fd, setting): + s = self._arguments[0].resolvestr(makefile, variables, setting) + r = self._arguments[1].resolvestr(makefile, variables, setting) + + p = data.Pattern(s) + fd.write(' '.join([p.subst(r, word, False) + for word in self._arguments[2].resolvesplit(makefile, variables, setting)])) + +class StripFunction(Function): + name = 'strip' + minargs = 1 + maxargs = 1 + + __slots__ = Function.__slots__ + + def resolve(self, makefile, variables, fd, setting): + util.joiniter(fd, self._arguments[0].resolvesplit(makefile, variables, setting)) + +class FindstringFunction(Function): + name = 'findstring' + minargs = 2 + maxargs = 2 + + __slots__ = Function.__slots__ + + def resolve(self, makefile, variables, fd, setting): + s = self._arguments[0].resolvestr(makefile, variables, setting) + r = self._arguments[1].resolvestr(makefile, variables, setting) + if r.find(s) == -1: + return + fd.write(s) + +class FilterFunction(Function): + name = 'filter' + minargs = 2 + maxargs = 2 + + __slots__ = Function.__slots__ + + def resolve(self, makefile, variables, fd, setting): + plist = [data.Pattern(p) + for p in self._arguments[0].resolvesplit(makefile, variables, setting)] + + fd.write(' '.join([w for w in self._arguments[1].resolvesplit(makefile, variables, setting) + if util.any((p.match(w) for p in plist))])) + +class FilteroutFunction(Function): + name = 'filter-out' + minargs = 2 + maxargs = 2 + + __slots__ = Function.__slots__ + + def resolve(self, makefile, variables, fd, setting): + plist = [data.Pattern(p) + for p in self._arguments[0].resolvesplit(makefile, variables, setting)] + + fd.write(' '.join([w for w in self._arguments[1].resolvesplit(makefile, variables, setting) + if not util.any((p.match(w) for p in plist))])) + +class SortFunction(Function): + name = 'sort' + minargs = 1 + maxargs = 1 + + __slots__ = Function.__slots__ + + def resolve(self, makefile, variables, fd, setting): + d = set(self._arguments[0].resolvesplit(makefile, variables, setting)) + util.joiniter(fd, sorted(d)) + +class WordFunction(Function): + name = 'word' + minargs = 2 + maxargs = 2 + + __slots__ = Function.__slots__ + + def resolve(self, makefile, variables, fd, setting): + n = self._arguments[0].resolvestr(makefile, variables, setting) + # TODO: provide better error if this doesn't convert + n = int(n) + words = list(self._arguments[1].resolvesplit(makefile, variables, setting)) + if n < 1 or n > len(words): + return + fd.write(words[n - 1]) + +class WordlistFunction(Function): + name = 'wordlist' + minargs = 3 + maxargs = 3 + + __slots__ = Function.__slots__ + + def resolve(self, makefile, variables, fd, setting): + nfrom = self._arguments[0].resolvestr(makefile, variables, setting) + nto = self._arguments[1].resolvestr(makefile, variables, setting) + # TODO: provide better errors if this doesn't convert + nfrom = int(nfrom) + nto = int(nto) + + words = list(self._arguments[2].resolvesplit(makefile, variables, setting)) + + if nfrom < 1: + nfrom = 1 + if nto < 1: + nto = 1 + + util.joiniter(fd, words[nfrom - 1:nto]) + +class WordsFunction(Function): + name = 'words' + minargs = 1 + maxargs = 1 + + __slots__ = Function.__slots__ + + def resolve(self, makefile, variables, fd, setting): + fd.write(str(len(self._arguments[0].resolvesplit(makefile, variables, setting)))) + +class FirstWordFunction(Function): + name = 'firstword' + minargs = 1 + maxargs = 1 + + __slots__ = Function.__slots__ + + def resolve(self, makefile, variables, fd, setting): + l = self._arguments[0].resolvesplit(makefile, variables, setting) + if len(l): + fd.write(l[0]) + +class LastWordFunction(Function): + name = 'lastword' + minargs = 1 + maxargs = 1 + + __slots__ = Function.__slots__ + + def resolve(self, makefile, variables, fd, setting): + l = self._arguments[0].resolvesplit(makefile, variables, setting) + if len(l): + fd.write(l[-1]) + +def pathsplit(path, default='./'): + """ + Splits a path into dirpart, filepart on the last slash. If there is no slash, dirpart + is ./ + """ + dir, slash, file = util.strrpartition(path, '/') + if dir == '': + return default, file + + return dir + slash, file + +class DirFunction(Function): + name = 'dir' + minargs = 1 + maxargs = 1 + + def resolve(self, makefile, variables, fd, setting): + fd.write(' '.join([pathsplit(path)[0] + for path in self._arguments[0].resolvesplit(makefile, variables, setting)])) + +class NotDirFunction(Function): + name = 'notdir' + minargs = 1 + maxargs = 1 + + __slots__ = Function.__slots__ + + def resolve(self, makefile, variables, fd, setting): + fd.write(' '.join([pathsplit(path)[1] + for path in self._arguments[0].resolvesplit(makefile, variables, setting)])) + +class SuffixFunction(Function): + name = 'suffix' + minargs = 1 + maxargs = 1 + + __slots__ = Function.__slots__ + + @staticmethod + def suffixes(words): + for w in words: + dir, file = pathsplit(w) + base, dot, suffix = util.strrpartition(file, '.') + if base != '': + yield dot + suffix + + def resolve(self, makefile, variables, fd, setting): + util.joiniter(fd, self.suffixes(self._arguments[0].resolvesplit(makefile, variables, setting))) + +class BasenameFunction(Function): + name = 'basename' + minargs = 1 + maxargs = 1 + + __slots__ = Function.__slots__ + + @staticmethod + def basenames(words): + for w in words: + dir, file = pathsplit(w, '') + base, dot, suffix = util.strrpartition(file, '.') + if dot == '': + base = suffix + + yield dir + base + + def resolve(self, makefile, variables, fd, setting): + util.joiniter(fd, self.basenames(self._arguments[0].resolvesplit(makefile, variables, setting))) + +class AddSuffixFunction(Function): + name = 'addsuffix' + minargs = 2 + maxargs = 2 + + __slots__ = Function.__slots__ + + def resolve(self, makefile, variables, fd, setting): + suffix = self._arguments[0].resolvestr(makefile, variables, setting) + + fd.write(' '.join([w + suffix for w in self._arguments[1].resolvesplit(makefile, variables, setting)])) + +class AddPrefixFunction(Function): + name = 'addprefix' + minargs = 2 + maxargs = 2 + + def resolve(self, makefile, variables, fd, setting): + prefix = self._arguments[0].resolvestr(makefile, variables, setting) + + fd.write(' '.join([prefix + w for w in self._arguments[1].resolvesplit(makefile, variables, setting)])) + +class JoinFunction(Function): + name = 'join' + minargs = 2 + maxargs = 2 + + __slots__ = Function.__slots__ + + @staticmethod + def iterjoin(l1, l2): + for i in xrange(0, max(len(l1), len(l2))): + i1 = i < len(l1) and l1[i] or '' + i2 = i < len(l2) and l2[i] or '' + yield i1 + i2 + + def resolve(self, makefile, variables, fd, setting): + list1 = list(self._arguments[0].resolvesplit(makefile, variables, setting)) + list2 = list(self._arguments[1].resolvesplit(makefile, variables, setting)) + + util.joiniter(fd, self.iterjoin(list1, list2)) + +class WildcardFunction(Function): + name = 'wildcard' + minargs = 1 + maxargs = 1 + + __slots__ = Function.__slots__ + + def resolve(self, makefile, variables, fd, setting): + patterns = self._arguments[0].resolvesplit(makefile, variables, setting) + + fd.write(' '.join([x.replace('\\','/') + for p in patterns + for x in glob(makefile.workdir, p)])) + + @property + def is_filesystem_dependent(self): + return True + +class RealpathFunction(Function): + name = 'realpath' + minargs = 1 + maxargs = 1 + + def resolve(self, makefile, variables, fd, setting): + fd.write(' '.join([os.path.realpath(os.path.join(makefile.workdir, path)).replace('\\', '/') + for path in self._arguments[0].resolvesplit(makefile, variables, setting)])) + + def is_filesystem_dependent(self): + return True + +class AbspathFunction(Function): + name = 'abspath' + minargs = 1 + maxargs = 1 + + __slots__ = Function.__slots__ + + def resolve(self, makefile, variables, fd, setting): + assert os.path.isabs(makefile.workdir) + fd.write(' '.join([util.normaljoin(makefile.workdir, path).replace('\\', '/') + for path in self._arguments[0].resolvesplit(makefile, variables, setting)])) + +class IfFunction(Function): + name = 'if' + minargs = 1 + maxargs = 3 + + __slots__ = Function.__slots__ + + def setup(self): + Function.setup(self) + self._arguments[0].lstrip() + self._arguments[0].rstrip() + + def resolve(self, makefile, variables, fd, setting): + condition = self._arguments[0].resolvestr(makefile, variables, setting) + + if len(condition): + self._arguments[1].resolve(makefile, variables, fd, setting) + elif len(self._arguments) > 2: + return self._arguments[2].resolve(makefile, variables, fd, setting) + +class OrFunction(Function): + name = 'or' + minargs = 1 + maxargs = 0 + + __slots__ = Function.__slots__ + + def resolve(self, makefile, variables, fd, setting): + for arg in self._arguments: + r = arg.resolvestr(makefile, variables, setting) + if r != '': + fd.write(r) + return + +class AndFunction(Function): + name = 'and' + minargs = 1 + maxargs = 0 + + __slots__ = Function.__slots__ + + def resolve(self, makefile, variables, fd, setting): + r = '' + + for arg in self._arguments: + r = arg.resolvestr(makefile, variables, setting) + if r == '': + return + + fd.write(r) + +class ForEachFunction(Function): + name = 'foreach' + minargs = 3 + maxargs = 3 + + __slots__ = Function.__slots__ + + def resolve(self, makefile, variables, fd, setting): + vname = self._arguments[0].resolvestr(makefile, variables, setting) + e = self._arguments[2] + + v = data.Variables(parent=variables) + firstword = True + + for w in self._arguments[1].resolvesplit(makefile, variables, setting): + if firstword: + firstword = False + else: + fd.write(' ') + + v.set(vname, data.Variables.FLAVOR_SIMPLE, data.Variables.SOURCE_AUTOMATIC, w) + e.resolve(makefile, v, fd, setting) + +class CallFunction(Function): + name = 'call' + minargs = 1 + maxargs = 0 + + __slots__ = Function.__slots__ + + def resolve(self, makefile, variables, fd, setting): + vname = self._arguments[0].resolvestr(makefile, variables, setting) + if vname in setting: + raise data.DataError("Recursively setting variable '%s'" % (vname,)) + + v = data.Variables(parent=variables) + v.set('0', data.Variables.FLAVOR_SIMPLE, data.Variables.SOURCE_AUTOMATIC, vname) + for i in xrange(1, len(self._arguments)): + param = self._arguments[i].resolvestr(makefile, variables, setting) + v.set(str(i), data.Variables.FLAVOR_SIMPLE, data.Variables.SOURCE_AUTOMATIC, param) + + flavor, source, e = variables.get(vname) + + if e is None: + return + + if flavor == data.Variables.FLAVOR_SIMPLE: + log.warning("%s: calling variable '%s' which is simply-expanded" % (self.loc, vname)) + + # but we'll do it anyway + e.resolve(makefile, v, fd, setting + [vname]) + +class ValueFunction(Function): + name = 'value' + minargs = 1 + maxargs = 1 + + __slots__ = Function.__slots__ + + def resolve(self, makefile, variables, fd, setting): + varname = self._arguments[0].resolvestr(makefile, variables, setting) + + flavor, source, value = variables.get(varname, expand=False) + if value is not None: + fd.write(value) + +class EvalFunction(Function): + name = 'eval' + minargs = 1 + maxargs = 1 + + def resolve(self, makefile, variables, fd, setting): + if makefile.parsingfinished: + # GNU make allows variables to be set by recursive expansion during + # command execution. This seems really dumb to me, so I don't! + raise data.DataError("$(eval) not allowed via recursive expansion after parsing is finished", self.loc) + + stmts = parser.parsestring(self._arguments[0].resolvestr(makefile, variables, setting), + 'evaluation from %s' % self.loc) + stmts.execute(makefile) + +class OriginFunction(Function): + name = 'origin' + minargs = 1 + maxargs = 1 + + __slots__ = Function.__slots__ + + def resolve(self, makefile, variables, fd, setting): + vname = self._arguments[0].resolvestr(makefile, variables, setting) + + flavor, source, value = variables.get(vname) + if source is None: + r = 'undefined' + elif source == data.Variables.SOURCE_OVERRIDE: + r = 'override' + + elif source == data.Variables.SOURCE_MAKEFILE: + r = 'file' + elif source == data.Variables.SOURCE_ENVIRONMENT: + r = 'environment' + elif source == data.Variables.SOURCE_COMMANDLINE: + r = 'command line' + elif source == data.Variables.SOURCE_AUTOMATIC: + r = 'automatic' + elif source == data.Variables.SOURCE_IMPLICIT: + r = 'default' + + fd.write(r) + +class FlavorFunction(Function): + name = 'flavor' + minargs = 1 + maxargs = 1 + + __slots__ = Function.__slots__ + + def resolve(self, makefile, variables, fd, setting): + varname = self._arguments[0].resolvestr(makefile, variables, setting) + + flavor, source, value = variables.get(varname) + if flavor is None: + r = 'undefined' + elif flavor == data.Variables.FLAVOR_RECURSIVE: + r = 'recursive' + elif flavor == data.Variables.FLAVOR_SIMPLE: + r = 'simple' + fd.write(r) + +class ShellFunction(Function): + name = 'shell' + minargs = 1 + maxargs = 1 + + __slots__ = Function.__slots__ + + def resolve(self, makefile, variables, fd, setting): + from process import prepare_command + cline = self._arguments[0].resolvestr(makefile, variables, setting) + executable, cline = prepare_command(cline, makefile.workdir, self.loc) + + # subprocess.Popen doesn't use the PATH set in the env argument for + # finding the executable on some platforms (but strangely it does on + # others!), so set os.environ['PATH'] explicitly. + oldpath = os.environ['PATH'] + if makefile.env is not None and 'PATH' in makefile.env: + os.environ['PATH'] = makefile.env['PATH'] + + log.debug("%s: running command '%s'" % (self.loc, ' '.join(cline))) + try: + p = subprocess.Popen(cline, executable=executable, env=makefile.env, shell=False, + stdout=subprocess.PIPE, cwd=makefile.workdir) + except OSError, e: + print >>sys.stderr, "Error executing command %s" % cline[0], e + return + finally: + os.environ['PATH'] = oldpath + + stdout, stderr = p.communicate() + stdout = stdout.replace('\r\n', '\n') + if stdout.endswith('\n'): + stdout = stdout[:-1] + stdout = stdout.replace('\n', ' ') + + fd.write(stdout) + +class ErrorFunction(Function): + name = 'error' + minargs = 1 + maxargs = 1 + + __slots__ = Function.__slots__ + + def resolve(self, makefile, variables, fd, setting): + v = self._arguments[0].resolvestr(makefile, variables, setting) + raise data.DataError(v, self.loc) + +class WarningFunction(Function): + name = 'warning' + minargs = 1 + maxargs = 1 + + __slots__ = Function.__slots__ + + def resolve(self, makefile, variables, fd, setting): + v = self._arguments[0].resolvestr(makefile, variables, setting) + log.warning(v) + +class InfoFunction(Function): + name = 'info' + minargs = 1 + maxargs = 1 + + __slots__ = Function.__slots__ + + def resolve(self, makefile, variables, fd, setting): + v = self._arguments[0].resolvestr(makefile, variables, setting) + print v + +functionmap = { + 'subst': SubstFunction, + 'patsubst': PatSubstFunction, + 'strip': StripFunction, + 'findstring': FindstringFunction, + 'filter': FilterFunction, + 'filter-out': FilteroutFunction, + 'sort': SortFunction, + 'word': WordFunction, + 'wordlist': WordlistFunction, + 'words': WordsFunction, + 'firstword': FirstWordFunction, + 'lastword': LastWordFunction, + 'dir': DirFunction, + 'notdir': NotDirFunction, + 'suffix': SuffixFunction, + 'basename': BasenameFunction, + 'addsuffix': AddSuffixFunction, + 'addprefix': AddPrefixFunction, + 'join': JoinFunction, + 'wildcard': WildcardFunction, + 'realpath': RealpathFunction, + 'abspath': AbspathFunction, + 'if': IfFunction, + 'or': OrFunction, + 'and': AndFunction, + 'foreach': ForEachFunction, + 'call': CallFunction, + 'value': ValueFunction, + 'eval': EvalFunction, + 'origin': OriginFunction, + 'flavor': FlavorFunction, + 'shell': ShellFunction, + 'error': ErrorFunction, + 'warning': WarningFunction, + 'info': InfoFunction, +} + +import data diff --git a/build/pymake/pymake/globrelative.py b/build/pymake/pymake/globrelative.py new file mode 100644 index 000000000..37ca28e06 --- /dev/null +++ b/build/pymake/pymake/globrelative.py @@ -0,0 +1,68 @@ +""" +Filename globbing like the python glob module with minor differences: + +* glob relative to an arbitrary directory +* include . and .. +* check that link targets exist, not just links +""" + +import os, re, fnmatch +import util + +_globcheck = re.compile('[[*?]') + +def hasglob(p): + return _globcheck.search(p) is not None + +def glob(fsdir, path): + """ + Yield paths matching the path glob. Sorts as a bonus. Excludes '.' and '..' + """ + + dir, leaf = os.path.split(path) + if dir == '': + return globpattern(fsdir, leaf) + + if hasglob(dir): + dirsfound = glob(fsdir, dir) + else: + dirsfound = [dir] + + r = [] + + for dir in dirsfound: + fspath = util.normaljoin(fsdir, dir) + if not os.path.isdir(fspath): + continue + + r.extend((util.normaljoin(dir, found) for found in globpattern(fspath, leaf))) + + return r + +def globpattern(dir, pattern): + """ + Return leaf names in the specified directory which match the pattern. + """ + + if not hasglob(pattern): + if pattern == '': + if os.path.isdir(dir): + return [''] + return [] + + if os.path.exists(util.normaljoin(dir, pattern)): + return [pattern] + return [] + + leaves = os.listdir(dir) + ['.', '..'] + + # "hidden" filenames are a bit special + if not pattern.startswith('.'): + leaves = [leaf for leaf in leaves + if not leaf.startswith('.')] + + leaves = fnmatch.filter(leaves, pattern) + leaves = filter(lambda l: os.path.exists(util.normaljoin(dir, l)), leaves) + + leaves.sort() + return leaves diff --git a/build/pymake/pymake/implicit.py b/build/pymake/pymake/implicit.py new file mode 100644 index 000000000..d73895cab --- /dev/null +++ b/build/pymake/pymake/implicit.py @@ -0,0 +1,14 @@ +"""
+Implicit variables; perhaps in the future this will also include some implicit
+rules, at least match-anything cancellation rules.
+"""
+
+variables = {
+ 'MKDIR': '%pymake.builtins mkdir',
+ 'RM': '%pymake.builtins rm -f',
+ 'SLEEP': '%pymake.builtins sleep',
+ 'TOUCH': '%pymake.builtins touch',
+ '.LIBPATTERNS': 'lib%.so lib%.a',
+ '.PYMAKE': '1',
+ }
+
diff --git a/build/pymake/pymake/parser.py b/build/pymake/pymake/parser.py new file mode 100644 index 000000000..ad977a9dd --- /dev/null +++ b/build/pymake/pymake/parser.py @@ -0,0 +1,799 @@ +""" +Module for parsing Makefile syntax. + +Makefiles use a line-based parsing system. Continuations and substitutions are handled differently based on the +type of line being parsed: + +Lines with makefile syntax condense continuations to a single space, no matter the actual trailing whitespace +of the first line or the leading whitespace of the continuation. In other situations, trailing whitespace is +relevant. + +Lines with command syntax do not condense continuations: the backslash and newline are part of the command. +(GNU Make is buggy in this regard, at least on mac). + +Lines with an initial tab are commands if they can be (there is a rule or a command immediately preceding). +Otherwise, they are parsed as makefile syntax. + +This file parses into the data structures defined in the parserdata module. Those classes are what actually +do the dirty work of "executing" the parsed data into a data.Makefile. + +Four iterator functions are available: +* iterdata +* itermakefilechars +* itercommandchars + +The iterators handle line continuations and comments in different ways, but share a common calling +convention: + +Called with (data, startoffset, tokenlist, finditer) + +yield 4-tuples (flatstr, token, tokenoffset, afteroffset) +flatstr is data, guaranteed to have no tokens (may be '') +token, tokenoffset, afteroffset *may be None*. That means there is more text +coming. +""" + +import logging, re, os, sys +import data, functions, util, parserdata + +_log = logging.getLogger('pymake.parser') + +class SyntaxError(util.MakeError): + pass + +_skipws = re.compile('\S') +class Data(object): + """ + A single virtual "line", which can be multiple source lines joined with + continuations. + """ + + __slots__ = ('s', 'lstart', 'lend', 'loc') + + def __init__(self, s, lstart, lend, loc): + self.s = s + self.lstart = lstart + self.lend = lend + self.loc = loc + + @staticmethod + def fromstring(s, path): + return Data(s, 0, len(s), parserdata.Location(path, 1, 0)) + + def getloc(self, offset): + assert offset >= self.lstart and offset <= self.lend + return self.loc.offset(self.s, self.lstart, offset) + + def skipwhitespace(self, offset): + """ + Return the offset of the first non-whitespace character in data starting at offset, or None if there are + only whitespace characters remaining. + """ + m = _skipws.search(self.s, offset, self.lend) + if m is None: + return self.lend + + return m.start(0) + +_linere = re.compile(r'\\*\n') +def enumeratelines(s, filename): + """ + Enumerate lines in a string as Data objects, joining line + continuations. + """ + + off = 0 + lineno = 1 + curlines = 0 + for m in _linere.finditer(s): + curlines += 1 + start, end = m.span(0) + + if (start - end) % 2 == 0: + # odd number of backslashes is a continuation + continue + + yield Data(s, off, end - 1, parserdata.Location(filename, lineno, 0)) + + lineno += curlines + curlines = 0 + off = end + + yield Data(s, off, len(s), parserdata.Location(filename, lineno, 0)) + +_alltokens = re.compile(r'''\\*\# | # hash mark preceeded by any number of backslashes + := | + \+= | + \?= | + :: | + (?:\$(?:$|[\(\{](?:%s)\s+|.)) | # dollar sign followed by EOF, a function keyword with whitespace, or any character + :(?![\\/]) | # colon followed by anything except a slash (Windows path detection) + [=#{}();,|'"]''' % '|'.join(functions.functionmap.iterkeys()), re.VERBOSE) + +def iterdata(d, offset, tokenlist, it): + """ + Iterate over flat data without line continuations, comments, or any special escaped characters. + + Typically used to parse recursively-expanded variables. + """ + + assert len(tokenlist), "Empty tokenlist passed to iterdata is meaningless!" + assert offset >= d.lstart and offset <= d.lend, "offset %i should be between %i and %i" % (offset, d.lstart, d.lend) + + if offset == d.lend: + return + + s = d.s + for m in it: + mstart, mend = m.span(0) + token = s[mstart:mend] + if token in tokenlist or (token[0] == '$' and '$' in tokenlist): + yield s[offset:mstart], token, mstart, mend + else: + yield s[offset:mend], None, None, mend + offset = mend + + yield s[offset:d.lend], None, None, None + +# multiple backslashes before a newline are unescaped, halving their total number +_makecontinuations = re.compile(r'(?:\s*|((?:\\\\)+))\\\n\s*') +def _replacemakecontinuations(m): + start, end = m.span(1) + if start == -1: + return ' ' + return ' '.rjust((end - start) / 2 + 1, '\\') + +def itermakefilechars(d, offset, tokenlist, it, ignorecomments=False): + """ + Iterate over data in makefile syntax. Comments are found at unescaped # characters, and escaped newlines + are converted to single-space continuations. + """ + + assert offset >= d.lstart and offset <= d.lend, "offset %i should be between %i and %i" % (offset, d.lstart, d.lend) + + if offset == d.lend: + return + + s = d.s + for m in it: + mstart, mend = m.span(0) + token = s[mstart:mend] + + starttext = _makecontinuations.sub(_replacemakecontinuations, s[offset:mstart]) + + if token[-1] == '#' and not ignorecomments: + l = mend - mstart + # multiple backslashes before a hash are unescaped, halving their total number + if l % 2: + # found a comment + yield starttext + token[:(l - 1) / 2], None, None, None + return + else: + yield starttext + token[-l / 2:], None, None, mend + elif token in tokenlist or (token[0] == '$' and '$' in tokenlist): + yield starttext, token, mstart, mend + else: + yield starttext + token, None, None, mend + offset = mend + + yield _makecontinuations.sub(_replacemakecontinuations, s[offset:d.lend]), None, None, None + +_findcomment = re.compile(r'\\*\#') +def flattenmakesyntax(d, offset): + """ + A shortcut method for flattening line continuations and comments in makefile syntax without + looking for other tokens. + """ + + assert offset >= d.lstart and offset <= d.lend, "offset %i should be between %i and %i" % (offset, d.lstart, d.lend) + if offset == d.lend: + return '' + + s = _makecontinuations.sub(_replacemakecontinuations, d.s[offset:d.lend]) + + elements = [] + offset = 0 + for m in _findcomment.finditer(s): + mstart, mend = m.span(0) + elements.append(s[offset:mstart]) + if (mend - mstart) % 2: + # even number of backslashes... it's a comment + elements.append(''.ljust((mend - mstart - 1) / 2, '\\')) + return ''.join(elements) + + # odd number of backslashes + elements.append(''.ljust((mend - mstart - 2) / 2, '\\') + '#') + offset = mend + + elements.append(s[offset:]) + return ''.join(elements) + +def itercommandchars(d, offset, tokenlist, it): + """ + Iterate over command syntax. # comment markers are not special, and escaped newlines are included + in the output text. + """ + + assert offset >= d.lstart and offset <= d.lend, "offset %i should be between %i and %i" % (offset, d.lstart, d.lend) + + if offset == d.lend: + return + + s = d.s + for m in it: + mstart, mend = m.span(0) + token = s[mstart:mend] + starttext = s[offset:mstart].replace('\n\t', '\n') + + if token in tokenlist or (token[0] == '$' and '$' in tokenlist): + yield starttext, token, mstart, mend + else: + yield starttext + token, None, None, mend + offset = mend + + yield s[offset:d.lend].replace('\n\t', '\n'), None, None, None + +_redefines = re.compile('\s*define|\s*endef') +def iterdefinelines(it, startloc): + """ + Process the insides of a define. Most characters are included literally. Escaped newlines are treated + as they would be in makefile syntax. Internal define/endef pairs are ignored. + """ + + results = [] + + definecount = 1 + for d in it: + m = _redefines.match(d.s, d.lstart, d.lend) + if m is not None: + directive = m.group(0).strip() + if directive == 'endef': + definecount -= 1 + if definecount == 0: + return _makecontinuations.sub(_replacemakecontinuations, '\n'.join(results)) + else: + definecount += 1 + + results.append(d.s[d.lstart:d.lend]) + + # Falling off the end is an unterminated define! + raise SyntaxError("define without matching endef", startloc) + +def _ensureend(d, offset, msg): + """ + Ensure that only whitespace remains in this data. + """ + + s = flattenmakesyntax(d, offset) + if s != '' and not s.isspace(): + raise SyntaxError(msg, d.getloc(offset)) + +_eqargstokenlist = ('(', "'", '"') + +def ifeq(d, offset): + if offset > d.lend - 1: + raise SyntaxError("No arguments after conditional", d.getloc(offset)) + + # the variety of formats for this directive is rather maddening + token = d.s[offset] + if token not in _eqargstokenlist: + raise SyntaxError("No arguments after conditional", d.getloc(offset)) + + offset += 1 + + if token == '(': + arg1, t, offset = parsemakesyntax(d, offset, (',',), itermakefilechars) + if t is None: + raise SyntaxError("Expected two arguments in conditional", d.getloc(d.lend)) + + arg1.rstrip() + + offset = d.skipwhitespace(offset) + arg2, t, offset = parsemakesyntax(d, offset, (')',), itermakefilechars) + if t is None: + raise SyntaxError("Unexpected text in conditional", d.getloc(offset)) + + _ensureend(d, offset, "Unexpected text after conditional") + else: + arg1, t, offset = parsemakesyntax(d, offset, (token,), itermakefilechars) + if t is None: + raise SyntaxError("Unexpected text in conditional", d.getloc(d.lend)) + + offset = d.skipwhitespace(offset) + if offset == d.lend: + raise SyntaxError("Expected two arguments in conditional", d.getloc(offset)) + + token = d.s[offset] + if token not in '\'"': + raise SyntaxError("Unexpected text in conditional", d.getloc(offset)) + + arg2, t, offset = parsemakesyntax(d, offset + 1, (token,), itermakefilechars) + + _ensureend(d, offset, "Unexpected text after conditional") + + return parserdata.EqCondition(arg1, arg2) + +def ifneq(d, offset): + c = ifeq(d, offset) + c.expected = False + return c + +def ifdef(d, offset): + e, t, offset = parsemakesyntax(d, offset, (), itermakefilechars) + e.rstrip() + + return parserdata.IfdefCondition(e) + +def ifndef(d, offset): + c = ifdef(d, offset) + c.expected = False + return c + +_conditionkeywords = { + 'ifeq': ifeq, + 'ifneq': ifneq, + 'ifdef': ifdef, + 'ifndef': ifndef + } + +_conditiontokens = tuple(_conditionkeywords.iterkeys()) +_conditionre = re.compile(r'(%s)(?:$|\s+)' % '|'.join(_conditiontokens)) + +_directivestokenlist = _conditiontokens + \ + ('else', 'endif', 'define', 'endef', 'override', 'include', '-include', 'includedeps', '-includedeps', 'vpath', 'export', 'unexport') + +_directivesre = re.compile(r'(%s)(?:$|\s+)' % '|'.join(_directivestokenlist)) + +_varsettokens = (':=', '+=', '?=', '=') + +def _parsefile(pathname): + fd = open(pathname, "rU") + stmts = parsestring(fd.read(), pathname) + stmts.mtime = os.fstat(fd.fileno()).st_mtime + fd.close() + return stmts + +def _checktime(path, stmts): + mtime = os.path.getmtime(path) + if mtime != stmts.mtime: + _log.debug("Re-parsing makefile '%s': mtimes differ", path) + return False + + return True + +_parsecache = util.MostUsedCache(15, _parsefile, _checktime) + +def parsefile(pathname): + """ + Parse a filename into a parserdata.StatementList. A cache is used to avoid re-parsing + makefiles that have already been parsed and have not changed. + """ + + pathname = os.path.realpath(pathname) + return _parsecache.get(pathname) + +def parsedepfile(pathname): + """ + Parse a filename listing only depencencies into a parserdata.StatementList. + """ + def continuation_iter(lines): + current_line = [] + for line in lines: + line = line.rstrip() + if line.endswith("\\"): + current_line.append(line.rstrip("\\")) + continue + if not len(line): + continue + current_line.append(line) + yield ''.join(current_line) + current_line = [] + if current_line: + yield ''.join(current_line) + + pathname = os.path.realpath(pathname) + stmts = parserdata.StatementList() + for line in continuation_iter(open(pathname).readlines()): + target, deps = line.split(":", 1) + stmts.append(parserdata.Rule(data.StringExpansion(target, None), + data.StringExpansion(deps, None), False)) + return stmts + +def parsestring(s, filename): + """ + Parse a string containing makefile data into a parserdata.StatementList. + """ + + currule = False + condstack = [parserdata.StatementList()] + + fdlines = enumeratelines(s, filename) + for d in fdlines: + assert len(condstack) > 0 + + offset = d.lstart + + if currule and offset < d.lend and d.s[offset] == '\t': + e, token, offset = parsemakesyntax(d, offset + 1, (), itercommandchars) + assert token is None + assert offset is None + condstack[-1].append(parserdata.Command(e)) + continue + + # To parse Makefile syntax, we first strip leading whitespace and + # look for initial keywords. If there are no keywords, it's either + # setting a variable or writing a rule. + + offset = d.skipwhitespace(offset) + if offset is None: + continue + + m = _directivesre.match(d.s, offset, d.lend) + if m is not None: + kword = m.group(1) + offset = m.end(0) + + if kword == 'endif': + _ensureend(d, offset, "Unexpected data after 'endif' directive") + if len(condstack) == 1: + raise SyntaxError("unmatched 'endif' directive", + d.getloc(offset)) + + condstack.pop().endloc = d.getloc(offset) + continue + + if kword == 'else': + if len(condstack) == 1: + raise SyntaxError("unmatched 'else' directive", + d.getloc(offset)) + + m = _conditionre.match(d.s, offset, d.lend) + if m is None: + _ensureend(d, offset, "Unexpected data after 'else' directive.") + condstack[-1].addcondition(d.getloc(offset), parserdata.ElseCondition()) + else: + kword = m.group(1) + if kword not in _conditionkeywords: + raise SyntaxError("Unexpected condition after 'else' directive.", + d.getloc(offset)) + + startoffset = offset + offset = d.skipwhitespace(m.end(1)) + c = _conditionkeywords[kword](d, offset) + condstack[-1].addcondition(d.getloc(startoffset), c) + continue + + if kword in _conditionkeywords: + c = _conditionkeywords[kword](d, offset) + cb = parserdata.ConditionBlock(d.getloc(d.lstart), c) + condstack[-1].append(cb) + condstack.append(cb) + continue + + if kword == 'endef': + raise SyntaxError("endef without matching define", d.getloc(offset)) + + if kword == 'define': + currule = False + vname, t, i = parsemakesyntax(d, offset, (), itermakefilechars) + vname.rstrip() + + startloc = d.getloc(d.lstart) + value = iterdefinelines(fdlines, startloc) + condstack[-1].append(parserdata.SetVariable(vname, value=value, valueloc=startloc, token='=', targetexp=None)) + continue + + if kword in ('include', '-include', 'includedeps', '-includedeps'): + if kword.startswith('-'): + required = False + kword = kword[1:] + else: + required = True + + deps = kword == 'includedeps' + + currule = False + incfile, t, offset = parsemakesyntax(d, offset, (), itermakefilechars) + condstack[-1].append(parserdata.Include(incfile, required, deps)) + + continue + + if kword == 'vpath': + currule = False + e, t, offset = parsemakesyntax(d, offset, (), itermakefilechars) + condstack[-1].append(parserdata.VPathDirective(e)) + continue + + if kword == 'override': + currule = False + vname, token, offset = parsemakesyntax(d, offset, _varsettokens, itermakefilechars) + vname.lstrip() + vname.rstrip() + + if token is None: + raise SyntaxError("Malformed override directive, need =", d.getloc(d.lstart)) + + value = flattenmakesyntax(d, offset).lstrip() + + condstack[-1].append(parserdata.SetVariable(vname, value=value, valueloc=d.getloc(offset), token=token, targetexp=None, source=data.Variables.SOURCE_OVERRIDE)) + continue + + if kword == 'export': + currule = False + e, token, offset = parsemakesyntax(d, offset, _varsettokens, itermakefilechars) + e.lstrip() + e.rstrip() + + if token is None: + condstack[-1].append(parserdata.ExportDirective(e, concurrent_set=False)) + else: + condstack[-1].append(parserdata.ExportDirective(e, concurrent_set=True)) + + value = flattenmakesyntax(d, offset).lstrip() + condstack[-1].append(parserdata.SetVariable(e, value=value, valueloc=d.getloc(offset), token=token, targetexp=None)) + + continue + + if kword == 'unexport': + e, token, offset = parsemakesyntax(d, offset, (), itermakefilechars) + condstack[-1].append(parserdata.UnexportDirective(e)) + continue + + e, token, offset = parsemakesyntax(d, offset, _varsettokens + ('::', ':'), itermakefilechars) + if token is None: + e.rstrip() + e.lstrip() + if not e.isempty(): + condstack[-1].append(parserdata.EmptyDirective(e)) + continue + + # if we encountered real makefile syntax, the current rule is over + currule = False + + if token in _varsettokens: + e.lstrip() + e.rstrip() + + value = flattenmakesyntax(d, offset).lstrip() + + condstack[-1].append(parserdata.SetVariable(e, value=value, valueloc=d.getloc(offset), token=token, targetexp=None)) + else: + doublecolon = token == '::' + + # `e` is targets or target patterns, which can end up as + # * a rule + # * an implicit rule + # * a static pattern rule + # * a target-specific variable definition + # * a pattern-specific variable definition + # any of the rules may have order-only prerequisites + # delimited by |, and a command delimited by ; + targets = e + + e, token, offset = parsemakesyntax(d, offset, + _varsettokens + (':', '|', ';'), + itermakefilechars) + if token in (None, ';'): + condstack[-1].append(parserdata.Rule(targets, e, doublecolon)) + currule = True + + if token == ';': + offset = d.skipwhitespace(offset) + e, t, offset = parsemakesyntax(d, offset, (), itercommandchars) + condstack[-1].append(parserdata.Command(e)) + + elif token in _varsettokens: + e.lstrip() + e.rstrip() + + value = flattenmakesyntax(d, offset).lstrip() + condstack[-1].append(parserdata.SetVariable(e, value=value, valueloc=d.getloc(offset), token=token, targetexp=targets)) + elif token == '|': + raise SyntaxError('order-only prerequisites not implemented', d.getloc(offset)) + else: + assert token == ':' + # static pattern rule + + pattern = e + + deps, token, offset = parsemakesyntax(d, offset, (';',), itermakefilechars) + + condstack[-1].append(parserdata.StaticPatternRule(targets, pattern, deps, doublecolon)) + currule = True + + if token == ';': + offset = d.skipwhitespace(offset) + e, token, offset = parsemakesyntax(d, offset, (), itercommandchars) + condstack[-1].append(parserdata.Command(e)) + + if len(condstack) != 1: + raise SyntaxError("Condition never terminated with endif", condstack[-1].loc) + + return condstack[0] + +_PARSESTATE_TOPLEVEL = 0 # at the top level +_PARSESTATE_FUNCTION = 1 # expanding a function call +_PARSESTATE_VARNAME = 2 # expanding a variable expansion. +_PARSESTATE_SUBSTFROM = 3 # expanding a variable expansion substitution "from" value +_PARSESTATE_SUBSTTO = 4 # expanding a variable expansion substitution "to" value +_PARSESTATE_PARENMATCH = 5 # inside nested parentheses/braces that must be matched + +class ParseStackFrame(object): + __slots__ = ('parsestate', 'parent', 'expansion', 'tokenlist', 'openbrace', 'closebrace', 'function', 'loc', 'varname', 'substfrom') + + def __init__(self, parsestate, parent, expansion, tokenlist, openbrace, closebrace, function=None, loc=None): + self.parsestate = parsestate + self.parent = parent + self.expansion = expansion + self.tokenlist = tokenlist + self.openbrace = openbrace + self.closebrace = closebrace + self.function = function + self.loc = loc + + def __str__(self): + return "<state=%i expansion=%s tokenlist=%s openbrace=%s closebrace=%s>" % (self.parsestate, self.expansion, self.tokenlist, self.openbrace, self.closebrace) + +_matchingbrace = { + '(': ')', + '{': '}', + } + +def parsemakesyntax(d, offset, stopon, iterfunc): + """ + Given Data, parse it into a data.Expansion. + + @param stopon (sequence) + Indicate characters where toplevel parsing should stop. + + @param iterfunc (generator function) + A function which is used to iterate over d, yielding (char, offset, loc) + @see iterdata + @see itermakefilechars + @see itercommandchars + + @return a tuple (expansion, token, offset). If all the data is consumed, + token and offset will be None + """ + + assert callable(iterfunc) + + stacktop = ParseStackFrame(_PARSESTATE_TOPLEVEL, None, data.Expansion(loc=d.getloc(d.lstart)), + tokenlist=stopon + ('$',), + openbrace=None, closebrace=None) + + tokeniterator = _alltokens.finditer(d.s, offset, d.lend) + + di = iterfunc(d, offset, stacktop.tokenlist, tokeniterator) + while True: # this is not a for loop because `di` changes during the function + assert stacktop is not None + try: + s, token, tokenoffset, offset = di.next() + except StopIteration: + break + + stacktop.expansion.appendstr(s) + if token is None: + continue + + parsestate = stacktop.parsestate + + if token[0] == '$': + if tokenoffset + 1 == d.lend: + # an unterminated $ expands to nothing + break + + loc = d.getloc(tokenoffset) + c = token[1] + if c == '$': + assert len(token) == 2 + stacktop.expansion.appendstr('$') + elif c in ('(', '{'): + closebrace = _matchingbrace[c] + + if len(token) > 2: + fname = token[2:].rstrip() + fn = functions.functionmap[fname](loc) + e = data.Expansion() + if len(fn) + 1 == fn.maxargs: + tokenlist = (c, closebrace, '$') + else: + tokenlist = (',', c, closebrace, '$') + + stacktop = ParseStackFrame(_PARSESTATE_FUNCTION, stacktop, + e, tokenlist, function=fn, + openbrace=c, closebrace=closebrace) + else: + e = data.Expansion() + tokenlist = (':', c, closebrace, '$') + stacktop = ParseStackFrame(_PARSESTATE_VARNAME, stacktop, + e, tokenlist, + openbrace=c, closebrace=closebrace, loc=loc) + else: + assert len(token) == 2 + e = data.Expansion.fromstring(c, loc) + stacktop.expansion.appendfunc(functions.VariableRef(loc, e)) + elif token in ('(', '{'): + assert token == stacktop.openbrace + + stacktop.expansion.appendstr(token) + stacktop = ParseStackFrame(_PARSESTATE_PARENMATCH, stacktop, + stacktop.expansion, + (token, stacktop.closebrace, '$'), + openbrace=token, closebrace=stacktop.closebrace, loc=d.getloc(tokenoffset)) + elif parsestate == _PARSESTATE_PARENMATCH: + assert token == stacktop.closebrace + stacktop.expansion.appendstr(token) + stacktop = stacktop.parent + elif parsestate == _PARSESTATE_TOPLEVEL: + assert stacktop.parent is None + return stacktop.expansion.finish(), token, offset + elif parsestate == _PARSESTATE_FUNCTION: + if token == ',': + stacktop.function.append(stacktop.expansion.finish()) + + stacktop.expansion = data.Expansion() + if len(stacktop.function) + 1 == stacktop.function.maxargs: + tokenlist = (stacktop.openbrace, stacktop.closebrace, '$') + stacktop.tokenlist = tokenlist + elif token in (')', '}'): + fn = stacktop.function + fn.append(stacktop.expansion.finish()) + fn.setup() + + stacktop = stacktop.parent + stacktop.expansion.appendfunc(fn) + else: + assert False, "Not reached, _PARSESTATE_FUNCTION" + elif parsestate == _PARSESTATE_VARNAME: + if token == ':': + stacktop.varname = stacktop.expansion + stacktop.parsestate = _PARSESTATE_SUBSTFROM + stacktop.expansion = data.Expansion() + stacktop.tokenlist = ('=', stacktop.openbrace, stacktop.closebrace, '$') + elif token in (')', '}'): + fn = functions.VariableRef(stacktop.loc, stacktop.expansion.finish()) + stacktop = stacktop.parent + stacktop.expansion.appendfunc(fn) + else: + assert False, "Not reached, _PARSESTATE_VARNAME" + elif parsestate == _PARSESTATE_SUBSTFROM: + if token == '=': + stacktop.substfrom = stacktop.expansion + stacktop.parsestate = _PARSESTATE_SUBSTTO + stacktop.expansion = data.Expansion() + stacktop.tokenlist = (stacktop.openbrace, stacktop.closebrace, '$') + elif token in (')', '}'): + # A substitution of the form $(VARNAME:.ee) is probably a mistake, but make + # parses it. Issue a warning. Combine the varname and substfrom expansions to + # make the compatible varname. See tests/var-substitutions.mk SIMPLE3SUBSTNAME + _log.warning("%s: Variable reference looks like substitution without =", stacktop.loc) + stacktop.varname.appendstr(':') + stacktop.varname.concat(stacktop.expansion) + fn = functions.VariableRef(stacktop.loc, stacktop.varname.finish()) + stacktop = stacktop.parent + stacktop.expansion.appendfunc(fn) + else: + assert False, "Not reached, _PARSESTATE_SUBSTFROM" + elif parsestate == _PARSESTATE_SUBSTTO: + assert token in (')','}'), "Not reached, _PARSESTATE_SUBSTTO" + + fn = functions.SubstitutionRef(stacktop.loc, stacktop.varname.finish(), + stacktop.substfrom.finish(), stacktop.expansion.finish()) + stacktop = stacktop.parent + stacktop.expansion.appendfunc(fn) + else: + assert False, "Unexpected parse state %s" % stacktop.parsestate + + if stacktop.parent is not None and iterfunc == itercommandchars: + di = itermakefilechars(d, offset, stacktop.tokenlist, tokeniterator, + ignorecomments=True) + else: + di = iterfunc(d, offset, stacktop.tokenlist, tokeniterator) + + if stacktop.parent is not None: + raise SyntaxError("Unterminated function call", d.getloc(offset)) + + assert stacktop.parsestate == _PARSESTATE_TOPLEVEL + + return stacktop.expansion.finish(), None, None diff --git a/build/pymake/pymake/parserdata.py b/build/pymake/pymake/parserdata.py new file mode 100644 index 000000000..0d3f147dc --- /dev/null +++ b/build/pymake/pymake/parserdata.py @@ -0,0 +1,1007 @@ +import logging, re, os +import data, parser, functions, util +from cStringIO import StringIO +from pymake.globrelative import hasglob, glob + +_log = logging.getLogger('pymake.data') +_tabwidth = 4 + +class Location(object): + """ + A location within a makefile. + + For the moment, locations are just path/line/column, but in the future + they may reference parent locations for more accurate "included from" + or "evaled at" error reporting. + """ + __slots__ = ('path', 'line', 'column') + + def __init__(self, path, line, column): + self.path = path + self.line = line + self.column = column + + def offset(self, s, start, end): + """ + Returns a new location offset by + the specified string. + """ + + if start == end: + return self + + skiplines = s.count('\n', start, end) + line = self.line + skiplines + if skiplines: + lastnl = s.rfind('\n', start, end) + assert lastnl != -1 + start = lastnl + 1 + column = 0 + else: + column = self.column + + while True: + j = s.find('\t', start, end) + if j == -1: + column += end - start + break + + column += j - start + column += _tabwidth + column -= column % _tabwidth + start = j + 1 + + return Location(self.path, line, column) + + def __str__(self): + return "%s:%s:%s" % (self.path, self.line, self.column) + +def _expandwildcards(makefile, tlist): + for t in tlist: + if not hasglob(t): + yield t + else: + l = glob(makefile.workdir, t) + for r in l: + yield r + +_flagescape = re.compile(r'([\s\\])') + +def parsecommandlineargs(args): + """ + Given a set of arguments from a command-line invocation of make, + parse out the variable definitions and return (stmts, arglist, overridestr) + """ + + overrides = [] + stmts = StatementList() + r = [] + for i in xrange(0, len(args)): + a = args[i] + + vname, t, val = util.strpartition(a, ':=') + if t == '': + vname, t, val = util.strpartition(a, '=') + if t != '': + overrides.append(_flagescape.sub(r'\\\1', a)) + + vname = vname.strip() + vnameexp = data.Expansion.fromstring(vname, "Command-line argument") + + stmts.append(ExportDirective(vnameexp, concurrent_set=True)) + stmts.append(SetVariable(vnameexp, token=t, + value=val, valueloc=Location('<command-line>', i, len(vname) + len(t)), + targetexp=None, source=data.Variables.SOURCE_COMMANDLINE)) + else: + r.append(a) + + return stmts, r, ' '.join(overrides) + +class Statement(object): + """ + Represents parsed make file syntax. + + This is an abstract base class. Child classes are expected to implement + basic methods defined below. + """ + + def execute(self, makefile, context): + """Executes this Statement within a make file execution context.""" + raise Exception("%s must implement execute()." % self.__class__) + + def to_source(self): + """Obtain the make file "source" representation of the Statement. + + This converts an individual Statement back to a string that can again + be parsed into this Statement. + """ + raise Exception("%s must implement to_source()." % self.__class__) + + def __eq__(self, other): + raise Exception("%s must implement __eq__." % self.__class__) + + def __ne__(self, other): + return self.__eq__(other) + +class DummyRule(object): + __slots__ = () + + def addcommand(self, r): + pass + +class Rule(Statement): + """ + Rules represent how to make specific targets. + + See https://www.gnu.org/software/make/manual/make.html#Rules. + + An individual rule is composed of a target, dependencies, and a recipe. + This class only contains references to the first 2. The recipe will be + contained in Command classes which follow this one in a stream of Statement + instances. + + Instances also contain a boolean property `doublecolon` which says whether + this is a doublecolon rule. Doublecolon rules are rules that are always + executed, if they are evaluated. Normally, rules are only executed if their + target is out of date. + """ + __slots__ = ('targetexp', 'depexp', 'doublecolon') + + def __init__(self, targetexp, depexp, doublecolon): + assert isinstance(targetexp, (data.Expansion, data.StringExpansion)) + assert isinstance(depexp, (data.Expansion, data.StringExpansion)) + + self.targetexp = targetexp + self.depexp = depexp + self.doublecolon = doublecolon + + def execute(self, makefile, context): + if context.weak: + self._executeweak(makefile, context) + else: + self._execute(makefile, context) + + def _executeweak(self, makefile, context): + """ + If the context is weak (we're just handling dependencies) we can make a number of assumptions here. + This lets us go really fast and is generally good. + """ + assert context.weak + deps = self.depexp.resolvesplit(makefile, makefile.variables) + # Skip targets with no rules and no dependencies + if not deps: + return + targets = self.targetexp.resolvesplit(makefile, makefile.variables) + assert len(targets) == 1 + target = targets[0] + rule = data.Rule(deps, self.doublecolon, loc=self.targetexp.loc, weakdeps=True) + makefile.gettarget(target).addrule(rule) + makefile.foundtarget(target) + context.currule = rule + + def _execute(self, makefile, context): + assert not context.weak + + atargets = data.stripdotslashes(self.targetexp.resolvesplit(makefile, makefile.variables)) + targets = [data.Pattern(p) for p in _expandwildcards(makefile, atargets)] + + if not len(targets): + context.currule = DummyRule() + return + + ispatterns = set((t.ispattern() for t in targets)) + if len(ispatterns) == 2: + raise data.DataError("Mixed implicit and normal rule", self.targetexp.loc) + ispattern, = ispatterns + + deps = list(_expandwildcards(makefile, data.stripdotslashes(self.depexp.resolvesplit(makefile, makefile.variables)))) + if ispattern: + rule = data.PatternRule(targets, map(data.Pattern, deps), self.doublecolon, loc=self.targetexp.loc) + makefile.appendimplicitrule(rule) + else: + rule = data.Rule(deps, self.doublecolon, loc=self.targetexp.loc, weakdeps=False) + for t in targets: + makefile.gettarget(t.gettarget()).addrule(rule) + + makefile.foundtarget(targets[0].gettarget()) + + context.currule = rule + + def dump(self, fd, indent): + print >>fd, "%sRule %s: %s" % (indent, self.targetexp, self.depexp) + + def to_source(self): + sep = ':' + + if self.doublecolon: + sep = '::' + + deps = self.depexp.to_source() + if len(deps) > 0 and not deps[0].isspace(): + sep += ' ' + + return '\n%s%s%s' % ( + self.targetexp.to_source(escape_variables=True), + sep, + deps) + + def __eq__(self, other): + if not isinstance(other, Rule): + return False + + return self.targetexp == other.targetexp \ + and self.depexp == other.depexp \ + and self.doublecolon == other.doublecolon + +class StaticPatternRule(Statement): + """ + Static pattern rules are rules which specify multiple targets based on a + string pattern. + + See https://www.gnu.org/software/make/manual/make.html#Static-Pattern + + They are like `Rule` instances except an added property, `patternexp` is + present. It contains the Expansion which represents the rule pattern. + """ + __slots__ = ('targetexp', 'patternexp', 'depexp', 'doublecolon') + + def __init__(self, targetexp, patternexp, depexp, doublecolon): + assert isinstance(targetexp, (data.Expansion, data.StringExpansion)) + assert isinstance(patternexp, (data.Expansion, data.StringExpansion)) + assert isinstance(depexp, (data.Expansion, data.StringExpansion)) + + self.targetexp = targetexp + self.patternexp = patternexp + self.depexp = depexp + self.doublecolon = doublecolon + + def execute(self, makefile, context): + if context.weak: + raise data.DataError("Static pattern rules not allowed in includedeps", self.targetexp.loc) + + targets = list(_expandwildcards(makefile, data.stripdotslashes(self.targetexp.resolvesplit(makefile, makefile.variables)))) + + if not len(targets): + context.currule = DummyRule() + return + + patterns = list(data.stripdotslashes(self.patternexp.resolvesplit(makefile, makefile.variables))) + if len(patterns) != 1: + raise data.DataError("Static pattern rules must have a single pattern", self.patternexp.loc) + pattern = data.Pattern(patterns[0]) + + deps = [data.Pattern(p) for p in _expandwildcards(makefile, data.stripdotslashes(self.depexp.resolvesplit(makefile, makefile.variables)))] + + rule = data.PatternRule([pattern], deps, self.doublecolon, loc=self.targetexp.loc) + + for t in targets: + if data.Pattern(t).ispattern(): + raise data.DataError("Target '%s' of a static pattern rule must not be a pattern" % (t,), self.targetexp.loc) + stem = pattern.match(t) + if stem is None: + raise data.DataError("Target '%s' does not match the static pattern '%s'" % (t, pattern), self.targetexp.loc) + makefile.gettarget(t).addrule(data.PatternRuleInstance(rule, '', stem, pattern.ismatchany())) + + makefile.foundtarget(targets[0]) + context.currule = rule + + def dump(self, fd, indent): + print >>fd, "%sStaticPatternRule %s: %s: %s" % (indent, self.targetexp, self.patternexp, self.depexp) + + def to_source(self): + sep = ':' + + if self.doublecolon: + sep = '::' + + pattern = self.patternexp.to_source() + deps = self.depexp.to_source() + + if len(pattern) > 0 and pattern[0] not in (' ', '\t'): + sep += ' ' + + return '\n%s%s%s:%s' % ( + self.targetexp.to_source(escape_variables=True), + sep, + pattern, + deps) + + def __eq__(self, other): + if not isinstance(other, StaticPatternRule): + return False + + return self.targetexp == other.targetexp \ + and self.patternexp == other.patternexp \ + and self.depexp == other.depexp \ + and self.doublecolon == other.doublecolon + +class Command(Statement): + """ + Commands are things that get executed by a rule. + + A rule's recipe is composed of 0 or more Commands. + + A command is simply an expansion. Commands typically represent strings to + be executed in a shell (e.g. via system()). Although, since make files + allow arbitrary shells to be used for command execution, this isn't a + guarantee. + """ + __slots__ = ('exp',) + + def __init__(self, exp): + assert isinstance(exp, (data.Expansion, data.StringExpansion)) + self.exp = exp + + def execute(self, makefile, context): + assert context.currule is not None + if context.weak: + raise data.DataError("rules not allowed in includedeps", self.exp.loc) + + context.currule.addcommand(self.exp) + + def dump(self, fd, indent): + print >>fd, "%sCommand %s" % (indent, self.exp,) + + def to_source(self): + # Commands have some interesting quirks when it comes to source + # formatting. First, they can be multi-line. Second, a tab needs to be + # inserted at the beginning of every line. Finally, there might be + # variable references inside the command. This means we need to escape + # variable references inside command strings. Luckily, this is handled + # by the Expansion. + s = self.exp.to_source(escape_variables=True) + + return '\n'.join(['\t%s' % line for line in s.split('\n')]) + + def __eq__(self, other): + if not isinstance(other, Command): + return False + + return self.exp == other.exp + +class SetVariable(Statement): + """ + Represents a variable assignment. + + Variable assignment comes in two different flavors. + + Simple assignment has the form: + + <Expansion> <Assignment Token> <string> + + e.g. FOO := bar + + These correspond to the fields `vnameexp`, `token`, and `value`. In + addition, `valueloc` will be a Location and `source` will be a + pymake.data.Variables.SOURCE_* constant. + + There are also target-specific variables. These are variables that only + apply in the context of a specific target. They are like the aforementioned + assignment except the `targetexp` field is set to an Expansion representing + the target they apply to. + """ + __slots__ = ('vnameexp', 'token', 'value', 'valueloc', 'targetexp', 'source') + + def __init__(self, vnameexp, token, value, valueloc, targetexp, source=None): + assert isinstance(vnameexp, (data.Expansion, data.StringExpansion)) + assert isinstance(value, str) + assert targetexp is None or isinstance(targetexp, (data.Expansion, data.StringExpansion)) + + if source is None: + source = data.Variables.SOURCE_MAKEFILE + + self.vnameexp = vnameexp + self.token = token + self.value = value + self.valueloc = valueloc + self.targetexp = targetexp + self.source = source + + def execute(self, makefile, context): + vname = self.vnameexp.resolvestr(makefile, makefile.variables) + if len(vname) == 0: + raise data.DataError("Empty variable name", self.vnameexp.loc) + + if self.targetexp is None: + setvariables = [makefile.variables] + else: + setvariables = [] + + targets = [data.Pattern(t) for t in data.stripdotslashes(self.targetexp.resolvesplit(makefile, makefile.variables))] + for t in targets: + if t.ispattern(): + setvariables.append(makefile.getpatternvariables(t)) + else: + setvariables.append(makefile.gettarget(t.gettarget()).variables) + + for v in setvariables: + if self.token == '+=': + v.append(vname, self.source, self.value, makefile.variables, makefile) + continue + + if self.token == '?=': + flavor = data.Variables.FLAVOR_RECURSIVE + oldflavor, oldsource, oldval = v.get(vname, expand=False) + if oldval is not None: + continue + value = self.value + elif self.token == '=': + flavor = data.Variables.FLAVOR_RECURSIVE + value = self.value + else: + assert self.token == ':=' + + flavor = data.Variables.FLAVOR_SIMPLE + d = parser.Data.fromstring(self.value, self.valueloc) + e, t, o = parser.parsemakesyntax(d, 0, (), parser.iterdata) + value = e.resolvestr(makefile, makefile.variables) + + v.set(vname, flavor, self.source, value) + + def dump(self, fd, indent): + print >>fd, "%sSetVariable<%s> %s %s\n%s %r" % (indent, self.valueloc, self.vnameexp, self.token, indent, self.value) + + def __eq__(self, other): + if not isinstance(other, SetVariable): + return False + + return self.vnameexp == other.vnameexp \ + and self.token == other.token \ + and self.value == other.value \ + and self.targetexp == other.targetexp \ + and self.source == other.source + + def to_source(self): + chars = [] + for i in xrange(0, len(self.value)): + c = self.value[i] + + # Literal # is escaped in variable assignment otherwise it would be + # a comment. + if c == '#': + # If a backslash precedes this, we need to escape it as well. + if i > 0 and self.value[i-1] == '\\': + chars.append('\\') + + chars.append('\\#') + continue + + chars.append(c) + + value = ''.join(chars) + + prefix = '' + if self.source == data.Variables.SOURCE_OVERRIDE: + prefix = 'override ' + + # SetVariable come in two flavors: simple and target-specific. + + # We handle the target-specific syntax first. + if self.targetexp is not None: + return '%s: %s %s %s' % ( + self.targetexp.to_source(), + self.vnameexp.to_source(), + self.token, + value) + + # The variable could be multi-line or have leading whitespace. For + # regular variable assignment, whitespace after the token but before + # the value is ignored. If we see leading whitespace in the value here, + # the variable must have come from a define. + if value.count('\n') > 0 or (len(value) and value[0].isspace()): + # The parser holds the token in vnameexp for whatever reason. + return '%sdefine %s\n%s\nendef' % ( + prefix, + self.vnameexp.to_source(), + value) + + return '%s%s %s %s' % ( + prefix, + self.vnameexp.to_source(), + self.token, + value) + +class Condition(object): + """ + An abstract "condition", either ifeq or ifdef, perhaps negated. + + See https://www.gnu.org/software/make/manual/make.html#Conditional-Syntax + + Subclasses must implement: + + def evaluate(self, makefile) + """ + + def __eq__(self, other): + raise Exception("%s must implement __eq__." % __class__) + + def __ne__(self, other): + return not self.__eq__(other) + +class EqCondition(Condition): + """ + Represents an ifeq or ifneq conditional directive. + + This directive consists of two Expansions which are compared for equality. + + The `expected` field is a bool indicating what the condition must evaluate + to in order for its body to be executed. If True, this is an "ifeq" + conditional directive. If False, an "ifneq." + """ + __slots__ = ('exp1', 'exp2', 'expected') + + def __init__(self, exp1, exp2): + assert isinstance(exp1, (data.Expansion, data.StringExpansion)) + assert isinstance(exp2, (data.Expansion, data.StringExpansion)) + + self.expected = True + self.exp1 = exp1 + self.exp2 = exp2 + + def evaluate(self, makefile): + r1 = self.exp1.resolvestr(makefile, makefile.variables) + r2 = self.exp2.resolvestr(makefile, makefile.variables) + return (r1 == r2) == self.expected + + def __str__(self): + return "ifeq (expected=%s) %s %s" % (self.expected, self.exp1, self.exp2) + + def __eq__(self, other): + if not isinstance(other, EqCondition): + return False + + return self.exp1 == other.exp1 \ + and self.exp2 == other.exp2 \ + and self.expected == other.expected + +class IfdefCondition(Condition): + """ + Represents an ifdef or ifndef conditional directive. + + This directive consists of a single expansion which represents the name of + a variable (without the leading '$') which will be checked for definition. + + The `expected` field is a bool and has the same behavior as EqCondition. + If it is True, this represents a "ifdef" conditional. If False, "ifndef." + """ + __slots__ = ('exp', 'expected') + + def __init__(self, exp): + assert isinstance(exp, (data.Expansion, data.StringExpansion)) + self.exp = exp + self.expected = True + + def evaluate(self, makefile): + vname = self.exp.resolvestr(makefile, makefile.variables) + flavor, source, value = makefile.variables.get(vname, expand=False) + + if value is None: + return not self.expected + + return (len(value) > 0) == self.expected + + def __str__(self): + return "ifdef (expected=%s) %s" % (self.expected, self.exp) + + def __eq__(self, other): + if not isinstance(other, IfdefCondition): + return False + + return self.exp == other.exp and self.expected == other.expected + +class ElseCondition(Condition): + """ + Represents the transition between branches in a ConditionBlock. + """ + __slots__ = () + + def evaluate(self, makefile): + return True + + def __str__(self): + return "else" + + def __eq__(self, other): + return isinstance(other, ElseCondition) + +class ConditionBlock(Statement): + """ + A set of related Conditions. + + This is essentially a list of 2-tuples of (Condition, list(Statement)). + + The parser creates a ConditionBlock for all statements related to the same + conditional group. If iterating over the parser's output, where you think + you would see an ifeq, you will see a ConditionBlock containing an IfEq. In + other words, the parser collapses separate statements into this container + class. + + ConditionBlock instances may exist within other ConditionBlock if the + conditional logic is multiple levels deep. + """ + __slots__ = ('loc', '_groups') + + def __init__(self, loc, condition): + self.loc = loc + self._groups = [] + self.addcondition(loc, condition) + + def getloc(self): + return self.loc + + def addcondition(self, loc, condition): + assert isinstance(condition, Condition) + condition.loc = loc + + if len(self._groups) and isinstance(self._groups[-1][0], ElseCondition): + raise parser.SyntaxError("Multiple else conditions for block starting at %s" % self.loc, loc) + + self._groups.append((condition, StatementList())) + + def append(self, statement): + self._groups[-1][1].append(statement) + + def execute(self, makefile, context): + i = 0 + for c, statements in self._groups: + if c.evaluate(makefile): + _log.debug("Condition at %s met by clause #%i", self.loc, i) + statements.execute(makefile, context) + return + + i += 1 + + def dump(self, fd, indent): + print >>fd, "%sConditionBlock" % (indent,) + + indent2 = indent + ' ' + for c, statements in self._groups: + print >>fd, "%s Condition %s" % (indent, c) + statements.dump(fd, indent2) + print >>fd, "%s ~Condition" % (indent,) + print >>fd, "%s~ConditionBlock" % (indent,) + + def to_source(self): + lines = [] + index = 0 + for condition, statements in self: + lines.append(ConditionBlock.condition_source(condition, index)) + index += 1 + + for statement in statements: + lines.append(statement.to_source()) + + lines.append('endif') + + return '\n'.join(lines) + + def __eq__(self, other): + if not isinstance(other, ConditionBlock): + return False + + if len(self) != len(other): + return False + + for i in xrange(0, len(self)): + our_condition, our_statements = self[i] + other_condition, other_statements = other[i] + + if our_condition != other_condition: + return False + + if our_statements != other_statements: + return False + + return True + + @staticmethod + def condition_source(statement, index): + """Convert a condition to its source representation. + + The index argument defines the index of this condition inside a + ConditionBlock. If it is greater than 0, an "else" will be prepended + to the result, if necessary. + """ + prefix = '' + if isinstance(statement, (EqCondition, IfdefCondition)) and index > 0: + prefix = 'else ' + + if isinstance(statement, IfdefCondition): + s = statement.exp.s + + if statement.expected: + return '%sifdef %s' % (prefix, s) + + return '%sifndef %s' % (prefix, s) + + if isinstance(statement, EqCondition): + args = [ + statement.exp1.to_source(escape_comments=True), + statement.exp2.to_source(escape_comments=True)] + + use_quotes = False + single_quote_present = False + double_quote_present = False + for i, arg in enumerate(args): + if len(arg) > 0 and (arg[0].isspace() or arg[-1].isspace()): + use_quotes = True + + if "'" in arg: + single_quote_present = True + + if '"' in arg: + double_quote_present = True + + # Quote everything if needed. + if single_quote_present and double_quote_present: + raise Exception('Cannot format condition with multiple quotes.') + + if use_quotes: + for i, arg in enumerate(args): + # Double to single quotes. + if single_quote_present: + args[i] = '"' + arg + '"' + else: + args[i] = "'" + arg + "'" + + body = None + if use_quotes: + body = ' '.join(args) + else: + body = '(%s)' % ','.join(args) + + if statement.expected: + return '%sifeq %s' % (prefix, body) + + return '%sifneq %s' % (prefix, body) + + if isinstance(statement, ElseCondition): + return 'else' + + raise Exception('Unhandled Condition statement: %s' % + statement.__class__) + + def __iter__(self): + return iter(self._groups) + + def __len__(self): + return len(self._groups) + + def __getitem__(self, i): + return self._groups[i] + +class Include(Statement): + """ + Represents the include directive. + + See https://www.gnu.org/software/make/manual/make.html#Include + + The file to be included is represented by the Expansion defined in the + field `exp`. `required` is a bool indicating whether execution should fail + if the specified file could not be processed. + """ + __slots__ = ('exp', 'required', 'deps') + + def __init__(self, exp, required, weak): + assert isinstance(exp, (data.Expansion, data.StringExpansion)) + self.exp = exp + self.required = required + self.weak = weak + + def execute(self, makefile, context): + files = self.exp.resolvesplit(makefile, makefile.variables) + for f in files: + makefile.include(f, self.required, loc=self.exp.loc, weak=self.weak) + + def dump(self, fd, indent): + print >>fd, "%sInclude %s" % (indent, self.exp) + + def to_source(self): + prefix = '' + + if not self.required: + prefix = '-' + + return '%sinclude %s' % (prefix, self.exp.to_source()) + + def __eq__(self, other): + if not isinstance(other, Include): + return False + + return self.exp == other.exp and self.required == other.required + +class VPathDirective(Statement): + """ + Represents the vpath directive. + + See https://www.gnu.org/software/make/manual/make.html#Selective-Search + """ + __slots__ = ('exp',) + + def __init__(self, exp): + assert isinstance(exp, (data.Expansion, data.StringExpansion)) + self.exp = exp + + def execute(self, makefile, context): + words = list(data.stripdotslashes(self.exp.resolvesplit(makefile, makefile.variables))) + if len(words) == 0: + makefile.clearallvpaths() + else: + pattern = data.Pattern(words[0]) + mpaths = words[1:] + + if len(mpaths) == 0: + makefile.clearvpath(pattern) + else: + dirs = [] + for mpath in mpaths: + dirs.extend((dir for dir in mpath.split(os.pathsep) + if dir != '')) + if len(dirs): + makefile.addvpath(pattern, dirs) + + def dump(self, fd, indent): + print >>fd, "%sVPath %s" % (indent, self.exp) + + def to_source(self): + return 'vpath %s' % self.exp.to_source() + + def __eq__(self, other): + if not isinstance(other, VPathDirective): + return False + + return self.exp == other.exp + +class ExportDirective(Statement): + """ + Represents the "export" directive. + + This is used to control exporting variables to sub makes. + + See https://www.gnu.org/software/make/manual/make.html#Variables_002fRecursion + + The `concurrent_set` field defines whether this statement occurred with or + without a variable assignment. If False, no variable assignment was + present. If True, the SetVariable immediately following this statement + originally came from this export directive (the parser splits it into + multiple statements). + """ + + __slots__ = ('exp', 'concurrent_set') + + def __init__(self, exp, concurrent_set): + assert isinstance(exp, (data.Expansion, data.StringExpansion)) + self.exp = exp + self.concurrent_set = concurrent_set + + def execute(self, makefile, context): + if self.concurrent_set: + vlist = [self.exp.resolvestr(makefile, makefile.variables)] + else: + vlist = list(self.exp.resolvesplit(makefile, makefile.variables)) + if not len(vlist): + raise data.DataError("Exporting all variables is not supported", self.exp.loc) + + for v in vlist: + makefile.exportedvars[v] = True + + def dump(self, fd, indent): + print >>fd, "%sExport (single=%s) %s" % (indent, self.single, self.exp) + + def to_source(self): + return ('export %s' % self.exp.to_source()).rstrip() + + def __eq__(self, other): + if not isinstance(other, ExportDirective): + return False + + # single is irrelevant because it just says whether the next Statement + # contains a variable definition. + return self.exp == other.exp + +class UnexportDirective(Statement): + """ + Represents the "unexport" directive. + + This is the opposite of ExportDirective. + """ + __slots__ = ('exp',) + + def __init__(self, exp): + self.exp = exp + + def execute(self, makefile, context): + vlist = list(self.exp.resolvesplit(makefile, makefile.variables)) + for v in vlist: + makefile.exportedvars[v] = False + + def dump(self, fd, indent): + print >>fd, "%sUnexport %s" % (indent, self.exp) + + def to_source(self): + return 'unexport %s' % self.exp.to_source() + + def __eq__(self, other): + if not isinstance(other, UnexportDirective): + return False + + return self.exp == other.exp + +class EmptyDirective(Statement): + """ + Represents a standalone statement, usually an Expansion. + + You will encounter EmptyDirective instances if there is a function + or similar at the top-level of a make file (e.g. outside of a rule or + variable assignment). You can also find them as the bodies of + ConditionBlock branches. + """ + __slots__ = ('exp',) + + def __init__(self, exp): + assert isinstance(exp, (data.Expansion, data.StringExpansion)) + self.exp = exp + + def execute(self, makefile, context): + v = self.exp.resolvestr(makefile, makefile.variables) + if v.strip() != '': + raise data.DataError("Line expands to non-empty value", self.exp.loc) + + def dump(self, fd, indent): + print >>fd, "%sEmptyDirective: %s" % (indent, self.exp) + + def to_source(self): + return self.exp.to_source() + + def __eq__(self, other): + if not isinstance(other, EmptyDirective): + return False + + return self.exp == other.exp + +class _EvalContext(object): + __slots__ = ('currule', 'weak') + + def __init__(self, weak): + self.weak = weak + +class StatementList(list): + """ + A list of Statement instances. + + This is what is generated by the parser when a make file is parsed. + + Consumers can iterate over all Statement instances in this collection to + statically inspect (and even modify) make files before they are executed. + """ + __slots__ = ('mtime',) + + def append(self, statement): + assert isinstance(statement, Statement) + list.append(self, statement) + + def execute(self, makefile, context=None, weak=False): + if context is None: + context = _EvalContext(weak=weak) + + for s in self: + s.execute(makefile, context) + + def dump(self, fd, indent): + for s in self: + s.dump(fd, indent) + + def __str__(self): + fd = StringIO() + self.dump(fd, '') + return fd.getvalue() + + def to_source(self): + return '\n'.join([s.to_source() for s in self]) + +def iterstatements(stmts): + for s in stmts: + yield s + if isinstance(s, ConditionBlock): + for c, sl in s: + for s2 in iterstatments(sl): yield s2 diff --git a/build/pymake/pymake/process.py b/build/pymake/pymake/process.py new file mode 100644 index 000000000..7aaf65689 --- /dev/null +++ b/build/pymake/pymake/process.py @@ -0,0 +1,557 @@ +""" +Skipping shell invocations is good, when possible. This wrapper around subprocess does dirty work of +parsing command lines into argv and making sure that no shell magic is being used. +""" + +#TODO: ship pyprocessing? +import multiprocessing +import subprocess, shlex, re, logging, sys, traceback, os, imp, glob +from collections import deque +# XXXkhuey Work around http://bugs.python.org/issue1731717 +subprocess._cleanup = lambda: None +import command, util +if sys.platform=='win32': + import win32process + +_log = logging.getLogger('pymake.process') + +_escapednewlines = re.compile(r'\\\n') + +def tokens2re(tokens): + # Create a pattern for non-escaped tokens, in the form: + # (?<!\\)(?:a|b|c...) + # This is meant to match patterns a, b, or c, or ... if they are not + # preceded by a backslash. + # where a, b, c... are in the form + # (?P<name>pattern) + # which matches the pattern and captures it in a named match group. + # The group names and patterns come are given as a dict in the function + # argument. + nonescaped = r'(?<!\\)(?:%s)' % '|'.join('(?P<%s>%s)' % (name, value) for name, value in tokens.iteritems()) + # The final pattern matches either the above pattern, or an escaped + # backslash, captured in the "escape" match group. + return re.compile('(?:%s|%s)' % (nonescaped, r'(?P<escape>\\\\)')) + +_unquoted_tokens = tokens2re({ + 'whitespace': r'[\t\r\n ]', + 'quote': r'[\'"]', + 'comment': '#', + 'special': r'[<>&|`~(){}$;]', + 'backslashed': r'\\[^\\]', + 'glob': r'[\*\?]', +}) + +_doubly_quoted_tokens = tokens2re({ + 'quote': '"', + 'backslashedquote': r'\\"', + 'special': '\$', + 'backslashed': r'\\[^\\"]', +}) + +class MetaCharacterException(Exception): + def __init__(self, char): + self.char = char + +class ClineSplitter(list): + """ + Parses a given command line string and creates a list of command + and arguments, with wildcard expansion. + """ + def __init__(self, cline, cwd): + self.cwd = cwd + self.arg = '' + self.cline = cline + self.glob = False + self._parse_unquoted() + + def _push(self, str): + """ + Push the given string as part of the current argument + """ + self.arg += str + + def _next(self): + """ + Finalize current argument, effectively adding it to the list. + Perform globbing if needed. + """ + if not self.arg: + return + if self.glob: + if os.path.isabs(self.arg): + path = self.arg + else: + path = os.path.join(self.cwd, self.arg) + globbed = glob.glob(path) + if not globbed: + # If globbing doesn't find anything, the literal string is + # used. + self.append(self.arg) + else: + self.extend(f[len(path)-len(self.arg):] for f in globbed) + self.glob = False + else: + self.append(self.arg) + self.arg = '' + + def _parse_unquoted(self): + """ + Parse command line remainder in the context of an unquoted string. + """ + while self.cline: + # Find the next token + m = _unquoted_tokens.search(self.cline) + # If we find none, the remainder of the string can be pushed to + # the current argument and the argument finalized + if not m: + self._push(self.cline) + break + # The beginning of the string, up to the found token, is part of + # the current argument + self._push(self.cline[:m.start()]) + self.cline = self.cline[m.end():] + + match = dict([(name, value) for name, value in m.groupdict().items() if value]) + if 'quote' in match: + # " or ' start a quoted string + if match['quote'] == '"': + self._parse_doubly_quoted() + else: + self._parse_quoted() + elif 'comment' in match: + # Comments are ignored. The current argument can be finalized, + # and parsing stopped. + break + elif 'special' in match: + # Unquoted, non-escaped special characters need to be sent to a + # shell. + raise MetaCharacterException, match['special'] + elif 'whitespace' in match: + # Whitespaces terminate current argument. + self._next() + elif 'escape' in match: + # Escaped backslashes turn into a single backslash + self._push('\\') + elif 'backslashed' in match: + # Backslashed characters are unbackslashed + # e.g. echo \a -> a + self._push(match['backslashed'][1]) + elif 'glob' in match: + # ? or * will need globbing + self.glob = True + self._push(m.group(0)) + else: + raise Exception, "Shouldn't reach here" + self._next() + + def _parse_quoted(self): + # Single quoted strings are preserved, except for the final quote + index = self.cline.find("'") + if index == -1: + raise Exception, 'Unterminated quoted string in command' + self._push(self.cline[:index]) + self.cline = self.cline[index+1:] + + def _parse_doubly_quoted(self): + if not self.cline: + raise Exception, 'Unterminated quoted string in command' + while self.cline: + m = _doubly_quoted_tokens.search(self.cline) + if not m: + raise Exception, 'Unterminated quoted string in command' + self._push(self.cline[:m.start()]) + self.cline = self.cline[m.end():] + match = dict([(name, value) for name, value in m.groupdict().items() if value]) + if 'quote' in match: + # a double quote ends the quoted string, so go back to + # unquoted parsing + return + elif 'special' in match: + # Unquoted, non-escaped special characters in a doubly quoted + # string still have a special meaning and need to be sent to a + # shell. + raise MetaCharacterException, match['special'] + elif 'escape' in match: + # Escaped backslashes turn into a single backslash + self._push('\\') + elif 'backslashedquote' in match: + # Backslashed double quotes are un-backslashed + self._push('"') + elif 'backslashed' in match: + # Backslashed characters are kept backslashed + self._push(match['backslashed']) + +def clinetoargv(cline, cwd): + """ + If this command line can safely skip the shell, return an argv array. + @returns argv, badchar + """ + str = _escapednewlines.sub('', cline) + try: + args = ClineSplitter(str, cwd) + except MetaCharacterException, e: + return None, e.char + + if len(args) and args[0].find('=') != -1: + return None, '=' + + return args, None + +# shellwords contains a set of shell builtin commands that need to be +# executed within a shell. It also contains a set of commands that are known +# to be giving problems when run directly instead of through the msys shell. +shellwords = (':', '.', 'break', 'cd', 'continue', 'exec', 'exit', 'export', + 'getopts', 'hash', 'pwd', 'readonly', 'return', 'shift', + 'test', 'times', 'trap', 'umask', 'unset', 'alias', + 'set', 'bind', 'builtin', 'caller', 'command', 'declare', + 'echo', 'enable', 'help', 'let', 'local', 'logout', + 'printf', 'read', 'shopt', 'source', 'type', 'typeset', + 'ulimit', 'unalias', 'set', 'find') + +def prepare_command(cline, cwd, loc): + """ + Returns a list of command and arguments for the given command line string. + If the command needs to be run through a shell for some reason, the + returned list contains the shell invocation. + """ + + #TODO: call this once up-front somewhere and save the result? + shell, msys = util.checkmsyscompat() + + shellreason = None + executable = None + if msys and cline.startswith('/'): + shellreason = "command starts with /" + else: + argv, badchar = clinetoargv(cline, cwd) + if argv is None: + shellreason = "command contains shell-special character '%s'" % (badchar,) + elif len(argv) and argv[0] in shellwords: + shellreason = "command starts with shell primitive '%s'" % (argv[0],) + elif argv and (os.sep in argv[0] or os.altsep and os.altsep in argv[0]): + executable = util.normaljoin(cwd, argv[0]) + # Avoid "%1 is not a valid Win32 application" errors, assuming + # that if the executable path is to be resolved with PATH, it will + # be a Win32 executable. + if sys.platform == 'win32' and os.path.isfile(executable) and open(executable, 'rb').read(2) == "#!": + shellreason = "command executable starts with a hashbang" + + if shellreason is not None: + _log.debug("%s: using shell: %s: '%s'", loc, shellreason, cline) + if msys: + if len(cline) > 3 and cline[1] == ':' and cline[2] == '/': + cline = '/' + cline[0] + cline[2:] + argv = [shell, "-c", cline] + executable = None + + return executable, argv + +def call(cline, env, cwd, loc, cb, context, echo, justprint=False): + executable, argv = prepare_command(cline, cwd, loc) + + if not len(argv): + cb(res=0) + return + + if argv[0] == command.makepypath: + command.main(argv[1:], env, cwd, cb) + return + + if argv[0:2] == [sys.executable.replace('\\', '/'), + command.makepypath.replace('\\', '/')]: + command.main(argv[2:], env, cwd, cb) + return + + context.call(argv, executable=executable, shell=False, env=env, cwd=cwd, cb=cb, + echo=echo, justprint=justprint) + +def call_native(module, method, argv, env, cwd, loc, cb, context, echo, justprint=False, + pycommandpath=None): + context.call_native(module, method, argv, env=env, cwd=cwd, cb=cb, + echo=echo, justprint=justprint, pycommandpath=pycommandpath) + +def statustoresult(status): + """ + Convert the status returned from waitpid into a prettier numeric result. + """ + sig = status & 0xFF + if sig: + return -sig + + return status >>8 + +class Job(object): + """ + A single job to be executed on the process pool. + """ + done = False # set to true when the job completes + + def __init__(self): + self.exitcode = -127 + + def notify(self, condition, result): + condition.acquire() + self.done = True + self.exitcode = result + condition.notify() + condition.release() + + def get_callback(self, condition): + return lambda result: self.notify(condition, result) + +class PopenJob(Job): + """ + A job that executes a command using subprocess.Popen. + """ + def __init__(self, argv, executable, shell, env, cwd): + Job.__init__(self) + self.argv = argv + self.executable = executable + self.shell = shell + self.env = env + self.cwd = cwd + self.parentpid = os.getpid() + + def run(self): + assert os.getpid() != self.parentpid + # subprocess.Popen doesn't use the PATH set in the env argument for + # finding the executable on some platforms (but strangely it does on + # others!), so set os.environ['PATH'] explicitly. This is parallel- + # safe because pymake uses separate processes for parallelism, and + # each process is serial. See http://bugs.python.org/issue8557 for a + # general overview of "subprocess PATH semantics and portability". + oldpath = os.environ['PATH'] + try: + if self.env is not None and self.env.has_key('PATH'): + os.environ['PATH'] = self.env['PATH'] + p = subprocess.Popen(self.argv, executable=self.executable, shell=self.shell, env=self.env, cwd=self.cwd) + return p.wait() + except OSError, e: + print >>sys.stderr, e + return -127 + finally: + os.environ['PATH'] = oldpath + +class PythonException(Exception): + def __init__(self, message, exitcode): + Exception.__init__(self) + self.message = message + self.exitcode = exitcode + + def __str__(self): + return self.message + +def load_module_recursive(module, path): + """ + Emulate the behavior of __import__, but allow + passing a custom path to search for modules. + """ + bits = module.split('.') + oldsyspath = sys.path + for i, bit in enumerate(bits): + dotname = '.'.join(bits[:i+1]) + try: + f, path, desc = imp.find_module(bit, path) + # Add the directory the module was found in to sys.path + if path != '': + abspath = os.path.abspath(path) + if not os.path.isdir(abspath): + abspath = os.path.dirname(path) + sys.path = [abspath] + sys.path + m = imp.load_module(dotname, f, path, desc) + if f is None: + path = m.__path__ + except ImportError: + return + finally: + sys.path = oldsyspath + +class PythonJob(Job): + """ + A job that calls a Python method. + """ + def __init__(self, module, method, argv, env, cwd, pycommandpath=None): + self.module = module + self.method = method + self.argv = argv + self.env = env + self.cwd = cwd + self.pycommandpath = pycommandpath or [] + self.parentpid = os.getpid() + + def run(self): + assert os.getpid() != self.parentpid + # os.environ is a magic dictionary. Setting it to something else + # doesn't affect the environment of subprocesses, so use clear/update + oldenv = dict(os.environ) + try: + os.chdir(self.cwd) + os.environ.clear() + os.environ.update(self.env) + if self.module not in sys.modules: + load_module_recursive(self.module, + sys.path + self.pycommandpath) + if self.module not in sys.modules: + print >>sys.stderr, "No module named '%s'" % self.module + return -127 + m = sys.modules[self.module] + if self.method not in m.__dict__: + print >>sys.stderr, "No method named '%s' in module %s" % (self.method, self.module) + return -127 + rv = m.__dict__[self.method](self.argv) + if rv != 0 and rv is not None: + print >>sys.stderr, ( + "Native command '%s %s' returned value '%s'" % + (self.module, self.method, rv)) + return (rv if isinstance(rv, int) else 1) + + except PythonException, e: + print >>sys.stderr, e + return e.exitcode + except: + e = sys.exc_info()[1] + if isinstance(e, SystemExit) and (e.code == 0 or e.code is None): + pass # sys.exit(0) is not a failure + else: + print >>sys.stderr, e + traceback.print_exc() + return -127 + finally: + os.environ.clear() + os.environ.update(oldenv) + return 0 + +def job_runner(job): + """ + Run a job. Called in a Process pool. + """ + return job.run() + +class ParallelContext(object): + """ + Manages the parallel execution of processes. + """ + + _allcontexts = set() + _condition = multiprocessing.Condition() + + def __init__(self, jcount): + self.jcount = jcount + self.exit = False + + self.processpool = multiprocessing.Pool(processes=jcount) + self.pending = deque() # deque of (cb, args, kwargs) + self.running = [] # list of (subprocess, cb) + + self._allcontexts.add(self) + + def finish(self): + assert len(self.pending) == 0 and len(self.running) == 0, "pending: %i running: %i" % (len(self.pending), len(self.running)) + self.processpool.close() + self.processpool.join() + self._allcontexts.remove(self) + + def run(self): + while len(self.pending) and len(self.running) < self.jcount: + cb, args, kwargs = self.pending.popleft() + cb(*args, **kwargs) + + def defer(self, cb, *args, **kwargs): + assert self.jcount > 1 or not len(self.pending), "Serial execution error defering %r %r %r: currently pending %r" % (cb, args, kwargs, self.pending) + self.pending.append((cb, args, kwargs)) + + def _docall_generic(self, pool, job, cb, echo, justprint): + if echo is not None: + print echo + processcb = job.get_callback(ParallelContext._condition) + if justprint: + processcb(0) + else: + pool.apply_async(job_runner, args=(job,), callback=processcb) + self.running.append((job, cb)) + + def call(self, argv, shell, env, cwd, cb, echo, justprint=False, executable=None): + """ + Asynchronously call the process + """ + + job = PopenJob(argv, executable=executable, shell=shell, env=env, cwd=cwd) + self.defer(self._docall_generic, self.processpool, job, cb, echo, justprint) + + def call_native(self, module, method, argv, env, cwd, cb, + echo, justprint=False, pycommandpath=None): + """ + Asynchronously call the native function + """ + + job = PythonJob(module, method, argv, env, cwd, pycommandpath) + self.defer(self._docall_generic, self.processpool, job, cb, echo, justprint) + + @staticmethod + def _waitany(condition): + def _checkdone(): + jobs = [] + for c in ParallelContext._allcontexts: + for i in xrange(0, len(c.running)): + if c.running[i][0].done: + jobs.append(c.running[i]) + for j in jobs: + if j in c.running: + c.running.remove(j) + return jobs + + # We must acquire the lock, and then check to see if any jobs have + # finished. If we don't check after acquiring the lock it's possible + # that all outstanding jobs will have completed before we wait and we'll + # wait for notifications that have already occurred. + condition.acquire() + jobs = _checkdone() + + if jobs == []: + condition.wait() + jobs = _checkdone() + + condition.release() + + return jobs + + @staticmethod + def spin(): + """ + Spin the 'event loop', and never return. + """ + + while True: + clist = list(ParallelContext._allcontexts) + for c in clist: + c.run() + + dowait = util.any((len(c.running) for c in ParallelContext._allcontexts)) + if dowait: + # Wait on local jobs first for perf + for job, cb in ParallelContext._waitany(ParallelContext._condition): + cb(job.exitcode) + else: + assert any(len(c.pending) for c in ParallelContext._allcontexts) + +def makedeferrable(usercb, **userkwargs): + def cb(*args, **kwargs): + kwargs.update(userkwargs) + return usercb(*args, **kwargs) + + return cb + +_serialContext = None +_parallelContext = None + +def getcontext(jcount): + global _serialContext, _parallelContext + if jcount == 1: + if _serialContext is None: + _serialContext = ParallelContext(1) + return _serialContext + else: + if _parallelContext is None: + _parallelContext = ParallelContext(jcount) + return _parallelContext + diff --git a/build/pymake/pymake/util.py b/build/pymake/pymake/util.py new file mode 100644 index 000000000..c63f930cc --- /dev/null +++ b/build/pymake/pymake/util.py @@ -0,0 +1,150 @@ +import os + +class MakeError(Exception): + def __init__(self, message, loc=None): + self.msg = message + self.loc = loc + + def __str__(self): + locstr = '' + if self.loc is not None: + locstr = "%s:" % (self.loc,) + + return "%s%s" % (locstr, self.msg) + +def normaljoin(path, suffix): + """ + Combine the given path with the suffix, and normalize if necessary to shrink the path to avoid hitting path length limits + """ + result = os.path.join(path, suffix) + if len(result) > 255: + result = os.path.normpath(result) + return result + +def joiniter(fd, it): + """ + Given an iterator that returns strings, write the words with a space in between each. + """ + + it = iter(it) + for i in it: + fd.write(i) + break + + for i in it: + fd.write(' ') + fd.write(i) + +def checkmsyscompat(): + """For msys compatibility on windows, honor the SHELL environment variable, + and if $MSYSTEM == MINGW32, run commands through $SHELL -c instead of + letting Python use the system shell.""" + if 'SHELL' in os.environ: + shell = os.environ['SHELL'] + elif 'MOZILLABUILD' in os.environ: + shell = os.environ['MOZILLABUILD'] + '/msys/bin/sh.exe' + elif 'COMSPEC' in os.environ: + shell = os.environ['COMSPEC'] + else: + raise DataError("Can't find a suitable shell!") + + msys = False + if 'MSYSTEM' in os.environ and os.environ['MSYSTEM'] == 'MINGW32': + msys = True + if not shell.lower().endswith(".exe"): + shell += ".exe" + return (shell, msys) + +if hasattr(str, 'partition'): + def strpartition(str, token): + return str.partition(token) + + def strrpartition(str, token): + return str.rpartition(token) + +else: + def strpartition(str, token): + """Python 2.4 compatible str.partition""" + + offset = str.find(token) + if offset == -1: + return str, '', '' + + return str[:offset], token, str[offset + len(token):] + + def strrpartition(str, token): + """Python 2.4 compatible str.rpartition""" + + offset = str.rfind(token) + if offset == -1: + return '', '', str + + return str[:offset], token, str[offset + len(token):] + +try: + from __builtin__ import any +except ImportError: + def any(it): + for i in it: + if i: + return True + return False + +class _MostUsedItem(object): + __slots__ = ('key', 'o', 'count') + + def __init__(self, key): + self.key = key + self.o = None + self.count = 1 + + def __repr__(self): + return "MostUsedItem(key=%r, count=%i, o=%r)" % (self.key, self.count, self.o) + +class MostUsedCache(object): + def __init__(self, capacity, creationfunc, verifyfunc): + self.capacity = capacity + self.cfunc = creationfunc + self.vfunc = verifyfunc + + self.d = {} + self.active = [] # lazily sorted! + + def setactive(self, item): + if item in self.active: + return + + if len(self.active) == self.capacity: + self.active.sort(key=lambda i: i.count) + old = self.active.pop(0) + old.o = None + # print "Evicting %s" % old.key + + self.active.append(item) + + def get(self, key): + item = self.d.get(key, None) + if item is None: + item = _MostUsedItem(key) + self.d[key] = item + else: + item.count += 1 + + if item.o is not None and self.vfunc(key, item.o): + return item.o + + item.o = self.cfunc(key) + self.setactive(item) + return item.o + + def verify(self): + for k, v in self.d.iteritems(): + if v.o: + assert v in self.active + else: + assert v not in self.active + + def debugitems(self): + l = [i.key for i in self.active] + l.sort() + return l diff --git a/build/pymake/pymake/win32process.py b/build/pymake/pymake/win32process.py new file mode 100644 index 000000000..880a26a5b --- /dev/null +++ b/build/pymake/pymake/win32process.py @@ -0,0 +1,28 @@ +from ctypes import windll, POINTER, byref, WinError +from ctypes.wintypes import WINFUNCTYPE, HANDLE, DWORD, BOOL + +INFINITE = -1 +WAIT_FAILED = 0xFFFFFFFF + +LPDWORD = POINTER(DWORD) +_GetExitCodeProcessProto = WINFUNCTYPE(BOOL, HANDLE, LPDWORD) +_GetExitCodeProcess = _GetExitCodeProcessProto(("GetExitCodeProcess", windll.kernel32)) +def GetExitCodeProcess(h): + exitcode = DWORD() + r = _GetExitCodeProcess(h, byref(exitcode)) + if r is 0: + raise WinError() + return exitcode.value + +_WaitForMultipleObjectsProto = WINFUNCTYPE(DWORD, DWORD, POINTER(HANDLE), BOOL, DWORD) +_WaitForMultipleObjects = _WaitForMultipleObjectsProto(("WaitForMultipleObjects", windll.kernel32)) + +def WaitForAnyProcess(processes): + arrtype = HANDLE * len(processes) + harray = arrtype(*(int(p._handle) for p in processes)) + + r = _WaitForMultipleObjects(len(processes), harray, False, INFINITE) + if r == WAIT_FAILED: + raise WinError() + + return processes[r], GetExitCodeProcess(int(processes[r]._handle)) <<8 diff --git a/build/pymake/tests/automatic-variables.mk b/build/pymake/tests/automatic-variables.mk new file mode 100644 index 000000000..5302c08ea --- /dev/null +++ b/build/pymake/tests/automatic-variables.mk @@ -0,0 +1,79 @@ +$(shell \ +mkdir -p src/subd; \ +mkdir subd; \ +touch dummy; \ +sleep 2; \ +touch subd/test.out src/subd/test.in2; \ +sleep 2; \ +touch subd/test.out2 src/subd/test.in; \ +sleep 2; \ +touch subd/host_test.out subd/host_test.out2; \ +sleep 2; \ +touch host_prog; \ +) + +VPATH = src + +all: prog host_prog prog dir/ + test "$@" = "all" + test "$<" = "prog" + test "$^" = "prog host_prog dir" + test "$?" = "prog host_prog dir" + test "$+" = "prog host_prog prog dir" + test "$(@D)" = "." + test "$(@F)" = "all" + test "$(<D)" = "." + test "$(<F)" = "prog" + test "$(^D)" = ". . ." + test "$(^F)" = "prog host_prog dir" + test "$(?D)" = ". . ." + test "$(?F)" = "prog host_prog dir" + test "$(+D)" = ". . . ." + test "$(+F)" = "prog host_prog prog dir" + @echo TEST-PASS + +dir/: + test "$@" = "dir" + test "$<" = "" + test "$^" = "" + test "$(@D)" = "." + test "$(@F)" = "dir" + mkdir $@ + +prog: subd/test.out subd/test.out2 + test "$@" = "prog" + test "$<" = "subd/test.out" + test "$^" = "subd/test.out subd/test.out2" # ^ + test "$?" = "subd/test.out subd/test.out2" # ? + cat $< + test "$$(cat $<)" = "remade" + test "$$(cat $(word 2,$^))" = "" + +host_prog: subd/host_test.out subd/host_test.out2 + @echo TEST-FAIL No need to remake + +%.out: %.in dummy + test "$@" = "subd/test.out" + test "$*" = "subd/test" # * + test "$<" = "src/subd/test.in" # < + test "$^" = "src/subd/test.in dummy" # ^ + test "$?" = "src/subd/test.in" # ? + test "$+" = "src/subd/test.in dummy" # + + test "$(@D)" = "subd" + test "$(@F)" = "test.out" + test "$(*D)" = "subd" + test "$(*F)" = "test" + test "$(<D)" = "src/subd" + test "$(<F)" = "test.in" + test "$(^D)" = "src/subd ." # ^D + test "$(^F)" = "test.in dummy" + test "$(?D)" = "src/subd" + test "$(?F)" = "test.in" + test "$(+D)" = "src/subd ." # +D + test "$(+F)" = "test.in dummy" + printf "remade" >$@ + +%.out2: %.in2 dummy + @echo TEST_FAIL No need to remake + +.PHONY: all diff --git a/build/pymake/tests/bad-command-continuation.mk b/build/pymake/tests/bad-command-continuation.mk new file mode 100644 index 000000000..d9ceccfc2 --- /dev/null +++ b/build/pymake/tests/bad-command-continuation.mk @@ -0,0 +1,3 @@ +all: + echo 'hello'\ +TEST-PASS diff --git a/build/pymake/tests/call.mk b/build/pymake/tests/call.mk new file mode 100644 index 000000000..9eeb7e00c --- /dev/null +++ b/build/pymake/tests/call.mk @@ -0,0 +1,12 @@ +test = $0 +reverse = $2 $1 +twice = $1$1 +sideeffect = $(shell echo "called$1:" >>dummyfile) + +all: + test "$(call test)" = "test" + test "$(call reverse,1,2)" = "2 1" +# expansion happens *before* substitution, thank sanity + test "$(call twice,$(sideeffect))" = "" + test `cat dummyfile` = "called:" + @echo TEST-PASS diff --git a/build/pymake/tests/cmdgoals.mk b/build/pymake/tests/cmdgoals.mk new file mode 100644 index 000000000..a3b25e751 --- /dev/null +++ b/build/pymake/tests/cmdgoals.mk @@ -0,0 +1,9 @@ +default: + test "$(MAKECMDGOALS)" = "" + $(MAKE) -f $(TESTPATH)/cmdgoals.mk t1 t2 + @echo TEST-PASS + +t1: + test "$(MAKECMDGOALS)" = "t1 t2" + +t2: diff --git a/build/pymake/tests/commandmodifiers.mk b/build/pymake/tests/commandmodifiers.mk new file mode 100644 index 000000000..8440462f3 --- /dev/null +++ b/build/pymake/tests/commandmodifiers.mk @@ -0,0 +1,21 @@ +define COMMAND +$(1) + $(1) + +endef + +all: + $(call COMMAND,@true #TEST-FAIL) + $(call COMMAND,-exit 4) + $(call COMMAND,@-exit 1 # TEST-FAIL) + $(call COMMAND,-@exit 1 # TEST-FAIL) + $(call COMMAND,+exit 0) + $(call COMMAND,+-exit 1) + $(call COMMAND,@+exit 0 # TEST-FAIL) + $(call COMMAND,+@exit 0 # TEST-FAIL) + $(call COMMAND,-+@exit 1 # TEST-FAIL) + $(call COMMAND,+-@exit 1 # TEST-FAIL) + $(call COMMAND,@+-exit 1 # TEST-FAIL) + $(call COMMAND,@+-@+-exit 1 # TEST-FAIL) + $(call COMMAND,@@++exit 0 # TEST-FAIL) + @echo TEST-PASS diff --git a/build/pymake/tests/comment-parsing.mk b/build/pymake/tests/comment-parsing.mk new file mode 100644 index 000000000..d469e1aea --- /dev/null +++ b/build/pymake/tests/comment-parsing.mk @@ -0,0 +1,29 @@ +# where do comments take effect? + +VAR = val1 # comment +VAR2 = lit2\#hash +VAR2_1 = lit2.1\\\#hash +VAR3 = val3 +VAR4 = lit4\\#backslash +VAR4_1 = lit4\\\\#backslash +VAR5 = lit5\char +VAR6 = lit6\\char +VAR7 = lit7\\ +VAR8 = lit8\\\\ +VAR9 = lit9\\\\extra +# This comment extends to the next line \ +VAR3 = ignored + +all: + test "$(VAR)" = "val1 " + test "$(VAR2)" = "lit2#hash" + test '$(VAR2_1)' = 'lit2.1\#hash' + test "$(VAR3)" = "val3" + test '$(VAR4)' = 'lit4\' + test '$(VAR4_1)' = 'lit4\\' + test '$(VAR5)' = 'lit5\char' + test '$(VAR6)' = 'lit6\\char' + test '$(VAR7)' = 'lit7\\' + test '$(VAR8)' = 'lit8\\\\' + test '$(VAR9)' = 'lit9\\\\extra' + @echo "TEST-PASS" diff --git a/build/pymake/tests/continuations-in-functions.mk b/build/pymake/tests/continuations-in-functions.mk new file mode 100644 index 000000000..533df6176 --- /dev/null +++ b/build/pymake/tests/continuations-in-functions.mk @@ -0,0 +1,6 @@ +all: + test 'Hello world.' = '$(if 1,Hello \ + world.)' + test '(Hello world.)' != '(Hello \ + world.)' + @echo TEST-PASS diff --git a/build/pymake/tests/datatests.py b/build/pymake/tests/datatests.py new file mode 100644 index 000000000..513028b0b --- /dev/null +++ b/build/pymake/tests/datatests.py @@ -0,0 +1,237 @@ +import pymake.data, pymake.functions, pymake.util +import unittest +import re +from cStringIO import StringIO + +def multitest(cls): + for name in cls.testdata.iterkeys(): + def m(self, name=name): + return self.runSingle(*self.testdata[name]) + + setattr(cls, 'test_%s' % name, m) + return cls + +class SplitWordsTest(unittest.TestCase): + testdata = ( + (' test test.c test.o ', ['test', 'test.c', 'test.o']), + ('\ttest\t test.c \ntest.o', ['test', 'test.c', 'test.o']), + ) + + def runTest(self): + for s, e in self.testdata: + w = s.split() + self.assertEqual(w, e, 'splitwords(%r)' % (s,)) + +class GetPatSubstTest(unittest.TestCase): + testdata = ( + ('%.c', '%.o', ' test test.c test.o ', 'test test.o test.o'), + ('%', '%.o', ' test.c test.o ', 'test.c.o test.o.o'), + ('foo', 'bar', 'test foo bar', 'test bar bar'), + ('foo', '%bar', 'test foo bar', 'test %bar bar'), + ('%', 'perc_%', 'path', 'perc_path'), + ('\\%', 'sub%', 'p %', 'p sub%'), + ('%.c', '\\%%.o', 'foo.c bar.o baz.cpp', '%foo.o bar.o baz.cpp'), + ) + + def runTest(self): + for s, r, d, e in self.testdata: + words = d.split() + p = pymake.data.Pattern(s) + a = ' '.join((p.subst(r, word, False) + for word in words)) + self.assertEqual(a, e, 'Pattern(%r).subst(%r, %r)' % (s, r, d)) + +class LRUTest(unittest.TestCase): + # getkey, expected, funccount, debugitems + expected = ( + (0, '', 1, (0,)), + (0, '', 2, (0,)), + (1, ' ', 3, (1, 0)), + (1, ' ', 3, (1, 0)), + (0, '', 4, (0, 1)), + (2, ' ', 5, (2, 0, 1)), + (1, ' ', 5, (1, 2, 0)), + (3, ' ', 6, (3, 1, 2)), + ) + + def spaceFunc(self, l): + self.funccount += 1 + return ''.ljust(l) + + def runTest(self): + self.funccount = 0 + c = pymake.util.LRUCache(3, self.spaceFunc, lambda k, v: k % 2) + self.assertEqual(tuple(c.debugitems()), ()) + + for i in xrange(0, len(self.expected)): + k, e, fc, di = self.expected[i] + + v = c.get(k) + self.assertEqual(v, e) + self.assertEqual(self.funccount, fc, + "funccount, iteration %i, got %i expected %i" % (i, self.funccount, fc)) + goti = tuple(c.debugitems()) + self.assertEqual(goti, di, + "debugitems, iteration %i, got %r expected %r" % (i, goti, di)) + +class EqualityTest(unittest.TestCase): + def test_string_expansion(self): + s1 = pymake.data.StringExpansion('foo bar', None) + s2 = pymake.data.StringExpansion('foo bar', None) + + self.assertEqual(s1, s2) + + def test_expansion_simple(self): + s1 = pymake.data.Expansion(None) + s2 = pymake.data.Expansion(None) + + self.assertEqual(s1, s2) + + s1.appendstr('foo') + s2.appendstr('foo') + self.assertEqual(s1, s2) + + def test_expansion_string_finish(self): + """Adjacent strings should normalize to same value.""" + s1 = pymake.data.Expansion(None) + s2 = pymake.data.Expansion(None) + + s1.appendstr('foo') + s2.appendstr('foo') + + s1.appendstr(' bar') + s1.appendstr(' baz') + s2.appendstr(' bar baz') + + self.assertEqual(s1, s2) + + def test_function(self): + s1 = pymake.data.Expansion(None) + s2 = pymake.data.Expansion(None) + + n1 = pymake.data.StringExpansion('FOO', None) + n2 = pymake.data.StringExpansion('FOO', None) + + v1 = pymake.functions.VariableRef(None, n1) + v2 = pymake.functions.VariableRef(None, n2) + + s1.appendfunc(v1) + s2.appendfunc(v2) + + self.assertEqual(s1, s2) + + +class StringExpansionTest(unittest.TestCase): + def test_base_expansion_interface(self): + s1 = pymake.data.StringExpansion('FOO', None) + + self.assertTrue(s1.is_static_string) + + funcs = list(s1.functions()) + self.assertEqual(len(funcs), 0) + + funcs = list(s1.functions(True)) + self.assertEqual(len(funcs), 0) + + refs = list(s1.variable_references()) + self.assertEqual(len(refs), 0) + + +class ExpansionTest(unittest.TestCase): + def test_is_static_string(self): + e1 = pymake.data.Expansion() + e1.appendstr('foo') + + self.assertTrue(e1.is_static_string) + + e1.appendstr('bar') + self.assertTrue(e1.is_static_string) + + vname = pymake.data.StringExpansion('FOO', None) + func = pymake.functions.VariableRef(None, vname) + + e1.appendfunc(func) + + self.assertFalse(e1.is_static_string) + + def test_get_functions(self): + e1 = pymake.data.Expansion() + e1.appendstr('foo') + + vname1 = pymake.data.StringExpansion('FOO', None) + vname2 = pymake.data.StringExpansion('BAR', None) + + func1 = pymake.functions.VariableRef(None, vname1) + func2 = pymake.functions.VariableRef(None, vname2) + + e1.appendfunc(func1) + e1.appendfunc(func2) + + funcs = list(e1.functions()) + self.assertEqual(len(funcs), 2) + + func3 = pymake.functions.SortFunction(None) + func3.append(vname1) + + e1.appendfunc(func3) + + funcs = list(e1.functions()) + self.assertEqual(len(funcs), 3) + + refs = list(e1.variable_references()) + self.assertEqual(len(refs), 2) + + def test_get_functions_descend(self): + e1 = pymake.data.Expansion() + vname1 = pymake.data.StringExpansion('FOO', None) + func1 = pymake.functions.VariableRef(None, vname1) + e2 = pymake.data.Expansion() + e2.appendfunc(func1) + + func2 = pymake.functions.SortFunction(None) + func2.append(e2) + + e1.appendfunc(func2) + + funcs = list(e1.functions()) + self.assertEqual(len(funcs), 1) + + funcs = list(e1.functions(True)) + self.assertEqual(len(funcs), 2) + + self.assertTrue(isinstance(funcs[0], pymake.functions.SortFunction)) + + def test_is_filesystem_dependent(self): + e = pymake.data.Expansion() + vname1 = pymake.data.StringExpansion('FOO', None) + func1 = pymake.functions.VariableRef(None, vname1) + e.appendfunc(func1) + + self.assertFalse(e.is_filesystem_dependent) + + func2 = pymake.functions.WildcardFunction(None) + func2.append(vname1) + e.appendfunc(func2) + + self.assertTrue(e.is_filesystem_dependent) + + def test_is_filesystem_dependent_descend(self): + sort = pymake.functions.SortFunction(None) + wildcard = pymake.functions.WildcardFunction(None) + + e = pymake.data.StringExpansion('foo/*', None) + wildcard.append(e) + + e = pymake.data.Expansion(None) + e.appendfunc(wildcard) + + sort.append(e) + + e = pymake.data.Expansion(None) + e.appendfunc(sort) + + self.assertTrue(e.is_filesystem_dependent) + + +if __name__ == '__main__': + unittest.main() diff --git a/build/pymake/tests/default-goal-set-first.mk b/build/pymake/tests/default-goal-set-first.mk new file mode 100644 index 000000000..00a5b53a2 --- /dev/null +++ b/build/pymake/tests/default-goal-set-first.mk @@ -0,0 +1,7 @@ +.DEFAULT_GOAL := default + +not-default: + @echo TEST-FAIL did not run default rule + +default: + @echo TEST-PASS diff --git a/build/pymake/tests/default-goal.mk b/build/pymake/tests/default-goal.mk new file mode 100644 index 000000000..699d6c0cd --- /dev/null +++ b/build/pymake/tests/default-goal.mk @@ -0,0 +1,8 @@ +not-default: + @echo TEST-FAIL did not run default rule + +default: + @echo $(if $(filter not-default,$(INTERMEDIATE_DEFAULT_GOAL)),TEST-PASS,TEST-FAIL .DEFAULT_GOAL not set by $(MAKE)) + +INTERMEDIATE_DEFAULT_GOAL := $(.DEFAULT_GOAL) +.DEFAULT_GOAL := default diff --git a/build/pymake/tests/default-target.mk b/build/pymake/tests/default-target.mk new file mode 100644 index 000000000..701ac6916 --- /dev/null +++ b/build/pymake/tests/default-target.mk @@ -0,0 +1,14 @@ +test: VAR = value + +%.do: + @echo TEST-FAIL: ran target "$@", should have run "all" + +.PHONY: test + +all: + @echo TEST-PASS: the default target is all + +test: + @echo TEST-FAIL: ran target "$@", should have run "all" + +test.do: diff --git a/build/pymake/tests/default-target2.mk b/build/pymake/tests/default-target2.mk new file mode 100644 index 000000000..b5a4b1bbf --- /dev/null +++ b/build/pymake/tests/default-target2.mk @@ -0,0 +1,6 @@ +test.foo: %.foo: + test "$@" = "test.foo" + @echo TEST-PASS made test.foo by default + +all: + @echo TEST-FAIL made $@, should have made test.foo diff --git a/build/pymake/tests/define-directive.mk b/build/pymake/tests/define-directive.mk new file mode 100644 index 000000000..789988666 --- /dev/null +++ b/build/pymake/tests/define-directive.mk @@ -0,0 +1,69 @@ +define COMMANDS +shellvar=hello +test "$$shellvar" != "hello" +endef + +define COMMANDS2 +shellvar=hello; \ + test "$$shellvar" = "hello" +endef + +define VARWITHCOMMENT # comment +value +endef + +define TEST3 + whitespace +endef + +define TEST4 +define TEST5 +random +endef + endef + +ifdef TEST5 +$(error TEST5 should not be set) +endif + +define TEST6 + define TEST7 +random +endef +endef + +ifdef TEST7 +$(error TEST7 should not be set) +endif + +define TEST8 +is this # a comment? +endef + +ifneq ($(TEST8),is this \# a comment?) +$(error TEST8 value not expected: $(TEST8)) +endif + +# A backslash continuation "hides" the endef +define TEST9 +value \ +endef +endef + +# Test ridiculous spacing + define TEST10 + define TEST11 + baz +endef +define TEST12 + foo + endef + endef + +all: + $(COMMANDS) + $(COMMANDS2) + test '$(VARWITHCOMMENT)' = 'value' + test '$(COMMANDS2)' = 'shellvar=hello; test "$$shellvar" = "hello"' + test "$(TEST3)" = " whitespace" + @echo TEST-PASS diff --git a/build/pymake/tests/depfailed.mk b/build/pymake/tests/depfailed.mk new file mode 100644 index 000000000..ce4137c38 --- /dev/null +++ b/build/pymake/tests/depfailed.mk @@ -0,0 +1,4 @@ +#T returncode: 2 + +all: foo.out foo.in + @echo TEST-PASS diff --git a/build/pymake/tests/depfailedj.mk b/build/pymake/tests/depfailedj.mk new file mode 100644 index 000000000..a94c74f6f --- /dev/null +++ b/build/pymake/tests/depfailedj.mk @@ -0,0 +1,10 @@ +#T returncode: 2 +#T commandline: ['-j4'] + +$(shell touch foo.in) + +all: foo.in foo.out missing + @echo TEST-PASS + +%.out: %.in + cp $< $@ diff --git a/build/pymake/tests/diamond-deps.mk b/build/pymake/tests/diamond-deps.mk new file mode 100644 index 000000000..40a4176d9 --- /dev/null +++ b/build/pymake/tests/diamond-deps.mk @@ -0,0 +1,13 @@ +# If the dependency graph includes a diamond dependency, we should only remake +# once! + +all: depA depB + cat testfile + test `cat testfile` = "data"; + @echo TEST-PASS + +depA: testfile +depB: testfile + +testfile: + printf "data" >>$@ diff --git a/build/pymake/tests/dotslash-dir.mk b/build/pymake/tests/dotslash-dir.mk new file mode 100644 index 000000000..8b30d1e3c --- /dev/null +++ b/build/pymake/tests/dotslash-dir.mk @@ -0,0 +1,8 @@ +#T grep-for: "dotslash-built" +.PHONY: $(dir foo) + +all: $(dir foo) + @echo TEST-PASS + +$(dir foo): + @echo dotslash-built diff --git a/build/pymake/tests/dotslash-parse.mk b/build/pymake/tests/dotslash-parse.mk new file mode 100644 index 000000000..91461bedb --- /dev/null +++ b/build/pymake/tests/dotslash-parse.mk @@ -0,0 +1,4 @@ +./: + +# This is merely a test to see that pymake doesn't choke on parsing ./ +$(info TEST-PASS) diff --git a/build/pymake/tests/dotslash-phony.mk b/build/pymake/tests/dotslash-phony.mk new file mode 100644 index 000000000..06b6ae78d --- /dev/null +++ b/build/pymake/tests/dotslash-phony.mk @@ -0,0 +1,3 @@ +.PHONY: ./ +./: + @echo TEST-PASS diff --git a/build/pymake/tests/dotslash.mk b/build/pymake/tests/dotslash.mk new file mode 100644 index 000000000..585db96b7 --- /dev/null +++ b/build/pymake/tests/dotslash.mk @@ -0,0 +1,9 @@ +$(shell touch foo.in) + +all: foo.out + test "$(wildcard ./*.in)" = "./foo.in" + @echo TEST-PASS + +./%.out: %.in + test "$@" = "foo.out" + cp $< $@ diff --git a/build/pymake/tests/doublecolon-exists.mk b/build/pymake/tests/doublecolon-exists.mk new file mode 100644 index 000000000..5d99a1f6b --- /dev/null +++ b/build/pymake/tests/doublecolon-exists.mk @@ -0,0 +1,16 @@ +$(shell touch foo.testfile1 foo.testfile2) + +# when a rule has commands and no prerequisites, should it be executed? +# double-colon: yes +# single-colon: no + +all: foo.testfile1 foo.testfile2 + test "$$(cat foo.testfile1)" = "" + test "$$(cat foo.testfile2)" = "remade:foo.testfile2" + @echo TEST-PASS + +foo.testfile1: + @echo TEST-FAIL + +foo.testfile2:: + printf "remade:$@"> $@ diff --git a/build/pymake/tests/doublecolon-priordeps.mk b/build/pymake/tests/doublecolon-priordeps.mk new file mode 100644 index 000000000..6cdf3a8e7 --- /dev/null +++ b/build/pymake/tests/doublecolon-priordeps.mk @@ -0,0 +1,19 @@ +#T commandline: ['-j3'] + +# All *prior* dependencies of a doublecolon rule must be satisfied before +# subsequent commands are run. + +all:: target1 + +all:: target2 + test -f target1 + @echo TEST-PASS + +target1: + touch starting-$@ + sleep 1 + touch $@ + +target2: + sleep 0.1 + test -f starting-target1 diff --git a/build/pymake/tests/doublecolon-remake.mk b/build/pymake/tests/doublecolon-remake.mk new file mode 100644 index 000000000..52aa9265c --- /dev/null +++ b/build/pymake/tests/doublecolon-remake.mk @@ -0,0 +1,4 @@ +$(shell touch somefile) + +all:: somefile + @echo TEST-PASS diff --git a/build/pymake/tests/dynamic-var.mk b/build/pymake/tests/dynamic-var.mk new file mode 100644 index 000000000..0993b9ccf --- /dev/null +++ b/build/pymake/tests/dynamic-var.mk @@ -0,0 +1,18 @@ +# The *name* of variables can be constructed dynamically. + +VARNAME = FOOBAR + +$(VARNAME) = foovalue +$(VARNAME)2 = foo2value + +$(VARNAME:%BAR=%BAM) = foobam + +all: + test "$(FOOBAR)" = "foovalue" + test "$(flavor FOOBAZ)" = "undefined" + test "$(FOOBAR2)" = "bazvalue" + test "$(FOOBAM)" = "foobam" + @echo TEST-PASS + +VARNAME = FOOBAZ +FOOBAR2 = bazvalue diff --git a/build/pymake/tests/empty-command-semicolon.mk b/build/pymake/tests/empty-command-semicolon.mk new file mode 100644 index 000000000..07789f3f1 --- /dev/null +++ b/build/pymake/tests/empty-command-semicolon.mk @@ -0,0 +1,5 @@ +all: + @echo TEST-PASS + +foo: ; + diff --git a/build/pymake/tests/empty-with-deps.mk b/build/pymake/tests/empty-with-deps.mk new file mode 100644 index 000000000..284e5a113 --- /dev/null +++ b/build/pymake/tests/empty-with-deps.mk @@ -0,0 +1,4 @@ +default.test: default.c + +default.c: + @echo TEST-PASS diff --git a/build/pymake/tests/env-var-append.mk b/build/pymake/tests/env-var-append.mk new file mode 100644 index 000000000..4db39c45f --- /dev/null +++ b/build/pymake/tests/env-var-append.mk @@ -0,0 +1,7 @@ +#T environment: {'FOO': 'TEST'} + +FOO += $(BAR) +BAR := PASS + +all: + @echo $(subst $(NULL) ,-,$(FOO)) diff --git a/build/pymake/tests/env-var-append2.mk b/build/pymake/tests/env-var-append2.mk new file mode 100644 index 000000000..fc0735d88 --- /dev/null +++ b/build/pymake/tests/env-var-append2.mk @@ -0,0 +1,8 @@ +#T environment: {'FOO': '$(BAZ)'} + +FOO += $(BAR) +BAR := PASS +BAZ := TEST + +all: + @echo $(subst $(NULL) ,-,$(FOO)) diff --git a/build/pymake/tests/eof-continuation.mk b/build/pymake/tests/eof-continuation.mk new file mode 100644 index 000000000..daeaabc3e --- /dev/null +++ b/build/pymake/tests/eof-continuation.mk @@ -0,0 +1,5 @@ +all: + test '$(TESTVAR)' = 'testval\' + @echo TEST-PASS + +TESTVAR = testval\
\ No newline at end of file diff --git a/build/pymake/tests/escape-chars.mk b/build/pymake/tests/escape-chars.mk new file mode 100644 index 000000000..ebea33074 --- /dev/null +++ b/build/pymake/tests/escape-chars.mk @@ -0,0 +1,26 @@ +space = $(NULL) $(NULL) +hello$(space)world$(space) = hellovalue + +A = aval + +VAR = value1\\ +VARAWFUL = value1\\#comment +VAR2 = value2 +VAR3 = test\$A +VAR4 = value4\\value5 + +VAR5 = value1\\ \ \ + value2 + +EPERCENT = \% + +all: + test "$(hello world )" = "hellovalue" + test "$(VAR)" = "value1\\" + test '$(VARAWFUL)' = 'value1\' + test "$(VAR2)" = "value2" + test "$(VAR3)" = "test\aval" + test "$(VAR4)" = "value4\\value5" + test "$(VAR5)" = "value1\\ \ value2" + test "$(EPERCENT)" = "\%" + @echo TEST-PASS diff --git a/build/pymake/tests/escaped-continuation.mk b/build/pymake/tests/escaped-continuation.mk new file mode 100644 index 000000000..537f7547f --- /dev/null +++ b/build/pymake/tests/escaped-continuation.mk @@ -0,0 +1,6 @@ +#T returncode: 2 + +all: + echo "Hello" \\ + test "world" = "not!" + @echo TEST-PASS diff --git a/build/pymake/tests/eval-duringexecute.mk b/build/pymake/tests/eval-duringexecute.mk new file mode 100644 index 000000000..dff848032 --- /dev/null +++ b/build/pymake/tests/eval-duringexecute.mk @@ -0,0 +1,12 @@ +#T returncode: 2 + +# Once parsing is finished, recursive expansion in commands are not allowed to create any new rules (it may only set variables) + +define MORERULE +all: + @echo TEST-FAIL +endef + +all: + $(eval $(MORERULE)) + @echo done diff --git a/build/pymake/tests/eval.mk b/build/pymake/tests/eval.mk new file mode 100644 index 000000000..de9759f02 --- /dev/null +++ b/build/pymake/tests/eval.mk @@ -0,0 +1,7 @@ +TESTVAR = val1 + +$(eval TESTVAR = val2) + +all: + test "$(TESTVAR)" = "val2" + @echo TEST-PASS diff --git a/build/pymake/tests/exit-code.mk b/build/pymake/tests/exit-code.mk new file mode 100644 index 000000000..84dcffcf9 --- /dev/null +++ b/build/pymake/tests/exit-code.mk @@ -0,0 +1,5 @@ +#T returncode: 2 + +all: + exit 1 + @echo TEST-PASS diff --git a/build/pymake/tests/file-functions-symlinks.mk b/build/pymake/tests/file-functions-symlinks.mk new file mode 100644 index 000000000..dcc0f6eef --- /dev/null +++ b/build/pymake/tests/file-functions-symlinks.mk @@ -0,0 +1,22 @@ +#T returncode-on: {'win32': 2} +$(shell \ +touch test.file; \ +ln -s test.file test.symlink; \ +ln -s test.missing missing.symlink; \ +touch .testhidden; \ +mkdir foo; \ +touch foo/testfile; \ +ln -s foo symdir; \ +) + +all: + test "$(abspath test.file test.symlink)" = "$(CURDIR)/test.file $(CURDIR)/test.symlink" + test "$(realpath test.file test.symlink)" = "$(CURDIR)/test.file $(CURDIR)/test.file" + test "$(sort $(wildcard *))" = "foo symdir test.file test.symlink" + test "$(sort $(wildcard .*))" = ". .. .testhidden" + test "$(sort $(wildcard test*))" = "test.file test.symlink" + test "$(sort $(wildcard foo/*))" = "foo/testfile" + test "$(sort $(wildcard ./*))" = "./foo ./symdir ./test.file ./test.symlink" + test "$(sort $(wildcard f?o/*))" = "foo/testfile" + test "$(sort $(wildcard */*))" = "foo/testfile symdir/testfile" + @echo TEST-PASS diff --git a/build/pymake/tests/file-functions.mk b/build/pymake/tests/file-functions.mk new file mode 100644 index 000000000..7e4c68e85 --- /dev/null +++ b/build/pymake/tests/file-functions.mk @@ -0,0 +1,19 @@ +$(shell \ +touch test.file; \ +touch .testhidden; \ +mkdir foo; \ +touch foo/testfile; \ +) + +all: + test "$(abspath test.file)" = "$(CURDIR)/test.file" + test "$(realpath test.file)" = "$(CURDIR)/test.file" + test "$(sort $(wildcard *))" = "foo test.file" +# commented out because GNU make matches . and .. while python doesn't, and I don't +# care enough +# test "$(sort $(wildcard .*))" = ". .. .testhidden" + test "$(sort $(wildcard test*))" = "test.file" + test "$(sort $(wildcard foo/*))" = "foo/testfile" + test "$(sort $(wildcard ./*))" = "./foo ./test.file" + test "$(sort $(wildcard f?o/*))" = "foo/testfile" + @echo TEST-PASS diff --git a/build/pymake/tests/formattingtests.py b/build/pymake/tests/formattingtests.py new file mode 100644 index 000000000..7aad6d4cc --- /dev/null +++ b/build/pymake/tests/formattingtests.py @@ -0,0 +1,289 @@ +# This file contains test code for the formatting of parsed statements back to +# make file "source." It essentially verifies to to_source() functions +# scattered across the tree. + +import glob +import logging +import os.path +import unittest + +from pymake.data import Expansion +from pymake.data import StringExpansion +from pymake.functions import BasenameFunction +from pymake.functions import SubstitutionRef +from pymake.functions import VariableRef +from pymake.functions import WordlistFunction +from pymake.parserdata import Include +from pymake.parserdata import SetVariable +from pymake.parser import parsestring +from pymake.parser import SyntaxError + +class TestBase(unittest.TestCase): + pass + +class VariableRefTest(TestBase): + def test_string_name(self): + e = StringExpansion('foo', None) + v = VariableRef(None, e) + + self.assertEqual(v.to_source(), '$(foo)') + + def test_special_variable(self): + e = StringExpansion('<', None) + v = VariableRef(None, e) + + self.assertEqual(v.to_source(), '$<') + + def test_expansion_simple(self): + e = Expansion() + e.appendstr('foo') + e.appendstr('bar') + + v = VariableRef(None, e) + + self.assertEqual(v.to_source(), '$(foobar)') + +class StandardFunctionTest(TestBase): + def test_basename(self): + e1 = StringExpansion('foo', None) + v = VariableRef(None, e1) + e2 = Expansion(None) + e2.appendfunc(v) + + b = BasenameFunction(None) + b.append(e2) + + self.assertEqual(b.to_source(), '$(basename $(foo))') + + def test_wordlist(self): + e1 = StringExpansion('foo', None) + e2 = StringExpansion('bar ', None) + e3 = StringExpansion(' baz', None) + + w = WordlistFunction(None) + w.append(e1) + w.append(e2) + w.append(e3) + + self.assertEqual(w.to_source(), '$(wordlist foo,bar , baz)') + + def test_curly_brackets(self): + e1 = Expansion(None) + e1.appendstr('foo') + + e2 = Expansion(None) + e2.appendstr('foo ( bar') + + f = WordlistFunction(None) + f.append(e1) + f.append(e2) + + self.assertEqual(f.to_source(), '${wordlist foo,foo ( bar}') + +class StringExpansionTest(TestBase): + def test_simple(self): + e = StringExpansion('foobar', None) + self.assertEqual(e.to_source(), 'foobar') + + e = StringExpansion('$var', None) + self.assertEqual(e.to_source(), '$var') + + def test_escaping(self): + e = StringExpansion('$var', None) + self.assertEqual(e.to_source(escape_variables=True), '$$var') + + e = StringExpansion('this is # not a comment', None) + self.assertEqual(e.to_source(escape_comments=True), + 'this is \# not a comment') + + def test_empty(self): + e = StringExpansion('', None) + self.assertEqual(e.to_source(), '') + + e = StringExpansion(' ', None) + self.assertEqual(e.to_source(), ' ') + +class ExpansionTest(TestBase): + def test_single_string(self): + e = Expansion() + e.appendstr('foo') + + self.assertEqual(e.to_source(), 'foo') + + def test_multiple_strings(self): + e = Expansion() + e.appendstr('hello') + e.appendstr('world') + + self.assertEqual(e.to_source(), 'helloworld') + + def test_string_escape(self): + e = Expansion() + e.appendstr('$var') + self.assertEqual(e.to_source(), '$var') + self.assertEqual(e.to_source(escape_variables=True), '$$var') + + e = Expansion() + e.appendstr('foo') + e.appendstr(' $bar') + self.assertEqual(e.to_source(escape_variables=True), 'foo $$bar') + +class SubstitutionRefTest(TestBase): + def test_simple(self): + name = StringExpansion('foo', None) + c = StringExpansion('%.c', None) + o = StringExpansion('%.o', None) + s = SubstitutionRef(None, name, c, o) + + self.assertEqual(s.to_source(), '$(foo:%.c=%.o)') + +class SetVariableTest(TestBase): + def test_simple(self): + v = SetVariable(StringExpansion('foo', None), '=', 'bar', None, None) + self.assertEqual(v.to_source(), 'foo = bar') + + def test_multiline(self): + s = 'hello\nworld' + foo = StringExpansion('FOO', None) + + v = SetVariable(foo, '=', s, None, None) + + self.assertEqual(v.to_source(), 'define FOO\nhello\nworld\nendef') + + def test_multiline_immediate(self): + source = 'define FOO :=\nhello\nworld\nendef' + + statements = parsestring(source, 'foo.mk') + self.assertEqual(statements.to_source(), source) + + def test_target_specific(self): + foo = StringExpansion('FOO', None) + bar = StringExpansion('BAR', None) + + v = SetVariable(foo, '+=', 'value', None, bar) + + self.assertEqual(v.to_source(), 'BAR: FOO += value') + +class IncludeTest(TestBase): + def test_include(self): + e = StringExpansion('rules.mk', None) + i = Include(e, True, False) + self.assertEqual(i.to_source(), 'include rules.mk') + + i = Include(e, False, False) + self.assertEqual(i.to_source(), '-include rules.mk') + +class IfdefTest(TestBase): + def test_simple(self): + source = 'ifdef FOO\nbar := $(value)\nendif' + + statements = parsestring(source, 'foo.mk') + self.assertEqual(statements[0].to_source(), source) + + def test_nested(self): + source = 'ifdef FOO\nifdef BAR\nhello = world\nendif\nendif' + + statements = parsestring(source, 'foo.mk') + self.assertEqual(statements[0].to_source(), source) + + def test_negation(self): + source = 'ifndef FOO\nbar += value\nendif' + + statements = parsestring(source, 'foo.mk') + self.assertEqual(statements[0].to_source(), source) + +class IfeqTest(TestBase): + def test_simple(self): + source = 'ifeq ($(foo),bar)\nhello = $(world)\nendif' + + statements = parsestring(source, 'foo.mk') + self.assertEqual(statements[0].to_source(), source) + + def test_negation(self): + source = 'ifneq (foo,bar)\nhello = world\nendif' + + statements = parsestring(source, 'foo.mk') + self.assertEqual(statements.to_source(), source) + +class ConditionBlocksTest(TestBase): + def test_mixed_conditions(self): + source = 'ifdef FOO\nifeq ($(FOO),bar)\nvar += $(value)\nendif\nendif' + + statements = parsestring(source, 'foo.mk') + self.assertEqual(statements.to_source(), source) + + def test_extra_statements(self): + source = 'ifdef FOO\nF := 1\nifdef BAR\nB += 1\nendif\nC = 1\nendif' + + statements = parsestring(source, 'foo.mk') + self.assertEqual(statements.to_source(), source) + + def test_whitespace_preservation(self): + source = "ifeq ' x' 'x '\n$(error stripping)\nendif" + + statements = parsestring(source, 'foo.mk') + self.assertEqual(statements.to_source(), source) + + source = 'ifneq (x , x)\n$(error stripping)\nendif' + statements = parsestring(source, 'foo.mk') + self.assertEqual(statements.to_source(), + 'ifneq (x,x)\n$(error stripping)\nendif') + +class MakefileCorupusTest(TestBase): + """Runs the make files from the pymake corpus through the formatter. + + All the above tests are child's play compared to this. + """ + + # Our reformatting isn't perfect. We ignore files with known failures until + # we make them work. + # TODO Address these formatting corner cases. + _IGNORE_FILES = [ + # We are thrown off by backslashes at end of lines. + 'comment-parsing.mk', + 'escape-chars.mk', + 'include-notfound.mk', + ] + + def _get_test_files(self): + ourdir = os.path.dirname(os.path.abspath(__file__)) + + for makefile in glob.glob(os.path.join(ourdir, '*.mk')): + if os.path.basename(makefile) in self._IGNORE_FILES: + continue + + source = None + with open(makefile, 'rU') as fh: + source = fh.read() + + try: + yield (makefile, source, parsestring(source, makefile)) + except SyntaxError: + continue + + def test_reparse_consistency(self): + for filename, source, statements in self._get_test_files(): + reformatted = statements.to_source() + + # We should be able to parse the reformatted source fine. + new_statements = parsestring(reformatted, filename) + + # If we do the formatting again, the representation shouldn't + # change. i.e. the only lossy change should be the original + # (whitespace and some semantics aren't preserved). + reformatted_again = new_statements.to_source() + self.assertEqual(reformatted, reformatted_again, + '%s has lossless reformat.' % filename) + + self.assertEqual(len(statements), len(new_statements)) + + for i in xrange(0, len(statements)): + original = statements[i] + formatted = new_statements[i] + + self.assertEqual(original, formatted, '%s %d: %s != %s' % (filename, + i, original, formatted)) + +if __name__ == '__main__': + logging.basicConfig(level=logging.DEBUG) + unittest.main() diff --git a/build/pymake/tests/func-refs.mk b/build/pymake/tests/func-refs.mk new file mode 100644 index 000000000..82ab17ba8 --- /dev/null +++ b/build/pymake/tests/func-refs.mk @@ -0,0 +1,11 @@ +unknown var = uval + +all: + test "$(subst a,b,value)" = "vblue" + test "${subst a,b,va)lue}" = "vb)lue" + test "$(subst /,\,ab/c)" = "ab\c" + test '$(subst a,b,\\#)' = '\\#' + test "$( subst a,b,value)" = "" + test "$(Subst a,b,value)" = "" + test "$(unknown var)" = "uval" + @echo TEST-PASS diff --git a/build/pymake/tests/functions.mk b/build/pymake/tests/functions.mk new file mode 100644 index 000000000..817be07aa --- /dev/null +++ b/build/pymake/tests/functions.mk @@ -0,0 +1,36 @@ +all: + test "$(subst e,EE,hello)" = "hEEllo" + test "$(strip $(NULL) test data )" = "test data" + test "$(findstring hell,hello)" = "hell" + test "$(findstring heaven,hello)" = "" + test "$(filter foo/%.c b%,foo/a.c b.c foo/a.o)" = "foo/a.c b.c" + test "$(filter foo,foo bar)" = "foo" + test "$(filter-out foo/%.c b%,foo/a.c b.c foo/a.o)" = "foo/a.o" + test "$(filter-out %.c,foo,bar.c foo,bar.o)" = "foo,bar.o" + test "$(sort .go a b aa A c cc)" = ".go A a aa b c cc" + test "$(word 1, hello )" = "hello" + test "$(word 2, hello )" = "" + test "$(wordlist 1, 2, foo bar baz )" = "foo bar" + test "$(words 1 2 3)" = "3" + test "$(words )" = "0" + test "$(firstword $(NULL) foo bar baz)" = "foo" + test "$(firstword )" = "" + test "$(dir foo.c path/foo.o dir/dir2/)" = "./ path/ dir/dir2/" + test "$(notdir foo.c path/foo.o dir/dir2/)" = "foo.c foo.o " + test "$(suffix src/foo.c dir/my.dir/foo foo.o)" = ".c .o" + test "$(basename src/foo.c dir/my.dir/foo foo.c .c)" = "src/foo dir/my.dir/foo foo " + test "$(addprefix src/,foo bar.c dir/foo)" = "src/foo src/bar.c src/dir/foo" + test "$(addsuffix .c,foo dir/bar)" = "foo.c dir/bar.c" + test "$(join a b c, 1 2 3)" = "a1 b2 c3" + test "$(join a b, 1 2 3)" = "a1 b2 3" + test "$(join a b c, 1 2)" = "a1 b2 c" + test "$(if $(NULL) ,yes)" = "" + test "$(if 1,yes,no)" = "yes" + test "$(if ,yes,no )" = "no " + test "$(if ,$(error Short-circuit problem))" = "" + test "$(or $(NULL),1)" = "1" + test "$(or $(NULL),2,$(warning TEST-FAIL bad or short-circuit))" = "2" + test "$(and ,$(warning TEST-FAIL bad and short-circuit))" = "" + test "$(and 1,2)" = "2" + test "$(foreach i,foo bar,found:$(i))" = "found:foo found:bar" + @echo TEST-PASS diff --git a/build/pymake/tests/functiontests.py b/build/pymake/tests/functiontests.py new file mode 100644 index 000000000..43a344a05 --- /dev/null +++ b/build/pymake/tests/functiontests.py @@ -0,0 +1,54 @@ +import unittest + +import pymake.data +import pymake.functions + +class VariableRefTest(unittest.TestCase): + def test_get_expansions(self): + e = pymake.data.StringExpansion('FOO', None) + f = pymake.functions.VariableRef(None, e) + + exps = list(f.expansions()) + self.assertEqual(len(exps), 1) + +class GetExpansionsTest(unittest.TestCase): + def test_get_arguments(self): + f = pymake.functions.SubstFunction(None) + + e1 = pymake.data.StringExpansion('FOO', None) + e2 = pymake.data.StringExpansion('BAR', None) + e3 = pymake.data.StringExpansion('BAZ', None) + + f.append(e1) + f.append(e2) + f.append(e3) + + exps = list(f.expansions()) + self.assertEqual(len(exps), 3) + + def test_descend(self): + f = pymake.functions.StripFunction(None) + + e = pymake.data.Expansion(None) + + e1 = pymake.data.StringExpansion('FOO', None) + f1 = pymake.functions.VariableRef(None, e1) + e.appendfunc(f1) + + f2 = pymake.functions.WildcardFunction(None) + e2 = pymake.data.StringExpansion('foo/*', None) + f2.append(e2) + e.appendfunc(f2) + + f.append(e) + + exps = list(f.expansions()) + self.assertEqual(len(exps), 1) + + exps = list(f.expansions(True)) + self.assertEqual(len(exps), 3) + + self.assertFalse(f.is_filesystem_dependent) + +if __name__ == '__main__': + unittest.main() diff --git a/build/pymake/tests/if-syntaxerr.mk b/build/pymake/tests/if-syntaxerr.mk new file mode 100644 index 000000000..c172492ef --- /dev/null +++ b/build/pymake/tests/if-syntaxerr.mk @@ -0,0 +1,6 @@ +#T returncode: 2 + +ifeq ($(FOO,VAR)) +all: + @echo TEST_FAIL +endif diff --git a/build/pymake/tests/ifdefs-nesting.mk b/build/pymake/tests/ifdefs-nesting.mk new file mode 100644 index 000000000..340530ffa --- /dev/null +++ b/build/pymake/tests/ifdefs-nesting.mk @@ -0,0 +1,13 @@ +ifdef RANDOM +ifeq (,$(error Not evaluated!)) +endif +endif + +ifdef RANDOM +ifeq (,) +else ifeq (,$(error Not evaluated!)) +endif +endif + +all: + @echo TEST-PASS diff --git a/build/pymake/tests/ifdefs.mk b/build/pymake/tests/ifdefs.mk new file mode 100644 index 000000000..a779d197b --- /dev/null +++ b/build/pymake/tests/ifdefs.mk @@ -0,0 +1,127 @@ +ifdef FOO +$(error FOO is not defined!) +endif + +FOO = foo +FOOFOUND = false +BARFOUND = false +BAZFOUND = false + +ifdef FOO +FOOFOUND = true +else ifdef BAR +BARFOUND = true +else +BAZFOUND = true +endif + +BAR2 = bar2 +FOO2FOUND = false +BAR2FOUND = false +BAZ2FOUND = false + +ifdef FOO2 +FOO2FOUND = true +else ifdef BAR2 +BAR2FOUND = true +else +BAZ2FOUND = true +endif + +FOO3FOUND = false +BAR3FOUND = false +BAZ3FOUND = false + +ifdef FOO3 +FOO3FOUND = true +else ifdef BAR3 +BAR3FOUND = true +else +BAZ3FOUND = true +endif + +ifdef RANDOM +CONTINUATION = \ +else \ +endif +endif + +ifndef ASDFJK +else +$(error ASFDJK was not set) +endif + +TESTSET = + +ifdef TESTSET +$(error TESTSET was not set) +endif + +TESTEMPTY = $(NULL) +ifndef TESTEMPTY +$(error TEST-FAIL TESTEMPTY was probably expanded!) +endif + +# ifneq ( a,a) +# $(error Arguments to ifeq should be stripped before evaluation) +# endif + +XSPACE = x # trick + +ifneq ($(NULL),$(NULL)) +$(error TEST-FAIL ifneq) +endif + +ifneq (x , x) +$(error argument-stripping1) +endif + +ifeq ( x,x ) +$(error argument-stripping2) +endif + +ifneq ($(XSPACE), x ) +$(error argument-stripping3) +endif + +ifeq 'x ' ' x' +$(error TEST-FAIL argument-stripping4) +endif + +all: + test $(FOOFOUND) = true # FOOFOUND + test $(BARFOUND) = false # BARFOUND + test $(BAZFOUND) = false # BAZFOUND + test $(FOO2FOUND) = false # FOO2FOUND + test $(BAR2FOUND) = true # BAR2FOUND + test $(BAZ2FOUND) = false # BAZ2FOUND + test $(FOO3FOUND) = false # FOO3FOUND + test $(BAR3FOUND) = false # BAR3FOUND + test $(BAZ3FOUND) = true # BAZ3FOUND +ifneq ($(FOO),foo) + echo TEST-FAIL 'FOO neq foo: "$(FOO)"' +endif +ifneq ($(FOO), foo) # Whitespace after the comma is stripped + echo TEST-FAIL 'FOO plus whitespace' +endif +ifeq ($(FOO), foo ) # But not trailing whitespace + echo TEST-FAIL 'FOO plus trailing whitespace' +endif +ifeq ( $(FOO),foo) # Not whitespace after the paren + echo TEST-FAIL 'FOO with leading whitespace' +endif +ifeq ($(FOO),$(NULL) foo) # Nor whitespace after expansion + echo TEST-FAIL 'FOO with embedded ws' +endif +ifeq ($(BAR2),bar) + echo TEST-FAIL 'BAR2 eq bar' +endif +ifeq '$(BAR3FOUND)' 'false' + echo BAR3FOUND is ok +else + echo TEST-FAIL BAR3FOUND is not ok +endif +ifndef FOO + echo TEST-FAIL "foo not defined?" +endif + @echo TEST-PASS diff --git a/build/pymake/tests/ignore-error.mk b/build/pymake/tests/ignore-error.mk new file mode 100644 index 000000000..dc8d3a72c --- /dev/null +++ b/build/pymake/tests/ignore-error.mk @@ -0,0 +1,13 @@ +all: + -rm foo + +-rm bar + -+rm baz + @-rm bah + -@rm humbug + +-@rm sincere + +@-rm flattery + @+-rm will + @-+rm not + -+@rm save + -@+rm you + @echo TEST-PASS diff --git a/build/pymake/tests/implicit-chain.mk b/build/pymake/tests/implicit-chain.mk new file mode 100644 index 000000000..16288b3f5 --- /dev/null +++ b/build/pymake/tests/implicit-chain.mk @@ -0,0 +1,12 @@ +all: test.prog + test "$$(cat $<)" = "Program: Object: Source: test.source" + @echo TEST-PASS + +%.prog: %.object + printf "Program: %s" "$$(cat $<)" > $@ + +%.object: %.source + printf "Object: %s" "$$(cat $<)" > $@ + +%.source: + printf "Source: %s" $@ > $@ diff --git a/build/pymake/tests/implicit-dir.mk b/build/pymake/tests/implicit-dir.mk new file mode 100644 index 000000000..c7f75e8d4 --- /dev/null +++ b/build/pymake/tests/implicit-dir.mk @@ -0,0 +1,16 @@ +# Implicit rules have special instructions to deal with directories, so that a pattern rule which doesn't directly apply +# may still be used. + +all: dir/host_test.otest + +host_%.otest: %.osource extra.file + @echo making $@ from $< + +test.osource: + @echo TEST-FAIL should have made dir/test.osource + +dir/test.osource: + @echo TEST-PASS made the correct dependency + +extra.file: + @echo building $@ diff --git a/build/pymake/tests/implicit-terminal.mk b/build/pymake/tests/implicit-terminal.mk new file mode 100644 index 000000000..db2e244ed --- /dev/null +++ b/build/pymake/tests/implicit-terminal.mk @@ -0,0 +1,16 @@ +#T returncode: 2 + +# the %.object rule is "terminal". This means that additional implicit rules cannot be chained to it. + +all: test.prog + test "$$(cat $<)" = "Program: Object: Source: test.source" + @echo TEST-FAIL + +%.prog: %.object + printf "Program: %s" "$$(cat $<)" > $@ + +%.object:: %.source + printf "Object: %s" "$$(cat $<)" > $@ + +%.source: + printf "Source: %s" $@ > $@ diff --git a/build/pymake/tests/implicitsubdir.mk b/build/pymake/tests/implicitsubdir.mk new file mode 100644 index 000000000..b9d854a2a --- /dev/null +++ b/build/pymake/tests/implicitsubdir.mk @@ -0,0 +1,12 @@ +$(shell \ +mkdir foo; \ +touch test.in \ +) + +all: foo/test.out + @echo TEST-PASS + +foo/%.out: %.in + cp $< $@ + + diff --git a/build/pymake/tests/include-dynamic.mk b/build/pymake/tests/include-dynamic.mk new file mode 100644 index 000000000..571895dc3 --- /dev/null +++ b/build/pymake/tests/include-dynamic.mk @@ -0,0 +1,21 @@ +$(shell \ +if ! test -f include-dynamic.inc; then \ + echo "TESTVAR = oldval" > include-dynamic.inc; \ + sleep 2; \ + echo "TESTVAR = newval" > include-dynamic.inc.in; \ +fi \ +) + +# before running the 'all' rule, we should be rebuilding include-dynamic.inc, +# because there is a rule to do so + +all: + test $(TESTVAR) = newval + test "$(MAKE_RESTARTS)" = 1 + @echo TEST-PASS + +include-dynamic.inc: include-dynamic.inc.in + test "$(MAKE_RESTARTS)" = "" + cp $< $@ + +include include-dynamic.inc diff --git a/build/pymake/tests/include-file.inc b/build/pymake/tests/include-file.inc new file mode 100644 index 000000000..d5d495dec --- /dev/null +++ b/build/pymake/tests/include-file.inc @@ -0,0 +1 @@ +INCLUDED = yes diff --git a/build/pymake/tests/include-missing.mk b/build/pymake/tests/include-missing.mk new file mode 100644 index 000000000..583d0a065 --- /dev/null +++ b/build/pymake/tests/include-missing.mk @@ -0,0 +1,9 @@ +#T returncode: 2 + +# If an include file isn't present and doesn't have a rule to remake it, make +# should fail. + +include notfound.mk + +all: + @echo TEST-FAIL diff --git a/build/pymake/tests/include-notfound.mk b/build/pymake/tests/include-notfound.mk new file mode 100644 index 000000000..1ee7e05b2 --- /dev/null +++ b/build/pymake/tests/include-notfound.mk @@ -0,0 +1,19 @@ +ifdef __WIN32__ +PS:=\\# +else +PS:=/ +endif + +ifneq ($(strip $(MAKEFILE_LIST)),$(NATIVE_TESTPATH)$(PS)include-notfound.mk) +$(error MAKEFILE_LIST incorrect: '$(MAKEFILE_LIST)' (expected '$(NATIVE_TESTPATH)$(PS)include-notfound.mk')) +endif + +-include notfound.inc-dummy + +ifneq ($(strip $(MAKEFILE_LIST)),$(NATIVE_TESTPATH)$(PS)include-notfound.mk) +$(error MAKEFILE_LIST incorrect: '$(MAKEFILE_LIST)' (expected '$(NATIVE_TESTPATH)$(PS)include-notfound.mk')) +endif + +all: + @echo TEST-PASS + diff --git a/build/pymake/tests/include-optional-warning.mk b/build/pymake/tests/include-optional-warning.mk new file mode 100644 index 000000000..901938dff --- /dev/null +++ b/build/pymake/tests/include-optional-warning.mk @@ -0,0 +1,4 @@ +-include TEST-FAIL.mk + +all: + @echo TEST-PASS diff --git a/build/pymake/tests/include-regen.mk b/build/pymake/tests/include-regen.mk new file mode 100644 index 000000000..c86e0c78d --- /dev/null +++ b/build/pymake/tests/include-regen.mk @@ -0,0 +1,10 @@ +# avoid infinite loops by not remaking makefiles with +# double-colon no-dependency rules +# http://www.gnu.org/software/make/manual/make.html#Remaking-Makefiles +-include notfound.mk + +all: + @echo TEST-PASS + +notfound.mk:: + @echo TEST-FAIL diff --git a/build/pymake/tests/include-regen2.mk b/build/pymake/tests/include-regen2.mk new file mode 100644 index 000000000..fc7fef073 --- /dev/null +++ b/build/pymake/tests/include-regen2.mk @@ -0,0 +1,10 @@ +# make should make makefiles that it has rules for if they are
+# included
+include test.mk
+
+all:
+ test "$(X)" = "1"
+ @echo "TEST-PASS"
+
+test.mk:
+ @echo "X = 1" > $@
diff --git a/build/pymake/tests/include-regen3.mk b/build/pymake/tests/include-regen3.mk new file mode 100644 index 000000000..878ce0adc --- /dev/null +++ b/build/pymake/tests/include-regen3.mk @@ -0,0 +1,10 @@ +# make should make makefiles that it has rules for if they are
+# included
+-include test.mk
+
+all:
+ test "$(X)" = "1"
+ @echo "TEST-PASS"
+
+test.mk:
+ @echo "X = 1" > $@
diff --git a/build/pymake/tests/include-test.mk b/build/pymake/tests/include-test.mk new file mode 100644 index 000000000..3608fc269 --- /dev/null +++ b/build/pymake/tests/include-test.mk @@ -0,0 +1,8 @@ +$(shell echo "INCLUDED2 = yes" >local-include.inc) + +include $(TESTPATH)/include-file.inc local-include.inc + +all: + test "$(INCLUDED)" = "yes" + test "$(INCLUDED2)" = "yes" + @echo TEST-PASS diff --git a/build/pymake/tests/includedeps-norebuild.mk b/build/pymake/tests/includedeps-norebuild.mk new file mode 100644 index 000000000..e30abd439 --- /dev/null +++ b/build/pymake/tests/includedeps-norebuild.mk @@ -0,0 +1,15 @@ +#T gmake skip + +$(shell \ +touch filemissing; \ +sleep 2; \ +touch file1; \ +) + +all: file1 + @echo TEST-PASS + +includedeps $(TESTPATH)/includedeps.deps + +file1: + @echo TEST-FAIL diff --git a/build/pymake/tests/includedeps-sideeffects.mk b/build/pymake/tests/includedeps-sideeffects.mk new file mode 100644 index 000000000..7e4ea30a2 --- /dev/null +++ b/build/pymake/tests/includedeps-sideeffects.mk @@ -0,0 +1,10 @@ +#T gmake skip +#T returncode: 2 + +all: file1 filemissing + @echo TEST-PASS + +includedeps $(TESTPATH)/includedeps.deps + +file: + touch $@ diff --git a/build/pymake/tests/includedeps.deps b/build/pymake/tests/includedeps.deps new file mode 100644 index 000000000..d3017c078 --- /dev/null +++ b/build/pymake/tests/includedeps.deps @@ -0,0 +1 @@ +file1: filemissing diff --git a/build/pymake/tests/includedeps.mk b/build/pymake/tests/includedeps.mk new file mode 100644 index 000000000..deaa71fe8 --- /dev/null +++ b/build/pymake/tests/includedeps.mk @@ -0,0 +1,9 @@ +#T gmake skip + +all: file1 + @echo TEST-PASS + +includedeps $(TESTPATH)/includedeps.deps + +file1: + touch $@ diff --git a/build/pymake/tests/info.mk b/build/pymake/tests/info.mk new file mode 100644 index 000000000..8dddfd815 --- /dev/null +++ b/build/pymake/tests/info.mk @@ -0,0 +1,8 @@ +#T grep-for: "info-printed\ninfo-nth" +all: + +INFO = info-printed + +$(info $(INFO)) +$(info $(subst second,nth,info-second)) +$(info TEST-PASS) diff --git a/build/pymake/tests/justprint-native.mk b/build/pymake/tests/justprint-native.mk new file mode 100644 index 000000000..580e402e9 --- /dev/null +++ b/build/pymake/tests/justprint-native.mk @@ -0,0 +1,28 @@ +## $(TOUCH) and $(RM) are native commands in pymake.
+## Test that pymake --just-print just prints them.
+
+ifndef TOUCH
+TOUCH = touch
+endif
+
+all:
+ $(RM) justprint-native-file1.txt
+ $(TOUCH) justprint-native-file2.txt
+ $(MAKE) --just-print -f $(TESTPATH)/justprint-native.mk justprint_target > justprint.log
+# make --just-print shouldn't have actually done anything.
+ test ! -f justprint-native-file1.txt
+ test -f justprint-native-file2.txt
+# but it should have printed each command
+ grep -q 'touch justprint-native-file1.txt' justprint.log
+ grep -q 'rm -f justprint-native-file2.txt' justprint.log
+ grep -q 'this string is "unlikely to appear in the log by chance"' justprint.log
+# tidy up
+ $(RM) justprint-native-file2.txt
+ @echo TEST-PASS
+
+justprint_target:
+ $(TOUCH) justprint-native-file1.txt
+ $(RM) justprint-native-file2.txt
+ this string is "unlikely to appear in the log by chance"
+
+.PHONY: justprint_target
diff --git a/build/pymake/tests/justprint.mk b/build/pymake/tests/justprint.mk new file mode 100644 index 000000000..be11ba8de --- /dev/null +++ b/build/pymake/tests/justprint.mk @@ -0,0 +1,5 @@ +#T commandline: ['-n']
+
+all:
+ false # without -n, we wouldn't get past this
+ TEST-PASS # heh
diff --git a/build/pymake/tests/keep-going-doublecolon.mk b/build/pymake/tests/keep-going-doublecolon.mk new file mode 100644 index 000000000..fa5b31df8 --- /dev/null +++ b/build/pymake/tests/keep-going-doublecolon.mk @@ -0,0 +1,16 @@ +#T commandline: ['-k'] +#T returncode: 2 +#T grep-for: "TEST-PASS" + +all:: t1 + @echo TEST-FAIL "(t1)" + +all:: t2 + @echo TEST-PASS + +t1: + @false + +t2: + touch $@ + diff --git a/build/pymake/tests/keep-going-parallel.mk b/build/pymake/tests/keep-going-parallel.mk new file mode 100644 index 000000000..a91d1a6ed --- /dev/null +++ b/build/pymake/tests/keep-going-parallel.mk @@ -0,0 +1,11 @@ +#T commandline: ['-k', '-j2'] +#T returncode: 2 +#T grep-for: "TEST-PASS" + +all: t1 slow1 slow2 slow3 t2 + +t2: + @echo TEST-PASS + +slow%: + sleep 1 diff --git a/build/pymake/tests/keep-going.mk b/build/pymake/tests/keep-going.mk new file mode 100644 index 000000000..4c709288c --- /dev/null +++ b/build/pymake/tests/keep-going.mk @@ -0,0 +1,14 @@ +#T commandline: ['-k'] +#T returncode: 2 +#T grep-for: "TEST-PASS" + +all: t2 t3 + +t1: + @false + +t2: t1 + @echo TEST-FAIL + +t3: + @echo TEST-PASS diff --git a/build/pymake/tests/line-continuations.mk b/build/pymake/tests/line-continuations.mk new file mode 100644 index 000000000..8b44480ea --- /dev/null +++ b/build/pymake/tests/line-continuations.mk @@ -0,0 +1,24 @@ +VAR = val1 \ + val2 + +VAR2 = val1space\ +val2 + +VAR3 = val3 \\\ + cont3 + +all: otarget test.target + test "$(VAR)" = "val1 val2 " + test "$(VAR2)" = "val1space val2" + test '$(VAR3)' = 'val3 \ cont3' + test "hello \ + world" = "hello world" + test "hello" = \ +"hello" + @echo TEST-PASS + +otarget: ; test "hello\ + world" = "helloworld" + +test.target: %.target: ; test "hello\ + world" = "helloworld" diff --git a/build/pymake/tests/link-search.mk b/build/pymake/tests/link-search.mk new file mode 100644 index 000000000..ea827f391 --- /dev/null +++ b/build/pymake/tests/link-search.mk @@ -0,0 +1,7 @@ +$(shell \ +touch libfoo.so \ +) + +all: -lfoo + test "$<" = "libfoo.so" + @echo TEST-PASS diff --git a/build/pymake/tests/makeflags.mk b/build/pymake/tests/makeflags.mk new file mode 100644 index 000000000..288ff7866 --- /dev/null +++ b/build/pymake/tests/makeflags.mk @@ -0,0 +1,7 @@ +#T environment: {'MAKEFLAGS': 'OVAR=oval'} + +all: + test "$(OVAR)" = "oval" + test "$$OVAR" = "oval" + @echo TEST-PASS + diff --git a/build/pymake/tests/matchany.mk b/build/pymake/tests/matchany.mk new file mode 100644 index 000000000..7876c90a3 --- /dev/null +++ b/build/pymake/tests/matchany.mk @@ -0,0 +1,14 @@ +#T returncode: 2 + +# we should fail to make foo.ooo from foo.ooo.test +all: foo.ooo + @echo TEST-FAIL + +%.ooo: + +# this match-anything pattern should not apply to %.ooo +%: %.test + cp $< $@ + +foo.ooo.test: + touch $@ diff --git a/build/pymake/tests/matchany2.mk b/build/pymake/tests/matchany2.mk new file mode 100644 index 000000000..d21d9702c --- /dev/null +++ b/build/pymake/tests/matchany2.mk @@ -0,0 +1,13 @@ +# we should succeed in making foo.ooo from foo.ooo.test +all: foo.ooo + @echo TEST-PASS + +%.ooo: %.ccc + exit 1 + +# this match-anything rule is terminal, and therefore applies +%:: %.test + cp $< $@ + +foo.ooo.test: + touch $@ diff --git a/build/pymake/tests/matchany3.mk b/build/pymake/tests/matchany3.mk new file mode 100644 index 000000000..83de8af2b --- /dev/null +++ b/build/pymake/tests/matchany3.mk @@ -0,0 +1,10 @@ +$(shell \ +echo "target" > target.in; \ +) + +all: target + test "$$(cat $^)" = "target" + @echo TEST-PASS + +%: %.in + cp $< $@ diff --git a/build/pymake/tests/mkdir-fail.mk b/build/pymake/tests/mkdir-fail.mk new file mode 100644 index 000000000..b05734aa9 --- /dev/null +++ b/build/pymake/tests/mkdir-fail.mk @@ -0,0 +1,7 @@ +#T returncode: 2 +all: + mkdir newdir/subdir + test ! -d newdir/subdir + test ! -d newdir + rm -r newdir + @echo TEST-PASS diff --git a/build/pymake/tests/mkdir.mk b/build/pymake/tests/mkdir.mk new file mode 100644 index 000000000..413348f77 --- /dev/null +++ b/build/pymake/tests/mkdir.mk @@ -0,0 +1,27 @@ +MKDIR ?= mkdir + +all: + $(MKDIR) newdir + test -d newdir + # subdir, parent exists + $(MKDIR) newdir/subdir + test -d newdir/subdir + # -p, existing dir + $(MKDIR) -p newdir + # -p, existing subdir + $(MKDIR) -p newdir/subdir + # multiple subdirs, existing parent + $(MKDIR) newdir/subdir1 newdir/subdir2 + test -d newdir/subdir1 -a -d newdir/subdir2 + rm -r newdir + # -p, subdir, no existing parent + $(MKDIR) -p newdir/subdir + test -d newdir/subdir + rm -r newdir + # -p, multiple subdirs, no existing parent + $(MKDIR) -p newdir/subdir1 newdir/subdir2 + test -d newdir/subdir1 -a -d newdir/subdir2 + # -p, multiple existing subdirs + $(MKDIR) -p newdir/subdir1 newdir/subdir2 + rm -r newdir + @echo TEST-PASS diff --git a/build/pymake/tests/native-command-raise.mk b/build/pymake/tests/native-command-raise.mk new file mode 100644 index 000000000..d1b28b331 --- /dev/null +++ b/build/pymake/tests/native-command-raise.mk @@ -0,0 +1,9 @@ +#T gmake skip +#T returncode: 2 +#T grep-for: "Exception: info-exception" + +CMD = %pycmd asplode_raise +PYCOMMANDPATH = $(TESTPATH) $(TESTPATH)/subdir + +all: + @$(CMD) info-exception diff --git a/build/pymake/tests/native-command-return-fail1.mk b/build/pymake/tests/native-command-return-fail1.mk new file mode 100644 index 000000000..0cf085ae2 --- /dev/null +++ b/build/pymake/tests/native-command-return-fail1.mk @@ -0,0 +1,8 @@ +#T gmake skip +#T returncode: 2 + +CMD = %pycmd asplode_return +PYCOMMANDPATH = $(TESTPATH) $(TESTPATH)/subdir + +all: + $(CMD) 1 diff --git a/build/pymake/tests/native-command-return-fail2.mk b/build/pymake/tests/native-command-return-fail2.mk new file mode 100644 index 000000000..c071fc879 --- /dev/null +++ b/build/pymake/tests/native-command-return-fail2.mk @@ -0,0 +1,8 @@ +#T gmake skip +#T returncode: 2 + +CMD = %pycmd asplode_return +PYCOMMANDPATH = $(TESTPATH) $(TESTPATH)/subdir + +all: + $(CMD) not-an-integer diff --git a/build/pymake/tests/native-command-return.mk b/build/pymake/tests/native-command-return.mk new file mode 100644 index 000000000..3e4d2e0c4 --- /dev/null +++ b/build/pymake/tests/native-command-return.mk @@ -0,0 +1,11 @@ +#T gmake skip + +CMD = %pycmd asplode_return +PYCOMMANDPATH = $(TESTPATH) $(TESTPATH)/subdir + +all: + $(CMD) 0 + -$(CMD) 1 + $(CMD) None + -$(CMD) not-an-integer + @echo TEST-PASS diff --git a/build/pymake/tests/native-command-shell-glob.mk b/build/pymake/tests/native-command-shell-glob.mk new file mode 100644 index 000000000..4bcdad8b9 --- /dev/null +++ b/build/pymake/tests/native-command-shell-glob.mk @@ -0,0 +1,11 @@ +#T gmake skip +all: + mkdir shell-glob-test + touch shell-glob-test/foo.txt + touch shell-glob-test/bar.txt + touch shell-glob-test/a.foo + touch shell-glob-test/b.foo + $(RM) shell-glob-test/*.txt + $(RM) shell-glob-test/?.foo + rmdir shell-glob-test + @echo TEST-PASS diff --git a/build/pymake/tests/native-command-sys-exit-fail1.mk b/build/pymake/tests/native-command-sys-exit-fail1.mk new file mode 100644 index 000000000..8e74800ed --- /dev/null +++ b/build/pymake/tests/native-command-sys-exit-fail1.mk @@ -0,0 +1,8 @@ +#T gmake skip +#T returncode: 2 + +CMD = %pycmd asplode +PYCOMMANDPATH = $(TESTPATH) $(TESTPATH)/subdir + +all: + $(CMD) 1 diff --git a/build/pymake/tests/native-command-sys-exit-fail2.mk b/build/pymake/tests/native-command-sys-exit-fail2.mk new file mode 100644 index 000000000..0a04395ad --- /dev/null +++ b/build/pymake/tests/native-command-sys-exit-fail2.mk @@ -0,0 +1,8 @@ +#T gmake skip +#T returncode: 2 + +CMD = %pycmd asplode +PYCOMMANDPATH = $(TESTPATH) $(TESTPATH)/subdir + +all: + $(CMD) not-an-integer diff --git a/build/pymake/tests/native-command-sys-exit.mk b/build/pymake/tests/native-command-sys-exit.mk new file mode 100644 index 000000000..c04913aca --- /dev/null +++ b/build/pymake/tests/native-command-sys-exit.mk @@ -0,0 +1,11 @@ +#T gmake skip + +CMD = %pycmd asplode +PYCOMMANDPATH = $(TESTPATH) $(TESTPATH)/subdir + +all: + $(CMD) 0 + -$(CMD) 1 + $(CMD) None + -$(CMD) not-an-integer + @echo TEST-PASS diff --git a/build/pymake/tests/native-environment.mk b/build/pymake/tests/native-environment.mk new file mode 100644 index 000000000..36bd5894a --- /dev/null +++ b/build/pymake/tests/native-environment.mk @@ -0,0 +1,11 @@ +#T gmake skip +export EXPECTED := some data + +PYCOMMANDPATH = $(TESTPATH) + +all: + %pycmd writeenvtofile results EXPECTED + test "$$(cat results)" = "$(EXPECTED)" + %pycmd writesubprocessenvtofile results EXPECTED + test "$$(cat results)" = "$(EXPECTED)" + @echo TEST-PASS diff --git a/build/pymake/tests/native-pycommandpath-sep.mk b/build/pymake/tests/native-pycommandpath-sep.mk new file mode 100644 index 000000000..b1c2c2b97 --- /dev/null +++ b/build/pymake/tests/native-pycommandpath-sep.mk @@ -0,0 +1,21 @@ +#T gmake skip +EXPECTED := some data + +# verify that we can load native command modules from +# multiple directories in PYCOMMANDPATH separated by the native +# path separator +ifdef __WIN32__ +PS:=; +else +PS:=: +endif +CMD = %pycmd writetofile +CMD2 = %pymod writetofile +PYCOMMANDPATH = $(TESTPATH)$(PS)$(TESTPATH)/subdir + +all: + $(CMD) results $(EXPECTED) + test "$$(cat results)" = "$(EXPECTED)" + $(CMD2) results2 $(EXPECTED) + test "$$(cat results2)" = "$(EXPECTED)" + @echo TEST-PASS diff --git a/build/pymake/tests/native-pycommandpath.mk b/build/pymake/tests/native-pycommandpath.mk new file mode 100644 index 000000000..dd0fbc9f9 --- /dev/null +++ b/build/pymake/tests/native-pycommandpath.mk @@ -0,0 +1,15 @@ +#T gmake skip +EXPECTED := some data + +# verify that we can load native command modules from +# multiple space-separated directories in PYCOMMANDPATH +CMD = %pycmd writetofile +CMD2 = %pymod writetofile +PYCOMMANDPATH = $(TESTPATH) $(TESTPATH)/subdir + +all: + $(CMD) results $(EXPECTED) + test "$$(cat results)" = "$(EXPECTED)" + $(CMD2) results2 $(EXPECTED) + test "$$(cat results2)" = "$(EXPECTED)" + @echo TEST-PASS diff --git a/build/pymake/tests/native-simple.mk b/build/pymake/tests/native-simple.mk new file mode 100644 index 000000000..626a58670 --- /dev/null +++ b/build/pymake/tests/native-simple.mk @@ -0,0 +1,12 @@ +ifndef TOUCH
+TOUCH = touch
+endif
+
+all: testfile {testfile2} (testfile3)
+ test -f testfile
+ test -f {testfile2}
+ test -f "(testfile3)"
+ @echo TEST-PASS
+
+testfile {testfile2} (testfile3):
+ $(TOUCH) "$@"
diff --git a/build/pymake/tests/native-touch.mk b/build/pymake/tests/native-touch.mk new file mode 100644 index 000000000..811161ece --- /dev/null +++ b/build/pymake/tests/native-touch.mk @@ -0,0 +1,15 @@ +TOUCH ?= touch + +foo: + $(TOUCH) bar + $(TOUCH) baz + $(MAKE) -f $(TESTPATH)/native-touch.mk baz + $(TOUCH) -t 198007040802 baz + $(MAKE) -f $(TESTPATH)/native-touch.mk baz + +bar: + $(TOUCH) $@ + +baz: bar + echo TEST-PASS + $(TOUCH) $@ diff --git a/build/pymake/tests/newlines.mk b/build/pymake/tests/newlines.mk new file mode 100644 index 000000000..5d8195c94 --- /dev/null +++ b/build/pymake/tests/newlines.mk @@ -0,0 +1,30 @@ +#T gmake skip + +# Test that we handle \\\n properly + +all: dep1 dep2 dep3 + cat testfile + test `cat testfile` = "data"; + test "$$(cat results)" = "$(EXPECTED)"; + @echo TEST-PASS + +# Test that something that still needs to go to the shell works +testfile: + printf "data" \ + >>$@ + +dep1: testfile + +# Test that something that does not need to go to the shell works +dep2: + $(echo foo) \ + $(echo bar) + +export EXPECTED := some data + +CMD = %pycmd writeenvtofile +PYCOMMANDPATH = $(TESTPATH) + +dep3: + $(CMD) \ + results EXPECTED diff --git a/build/pymake/tests/no-remake.mk b/build/pymake/tests/no-remake.mk new file mode 100644 index 000000000..c8df81bc3 --- /dev/null +++ b/build/pymake/tests/no-remake.mk @@ -0,0 +1,7 @@ +$(shell date >testfile) + +all: testfile + @echo TEST-PASS + +testfile: + @echo TEST-FAIL "We shouldn't have remade this!" diff --git a/build/pymake/tests/nosuchfile.mk b/build/pymake/tests/nosuchfile.mk new file mode 100644 index 000000000..cca9ce1e9 --- /dev/null +++ b/build/pymake/tests/nosuchfile.mk @@ -0,0 +1,4 @@ +#T returncode: 2 + +all: + reallythereisnosuchcommand diff --git a/build/pymake/tests/notargets.mk b/build/pymake/tests/notargets.mk new file mode 100644 index 000000000..8e55d944f --- /dev/null +++ b/build/pymake/tests/notargets.mk @@ -0,0 +1,5 @@ +$(NULL): foo.c + @echo TEST-FAIL + +all: + @echo TEST-PASS diff --git a/build/pymake/tests/notparallel.mk b/build/pymake/tests/notparallel.mk new file mode 100644 index 000000000..4fd8b1a8d --- /dev/null +++ b/build/pymake/tests/notparallel.mk @@ -0,0 +1,8 @@ +#T commandline: ['-j3'] + +include $(TESTPATH)/serial-rule-execution.mk + +all:: + $(MAKE) -f $(TESTPATH)/parallel-simple.mk + +.NOTPARALLEL: diff --git a/build/pymake/tests/oneline-command-continuations.mk b/build/pymake/tests/oneline-command-continuations.mk new file mode 100644 index 000000000..c11f3df52 --- /dev/null +++ b/build/pymake/tests/oneline-command-continuations.mk @@ -0,0 +1,5 @@ +all: test + @echo TEST-PASS + +test: ; test "Hello \ + world" = "Hello world" diff --git a/build/pymake/tests/override-propagate.mk b/build/pymake/tests/override-propagate.mk new file mode 100644 index 000000000..a1663ff41 --- /dev/null +++ b/build/pymake/tests/override-propagate.mk @@ -0,0 +1,37 @@ +#T commandline: ['-w', 'OVAR=oval'] + +OVAR=mval + +all: vartest run-override + $(MAKE) -f $(TESTPATH)/override-propagate.mk vartest + @echo TEST-PASS + +CLINE := OVAR=oval TESTPATH=$(TESTPATH) NATIVE_TESTPATH=$(NATIVE_TESTPATH) +ifdef __WIN32__ +CLINE += __WIN32__=1 +endif + +SORTED_CLINE := $(subst \,\\,$(sort $(CLINE))) + +vartest: + @echo MAKELEVEL: '$(MAKELEVEL)' + test '$(value MAKEFLAGS)' = 'w -- $$(MAKEOVERRIDES)' + test '$(origin MAKEFLAGS)' = 'file' + test '$(value MAKEOVERRIDES)' = '$${-*-command-variables-*-}' + test "$(sort $(MAKEOVERRIDES))" = "$(SORTED_CLINE)" + test '$(origin MAKEOVERRIDES)' = 'environment' + test '$(origin -*-command-variables-*-)' = 'automatic' + test "$(origin OVAR)" = "command line" + test "$(OVAR)" = "oval" + +run-override: MAKEOVERRIDES= +run-override: + test "$(OVAR)" = "oval" + $(MAKE) -f $(TESTPATH)/override-propagate.mk otest + +otest: + test '$(value MAKEFLAGS)' = 'w' + test '$(value MAKEOVERRIDES)' = '$${-*-command-variables-*-}' + test '$(MAKEOVERRIDES)' = '' + test '$(origin -*-command-variables-*-)' = 'undefined' + test "$(OVAR)" = "mval" diff --git a/build/pymake/tests/parallel-dep-resolution.mk b/build/pymake/tests/parallel-dep-resolution.mk new file mode 100644 index 000000000..7967eba2d --- /dev/null +++ b/build/pymake/tests/parallel-dep-resolution.mk @@ -0,0 +1,8 @@ +#T commandline: ['-j3'] +#T returncode: 2 + +all: t1 t2 + +t1: + sleep 1 + touch t1 t2 diff --git a/build/pymake/tests/parallel-dep-resolution2.mk b/build/pymake/tests/parallel-dep-resolution2.mk new file mode 100644 index 000000000..7d61e6b3e --- /dev/null +++ b/build/pymake/tests/parallel-dep-resolution2.mk @@ -0,0 +1,9 @@ +#T commandline: ['-j3'] +#T returncode: 2 + +all:: + sleep 1 + touch somefile + +all:: somefile + @echo TEST-PASS diff --git a/build/pymake/tests/parallel-native.mk b/build/pymake/tests/parallel-native.mk new file mode 100644 index 000000000..d50cfbdbb --- /dev/null +++ b/build/pymake/tests/parallel-native.mk @@ -0,0 +1,21 @@ +#T commandline: ['-j2'] + +# ensure that calling python commands doesn't block other targets +ifndef SLEEP +SLEEP := sleep +endif + +PRINTF = printf "$@:0:" >>results +EXPECTED = target2:0:target1:0: + +all:: target1 target2 + cat results + test "$$(cat results)" = "$(EXPECTED)" + @echo TEST-PASS + +target1: + $(SLEEP) 0.1 + $(PRINTF) + +target2: + $(PRINTF) diff --git a/build/pymake/tests/parallel-simple.mk b/build/pymake/tests/parallel-simple.mk new file mode 100644 index 000000000..f1aafc5f1 --- /dev/null +++ b/build/pymake/tests/parallel-simple.mk @@ -0,0 +1,27 @@ +#T commandline: ['-j2'] + +# CAUTION: this makefile is also used by serial-toparallel.mk + +define SLOWMAKE +printf "$@:0:" >>results +sleep 0.5 +printf "$@:1:" >>results +sleep 0.5 +printf "$@:2:" >>results +endef + +EXPECTED = target1:0:target2:0:target1:1:target2:1:target1:2:target2:2: + +all:: target1 target2 + cat results + test "$$(cat results)" = "$(EXPECTED)" + @echo TEST-PASS + +target1: + $(SLOWMAKE) + +target2: + sleep 0.1 + $(SLOWMAKE) + +.PHONY: all diff --git a/build/pymake/tests/parallel-submake.mk b/build/pymake/tests/parallel-submake.mk new file mode 100644 index 000000000..65cb2cf7c --- /dev/null +++ b/build/pymake/tests/parallel-submake.mk @@ -0,0 +1,17 @@ +#T commandline: ['-j2'] + +# A submake shouldn't return control to the parent until it has actually finished doing everything. + +all: + -$(MAKE) -f $(TESTPATH)/parallel-submake.mk subtarget + cat results + test "$$(cat results)" = "0123" + @echo TEST-PASS + +subtarget: succeed-slowly fail-quickly + +succeed-slowly: + printf 0 >>results; sleep 1; printf 1 >>results; sleep 1; printf 2 >>results; sleep 1; printf 3 >>results + +fail-quickly: + exit 1 diff --git a/build/pymake/tests/parallel-toserial.mk b/build/pymake/tests/parallel-toserial.mk new file mode 100644 index 000000000..9a355eb33 --- /dev/null +++ b/build/pymake/tests/parallel-toserial.mk @@ -0,0 +1,31 @@ +#T commandline: ['-j4'] + +# Test that -j1 in a submake has the proper effect. + +define SLOWCMD +printf "$@:0:" >>$(RFILE) +sleep 0.5 +printf "$@:1:" >>$(RFILE) +endef + +all: p1 p2 +subtarget: s1 s2 + +p1 p2: RFILE = presult +s1 s2: RFILE = sresult + +p1 s1: + $(SLOWCMD) + +p2 s2: + sleep 0.1 + $(SLOWCMD) + +all: + $(MAKE) -j1 -f $(TESTPATH)/parallel-toserial.mk subtarget + printf "presult: %s\n" "$$(cat presult)" + test "$$(cat presult)" = "p1:0:p2:0:p1:1:p2:1:" + printf "sresult: %s\n" "$$(cat sresult)" + test "$$(cat sresult)" = "s1:0:s1:1:s2:0:s2:1:" + @echo TEST-PASS + diff --git a/build/pymake/tests/parallel-waiting.mk b/build/pymake/tests/parallel-waiting.mk new file mode 100644 index 000000000..40a6e0d50 --- /dev/null +++ b/build/pymake/tests/parallel-waiting.mk @@ -0,0 +1,21 @@ +#T commandline: ['-j2'] + +EXPECTED = target1:before:target2:1:target2:2:target2:3:target1:after + +all:: target1 target2 + cat results + test "$$(cat results)" = "$(EXPECTED)" + @echo TEST-PASS + +target1: + printf "$@:before:" >>results + sleep 4 + printf "$@:after" >>results + +target2: + sleep 0.2 + printf "$@:1:" >>results + sleep 0.1 + printf "$@:2:" >>results + sleep 0.1 + printf "$@:3:" >>results diff --git a/build/pymake/tests/parentheses.mk b/build/pymake/tests/parentheses.mk new file mode 100644 index 000000000..f207234ff --- /dev/null +++ b/build/pymake/tests/parentheses.mk @@ -0,0 +1,2 @@ +all: + @(echo TEST-PASS) diff --git a/build/pymake/tests/parsertests.py b/build/pymake/tests/parsertests.py new file mode 100644 index 000000000..ab6406be0 --- /dev/null +++ b/build/pymake/tests/parsertests.py @@ -0,0 +1,314 @@ +import pymake.data, pymake.parser, pymake.parserdata, pymake.functions +import unittest +import logging + +from cStringIO import StringIO + +def multitest(cls): + for name in cls.testdata.iterkeys(): + def m(self, name=name): + return self.runSingle(*self.testdata[name]) + + setattr(cls, 'test_%s' % name, m) + return cls + +class TestBase(unittest.TestCase): + def assertEqual(self, a, b, msg=""): + """Actually print the values which weren't equal, if things don't work out!""" + unittest.TestCase.assertEqual(self, a, b, "%s got %r expected %r" % (msg, a, b)) + +class DataTest(TestBase): + testdata = { + 'oneline': + ("He\tllo", "f", 1, 0, + ((0, "f", 1, 0), (2, "f", 1, 2), (3, "f", 1, 4))), + 'twoline': + ("line1 \n\tl\tine2", "f", 1, 4, + ((0, "f", 1, 4), (5, "f", 1, 9), (6, "f", 1, 10), (7, "f", 2, 0), (8, "f", 2, 4), (10, "f", 2, 8), (13, "f", 2, 11))), + } + + def runSingle(self, data, filename, line, col, results): + d = pymake.parser.Data(data, 0, len(data), pymake.parserdata.Location(filename, line, col)) + for pos, file, lineno, col in results: + loc = d.getloc(pos) + self.assertEqual(loc.path, file, "data file offset %i" % pos) + self.assertEqual(loc.line, lineno, "data line offset %i" % pos) + self.assertEqual(loc.column, col, "data col offset %i" % pos) +multitest(DataTest) + +class LineEnumeratorTest(TestBase): + testdata = { + 'simple': ( + 'Hello, world', [ + ('Hello, world', 1), + ] + ), + 'multi': ( + 'Hello\nhappy \n\nworld\n', [ + ('Hello', 1), + ('happy ', 2), + ('', 3), + ('world', 4), + ('', 5), + ] + ), + 'continuation': ( + 'Hello, \\\n world\nJellybeans!', [ + ('Hello, \\\n world', 1), + ('Jellybeans!', 3), + ] + ), + 'multislash': ( + 'Hello, \\\\\n world', [ + ('Hello, \\\\', 1), + (' world', 2), + ] + ) + } + + def runSingle(self, s, lines): + gotlines = [(d.s[d.lstart:d.lend], d.loc.line) for d in pymake.parser.enumeratelines(s, 'path')] + self.assertEqual(gotlines, lines) + +multitest(LineEnumeratorTest) + +class IterTest(TestBase): + testdata = { + 'plaindata': ( + pymake.parser.iterdata, + "plaindata # test\n", + "plaindata # test\n" + ), + 'makecomment': ( + pymake.parser.itermakefilechars, + "VAR = val # comment", + "VAR = val " + ), + 'makeescapedcomment': ( + pymake.parser.itermakefilechars, + "VAR = val \# escaped hash", + "VAR = val # escaped hash" + ), + 'makeescapedslash': ( + pymake.parser.itermakefilechars, + "VAR = val\\\\", + "VAR = val\\\\", + ), + 'makecontinuation': ( + pymake.parser.itermakefilechars, + "VAR = VAL \\\n continuation # comment \\\n continuation", + "VAR = VAL continuation " + ), + 'makecontinuation2': ( + pymake.parser.itermakefilechars, + "VAR = VAL \\ \\\n continuation", + "VAR = VAL \\ continuation" + ), + 'makeawful': ( + pymake.parser.itermakefilechars, + "VAR = VAL \\\\# comment\n", + "VAR = VAL \\" + ), + 'command': ( + pymake.parser.itercommandchars, + "echo boo # comment", + "echo boo # comment", + ), + 'commandcomment': ( + pymake.parser.itercommandchars, + "echo boo \# comment", + "echo boo \# comment", + ), + 'commandcontinue': ( + pymake.parser.itercommandchars, + "echo boo # \\\n\t command 2", + "echo boo # \\\n command 2" + ), + } + + def runSingle(self, ifunc, idata, expected): + d = pymake.parser.Data.fromstring(idata, 'IterTest data') + + it = pymake.parser._alltokens.finditer(d.s, 0, d.lend) + actual = ''.join( [c for c, t, o, oo in ifunc(d, 0, ('dummy-token',), it)] ) + self.assertEqual(actual, expected) + + if ifunc == pymake.parser.itermakefilechars: + print "testing %r" % expected + self.assertEqual(pymake.parser.flattenmakesyntax(d, 0), expected) + +multitest(IterTest) + + +# 'define': ( +# pymake.parser.iterdefinechars, +# "endef", +# "" +# ), +# 'definenesting': ( +# pymake.parser.iterdefinechars, +# """define BAR # comment +#random text +#endef not what you think! +#endef # comment is ok\n""", +# """define BAR # comment +#random text +#endef not what you think!""" +# ), +# 'defineescaped': ( +# pymake.parser.iterdefinechars, +# """value \\ +#endef +#endef\n""", +# "value endef" +# ), + +class MakeSyntaxTest(TestBase): + # (string, startat, stopat, stopoffset, expansion + testdata = { + 'text': ('hello world', 0, (), None, ['hello world']), + 'singlechar': ('hello $W', 0, (), None, + ['hello ', + {'type': 'VariableRef', + '.vname': ['W']} + ]), + 'stopat': ('hello: world', 0, (':', '='), 6, ['hello']), + 'funccall': ('h $(flavor FOO)', 0, (), None, + ['h ', + {'type': 'FlavorFunction', + '[0]': ['FOO']} + ]), + 'escapedollar': ('hello$$world', 0, (), None, ['hello$world']), + 'varref': ('echo $(VAR)', 0, (), None, + ['echo ', + {'type': 'VariableRef', + '.vname': ['VAR']} + ]), + 'dynamicvarname': ('echo $($(VARNAME):.c=.o)', 0, (':',), None, + ['echo ', + {'type': 'SubstitutionRef', + '.vname': [{'type': 'VariableRef', + '.vname': ['VARNAME']} + ], + '.substfrom': ['.c'], + '.substto': ['.o']} + ]), + 'substref': (' $(VAR:VAL) := $(VAL)', 0, (':=', '+=', '=', ':'), 15, + [' ', + {'type': 'VariableRef', + '.vname': ['VAR:VAL']}, + ' ']), + 'vadsubstref': (' $(VAR:VAL) = $(VAL)', 15, (), None, + [{'type': 'VariableRef', + '.vname': ['VAL']}, + ]), + } + + def compareRecursive(self, actual, expected, path): + self.assertEqual(len(actual), len(expected), + "compareRecursive: %s %r" % (path, actual)) + for i in xrange(0, len(actual)): + ipath = path + [i] + + a, isfunc = actual[i] + e = expected[i] + if isinstance(e, str): + self.assertEqual(a, e, "compareRecursive: %s" % (ipath,)) + else: + self.assertEqual(type(a), getattr(pymake.functions, e['type']), + "compareRecursive: %s" % (ipath,)) + for k, v in e.iteritems(): + if k == 'type': + pass + elif k[0] == '[': + item = int(k[1:-1]) + proppath = ipath + [item] + self.compareRecursive(a[item], v, proppath) + elif k[0] == '.': + item = k[1:] + proppath = ipath + [item] + self.compareRecursive(getattr(a, item), v, proppath) + else: + raise Exception("Unexpected property at %s: %s" % (ipath, k)) + + def runSingle(self, s, startat, stopat, stopoffset, expansion): + d = pymake.parser.Data.fromstring(s, pymake.parserdata.Location('testdata', 1, 0)) + + a, t, offset = pymake.parser.parsemakesyntax(d, startat, stopat, pymake.parser.itermakefilechars) + self.compareRecursive(a, expansion, []) + self.assertEqual(offset, stopoffset) + +multitest(MakeSyntaxTest) + +class VariableTest(TestBase): + testdata = """ + VAR = value + VARNAME = TESTVAR + $(VARNAME) = testvalue + $(VARNAME:VAR=VAL) = moretesting + IMM := $(VARNAME) # this is a comment + MULTIVAR = val1 \\ + val2 + VARNAME = newname + """ + expected = {'VAR': 'value', + 'VARNAME': 'newname', + 'TESTVAR': 'testvalue', + 'TESTVAL': 'moretesting', + 'IMM': 'TESTVAR ', + 'MULTIVAR': 'val1 val2', + 'UNDEF': None} + + def runTest(self): + stmts = pymake.parser.parsestring(self.testdata, 'VariableTest') + + m = pymake.data.Makefile() + stmts.execute(m) + for k, v in self.expected.iteritems(): + flavor, source, val = m.variables.get(k) + if val is None: + self.assertEqual(val, v, 'variable named %s' % k) + else: + self.assertEqual(val.resolvestr(m, m.variables), v, 'variable named %s' % k) + +class SimpleRuleTest(TestBase): + testdata = """ + VAR = value +TSPEC = dummy +all: TSPEC = myrule +all:: test test2 $(VAR) + echo "Hello, $(TSPEC)" + +%.o: %.c + $(CC) -o $@ $< +""" + + def runTest(self): + stmts = pymake.parser.parsestring(self.testdata, 'SimpleRuleTest') + + m = pymake.data.Makefile() + stmts.execute(m) + self.assertEqual(m.defaulttarget, 'all', "Default target") + + self.assertTrue(m.hastarget('all'), "Has 'all' target") + target = m.gettarget('all') + rules = target.rules + self.assertEqual(len(rules), 1, "Number of rules") + prereqs = rules[0].prerequisites + self.assertEqual(prereqs, ['test', 'test2', 'value'], "Prerequisites") + commands = rules[0].commands + self.assertEqual(len(commands), 1, "Number of commands") + expanded = commands[0].resolvestr(m, target.variables) + self.assertEqual(expanded, 'echo "Hello, myrule"') + + irules = m.implicitrules + self.assertEqual(len(irules), 1, "Number of implicit rules") + + irule = irules[0] + self.assertEqual(len(irule.targetpatterns), 1, "%.o target pattern count") + self.assertEqual(len(irule.prerequisites), 1, "%.o prerequisite count") + self.assertEqual(irule.targetpatterns[0].match('foo.o'), 'foo', "%.o stem") + +if __name__ == '__main__': + logging.basicConfig(level=logging.DEBUG) + unittest.main() diff --git a/build/pymake/tests/path-length.mk b/build/pymake/tests/path-length.mk new file mode 100644 index 000000000..10c33b5ed --- /dev/null +++ b/build/pymake/tests/path-length.mk @@ -0,0 +1,9 @@ +#T gmake skip + +$(shell \ +mkdir foo; \ +touch tfile; \ +) + +all: foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../tfile + @echo TEST-PASS diff --git a/build/pymake/tests/pathdir/pathtest b/build/pymake/tests/pathdir/pathtest new file mode 100644 index 000000000..17037159f --- /dev/null +++ b/build/pymake/tests/pathdir/pathtest @@ -0,0 +1,2 @@ +#!/bin/sh +echo Called shell script: 2f7cdd0b-7277-48c1-beaf-56cb0dbacb24 diff --git a/build/pymake/tests/pathdir/src/Makefile b/build/pymake/tests/pathdir/src/Makefile new file mode 100644 index 000000000..6c24bd8f9 --- /dev/null +++ b/build/pymake/tests/pathdir/src/Makefile @@ -0,0 +1,2 @@ +pathtest.exe: pathtest.cpp + cl -EHsc -MT $^ diff --git a/build/pymake/tests/pathdir/src/pathtest.cpp b/build/pymake/tests/pathdir/src/pathtest.cpp new file mode 100644 index 000000000..bef8d8a11 --- /dev/null +++ b/build/pymake/tests/pathdir/src/pathtest.cpp @@ -0,0 +1,6 @@ +#include <cstdio> + +int main() { + std::printf("Called Windows executable: 2f7cdd0b-7277-48c1-beaf-56cb0dbacb24\n"); + return 0; +} diff --git a/build/pymake/tests/patsubst.mk b/build/pymake/tests/patsubst.mk new file mode 100644 index 000000000..0c3efdc4b --- /dev/null +++ b/build/pymake/tests/patsubst.mk @@ -0,0 +1,7 @@ +all: + test "$(patsubst foo,%.bar,foo)" = "%.bar" + test "$(patsubst \%word,replace,word %word other)" = "word replace other" + test "$(patsubst %.c,\%%.o,foo.c bar.o baz.cpp)" = "%foo.o bar.o baz.cpp" + test "$(patsubst host_%.c,host_%.o,dir/host_foo.c host_bar.c)" = "dir/host_foo.c host_bar.o" + test "$(patsubst foo,bar,dir/foo foo baz)" = "dir/foo bar baz" + @echo TEST-PASS diff --git a/build/pymake/tests/phony.mk b/build/pymake/tests/phony.mk new file mode 100644 index 000000000..36db4d121 --- /dev/null +++ b/build/pymake/tests/phony.mk @@ -0,0 +1,10 @@ +$(shell \ +touch dep; \ +sleep 2; \ +touch all; \ +) + +all:: dep + @echo TEST-PASS + +.PHONY: all diff --git a/build/pymake/tests/pycmd.py b/build/pymake/tests/pycmd.py new file mode 100644 index 000000000..20ed2fed0 --- /dev/null +++ b/build/pymake/tests/pycmd.py @@ -0,0 +1,35 @@ +import os, sys, subprocess + +def writetofile(args): + with open(args[0], 'w') as f: + f.write(' '.join(args[1:])) + +def writeenvtofile(args): + with open(args[0], 'w') as f: + f.write(os.environ[args[1]]) + +def writesubprocessenvtofile(args): + with open(args[0], 'w') as f: + p = subprocess.Popen([sys.executable, "-c", + "import os; print os.environ['%s']" % args[1]], + stdout=subprocess.PIPE, stderr=subprocess.PIPE) + stdout, stderr = p.communicate() + assert p.returncode == 0 + f.write(stdout) + +def convertasplode(arg): + try: + return int(arg) + except: + return (None if arg == "None" else arg) + +def asplode(args): + arg0 = convertasplode(args[0]) + sys.exit(arg0) + +def asplode_return(args): + arg0 = convertasplode(args[0]) + return arg0 + +def asplode_raise(args): + raise Exception(args[0]) diff --git a/build/pymake/tests/recursive-set.mk b/build/pymake/tests/recursive-set.mk new file mode 100644 index 000000000..853f90463 --- /dev/null +++ b/build/pymake/tests/recursive-set.mk @@ -0,0 +1,7 @@ +#T returncode: 2 + +FOO = $(FOO) + +all: + echo $(FOO) + @echo TEST-FAIL diff --git a/build/pymake/tests/recursive-set2.mk b/build/pymake/tests/recursive-set2.mk new file mode 100644 index 000000000..b68e34f0d --- /dev/null +++ b/build/pymake/tests/recursive-set2.mk @@ -0,0 +1,8 @@ +#T returncode: 2 + +FOO = $(BAR) +BAR = $(FOO) + +all: + echo $(FOO) + @echo TEST-FAIL diff --git a/build/pymake/tests/remake-mtime.mk b/build/pymake/tests/remake-mtime.mk new file mode 100644 index 000000000..47c775b93 --- /dev/null +++ b/build/pymake/tests/remake-mtime.mk @@ -0,0 +1,14 @@ +# mtime(dep1) < mtime(target) so the target should not be made +$(shell touch dep1; sleep 1; touch target) + +all: target + echo TEST-PASS + +target: dep1 + echo TEST-FAIL target should not have been made + +dep1: dep2 + @echo "Remaking dep1 (actually not)" + +dep2: + @echo "Making dep2 (actually not)" diff --git a/build/pymake/tests/rm-fail.mk b/build/pymake/tests/rm-fail.mk new file mode 100644 index 000000000..1a9aefb57 --- /dev/null +++ b/build/pymake/tests/rm-fail.mk @@ -0,0 +1,7 @@ +#T returncode: 2 +all: + mkdir newdir + test -d newdir + touch newdir/newfile + $(RM) newdir + @echo TEST-PASS diff --git a/build/pymake/tests/rm.mk b/build/pymake/tests/rm.mk new file mode 100644 index 000000000..6c7140e39 --- /dev/null +++ b/build/pymake/tests/rm.mk @@ -0,0 +1,21 @@ +all: +# $(RM) defaults to -f + $(RM) nosuchfile + touch newfile + test -f newfile + $(RM) newfile + test ! -f newfile + mkdir newdir + test -d newdir + touch newdir/newfile + mkdir newdir/subdir + $(RM) -r newdir/subdir + test ! -d newdir/subdir + test -d newdir + mkdir newdir/subdir1 newdir/subdir2 + $(RM) -r newdir/subdir1 newdir/subdir2 + test ! -d newdir/subdir1 -a ! -d newdir/subdir2 + test -d newdir + $(RM) -r newdir + test ! -d newdir + @echo TEST-PASS diff --git a/build/pymake/tests/runtests.py b/build/pymake/tests/runtests.py new file mode 100644 index 000000000..ab149ecfb --- /dev/null +++ b/build/pymake/tests/runtests.py @@ -0,0 +1,215 @@ +#!/usr/bin/env python +""" +Run the test(s) listed on the command line. If a directory is listed, the script will recursively +walk the directory for files named .mk and run each. + +For each test, we run gmake -f test.mk. By default, make must exit with an exit code of 0, and must print 'TEST-PASS'. + +Each test is run in an empty directory. + +The test file may contain lines at the beginning to alter the default behavior. These are all evaluated as python: + +#T commandline: ['extra', 'params', 'here'] +#T returncode: 2 +#T returncode-on: {'win32': 2} +#T environment: {'VAR': 'VALUE} +#T grep-for: "text" +""" + +from subprocess import Popen, PIPE, STDOUT +from optparse import OptionParser +import os, re, sys, shutil, glob + +class ParentDict(dict): + def __init__(self, parent, **kwargs): + self.d = dict(kwargs) + self.parent = parent + + def __setitem__(self, k, v): + self.d[k] = v + + def __getitem__(self, k): + if k in self.d: + return self.d[k] + + return self.parent[k] + +thisdir = os.path.dirname(os.path.abspath(__file__)) + +pymake = [sys.executable, os.path.join(os.path.dirname(thisdir), 'make.py')] +manifest = os.path.join(thisdir, 'tests.manifest') + +o = OptionParser() +o.add_option('-g', '--gmake', + dest="gmake", default="gmake") +o.add_option('-d', '--tempdir', + dest="tempdir", default="_mktests") +opts, args = o.parse_args() + +if len(args) == 0: + args = [thisdir] + +makefiles = [] +for a in args: + if os.path.isfile(a): + makefiles.append(a) + elif os.path.isdir(a): + makefiles.extend(sorted(glob.glob(os.path.join(a, '*.mk')))) + +def runTest(makefile, make, logfile, options): + """ + Given a makefile path, test it with a given `make` and return + (pass, message). + """ + + if os.path.exists(opts.tempdir): shutil.rmtree(opts.tempdir) + os.mkdir(opts.tempdir, 0755) + + logfd = open(logfile, 'w') + p = Popen(make + options['commandline'], stdout=logfd, stderr=STDOUT, env=options['env']) + logfd.close() + retcode = p.wait() + + if retcode != options['returncode']: + return False, "FAIL (returncode=%i)" % retcode + + logfd = open(logfile) + stdout = logfd.read() + logfd.close() + + if stdout.find('TEST-FAIL') != -1: + print stdout + return False, "FAIL (TEST-FAIL printed)" + + if options['grepfor'] and stdout.find(options['grepfor']) == -1: + print stdout + return False, "FAIL (%s not in output)" % options['grepfor'] + + if options['returncode'] == 0 and stdout.find('TEST-PASS') == -1: + print stdout + return False, 'FAIL (No TEST-PASS printed)' + + if options['returncode'] != 0: + return True, 'PASS (retcode=%s)' % retcode + + return True, 'PASS' + +print "%-30s%-28s%-28s" % ("Test:", "gmake:", "pymake:") + +gmakefails = 0 +pymakefails = 0 + +tre = re.compile('^#T (gmake |pymake )?([a-z-]+)(?:: (.*))?$') + +for makefile in makefiles: + # For some reason, MAKEFILE_LIST uses native paths in GNU make on Windows + # (even in MSYS!) so we pass both TESTPATH and NATIVE_TESTPATH + cline = ['-C', opts.tempdir, '-f', os.path.abspath(makefile), 'TESTPATH=%s' % thisdir.replace('\\','/'), 'NATIVE_TESTPATH=%s' % thisdir] + if sys.platform == 'win32': + #XXX: hack so we can specialize the separator character on windows. + # we really shouldn't need this, but y'know + cline += ['__WIN32__=1'] + + options = { + 'returncode': 0, + 'grepfor': None, + 'env': dict(os.environ), + 'commandline': cline, + 'pass': True, + 'skip': False, + } + + gmakeoptions = ParentDict(options) + pymakeoptions = ParentDict(options) + + dmap = {None: options, 'gmake ': gmakeoptions, 'pymake ': pymakeoptions} + + mdata = open(makefile) + for line in mdata: + line = line.strip() + m = tre.search(line) + if m is None: + break + + make, key, data = m.group(1, 2, 3) + d = dmap[make] + if data is not None: + data = eval(data) + if key == 'commandline': + assert make is None + d['commandline'].extend(data) + elif key == 'returncode': + d['returncode'] = data + elif key == 'returncode-on': + if sys.platform in data: + d['returncode'] = data[sys.platform] + elif key == 'environment': + for k, v in data.iteritems(): + d['env'][k] = v + elif key == 'grep-for': + d['grepfor'] = data + elif key == 'fail': + d['pass'] = False + elif key == 'skip': + d['skip'] = True + else: + print >>sys.stderr, "%s: Unexpected #T key: %s" % (makefile, key) + sys.exit(1) + + mdata.close() + + if gmakeoptions['skip']: + gmakepass, gmakemsg = True, '' + else: + gmakepass, gmakemsg = runTest(makefile, [opts.gmake], + makefile + '.gmakelog', gmakeoptions) + + if gmakeoptions['pass']: + if not gmakepass: + gmakefails += 1 + else: + if gmakepass: + gmakefails += 1 + gmakemsg = "UNEXPECTED PASS" + else: + gmakemsg = "KNOWN FAIL" + + if pymakeoptions['skip']: + pymakepass, pymakemsg = True, '' + else: + pymakepass, pymakemsg = runTest(makefile, pymake, + makefile + '.pymakelog', pymakeoptions) + + if pymakeoptions['pass']: + if not pymakepass: + pymakefails += 1 + else: + if pymakepass: + pymakefails += 1 + pymakemsg = "UNEXPECTED PASS" + else: + pymakemsg = "OK (known fail)" + + print "%-30.30s%-28.28s%-28.28s" % (os.path.basename(makefile), + gmakemsg, pymakemsg) + +print +print "Summary:" +print "%-30s%-28s%-28s" % ("", "gmake:", "pymake:") + +if gmakefails == 0: + gmakemsg = 'PASS' +else: + gmakemsg = 'FAIL (%i failures)' % gmakefails + +if pymakefails == 0: + pymakemsg = 'PASS' +else: + pymakemsg = 'FAIL (%i failures)' % pymakefails + +print "%-30.30s%-28.28s%-28.28s" % ('', gmakemsg, pymakemsg) + +shutil.rmtree(opts.tempdir) + +if gmakefails or pymakefails: + sys.exit(1) diff --git a/build/pymake/tests/serial-dep-resolution.mk b/build/pymake/tests/serial-dep-resolution.mk new file mode 100644 index 000000000..e65f1ed03 --- /dev/null +++ b/build/pymake/tests/serial-dep-resolution.mk @@ -0,0 +1,5 @@ +all: t1 t2 + @echo TEST-PASS + +t1: + touch t1 t2 diff --git a/build/pymake/tests/serial-doublecolon-execution.mk b/build/pymake/tests/serial-doublecolon-execution.mk new file mode 100644 index 000000000..1871cb13a --- /dev/null +++ b/build/pymake/tests/serial-doublecolon-execution.mk @@ -0,0 +1,18 @@ +#T commandline: ['-j3'] + +# Commands of double-colon rules are always executed in order. + +all: dc + cat status + test "$$(cat status)" = "all1:all2:" + @echo TEST-PASS + +dc:: slowt + printf "all1:" >> status + +dc:: + sleep 0.2 + printf "all2:" >> status + +slowt: + sleep 1 diff --git a/build/pymake/tests/serial-rule-execution.mk b/build/pymake/tests/serial-rule-execution.mk new file mode 100644 index 000000000..da5b177de --- /dev/null +++ b/build/pymake/tests/serial-rule-execution.mk @@ -0,0 +1,5 @@ +all:: + touch somefile + +all:: somefile + @echo TEST-PASS diff --git a/build/pymake/tests/serial-rule-execution2.mk b/build/pymake/tests/serial-rule-execution2.mk new file mode 100644 index 000000000..252a7df83 --- /dev/null +++ b/build/pymake/tests/serial-rule-execution2.mk @@ -0,0 +1,13 @@ +#T returncode: 2 + +# The dependencies of the command rule of a single-colon target are resolved before the rules without commands. + +all: export + +export: + sleep 1 + touch somefile + +all: somefile + test -f somefile + @echo TEST-PASS diff --git a/build/pymake/tests/serial-toparallel.mk b/build/pymake/tests/serial-toparallel.mk new file mode 100644 index 000000000..a980badc7 --- /dev/null +++ b/build/pymake/tests/serial-toparallel.mk @@ -0,0 +1,5 @@ +all:: + $(MAKE) -j2 -f $(TESTPATH)/parallel-simple.mk + +all:: results + @echo TEST-PASS diff --git a/build/pymake/tests/shellfunc.mk b/build/pymake/tests/shellfunc.mk new file mode 100644 index 000000000..1e408dbac --- /dev/null +++ b/build/pymake/tests/shellfunc.mk @@ -0,0 +1,7 @@ +all: testfile + test "$(shell cat $<)" = "Hello world" + test "$(shell printf "\n")" = "" + @echo TEST-PASS + +testfile: + printf "Hello\nworld\n" > $@ diff --git a/build/pymake/tests/simple-makeflags.mk b/build/pymake/tests/simple-makeflags.mk new file mode 100644 index 000000000..c7c92ec9d --- /dev/null +++ b/build/pymake/tests/simple-makeflags.mk @@ -0,0 +1,10 @@ +# There once was a time when MAKEFLAGS=w without any following spaces would +# cause us to treat w as a target, not a flag. Silly! + +MAKEFLAGS=w + +all: + $(MAKE) -f $(TESTPATH)/simple-makeflags.mk subt + @echo TEST-PASS + +subt: diff --git a/build/pymake/tests/sort.mk b/build/pymake/tests/sort.mk new file mode 100644 index 000000000..e1313ad5c --- /dev/null +++ b/build/pymake/tests/sort.mk @@ -0,0 +1,4 @@ +# sort should remove duplicates +all: + @test "$(sort x a y b z c a z b x c y)" = "a b c x y z" + @echo "TEST-PASS" diff --git a/build/pymake/tests/specified-target.mk b/build/pymake/tests/specified-target.mk new file mode 100644 index 000000000..3b23fbf69 --- /dev/null +++ b/build/pymake/tests/specified-target.mk @@ -0,0 +1,7 @@ +#T commandline: ['VAR=all', '$(VAR)'] + +all: + @echo TEST-FAIL: unexpected target 'all' + +$$(VAR): + @echo TEST-PASS: expected target '$$(VAR)' diff --git a/build/pymake/tests/static-pattern.mk b/build/pymake/tests/static-pattern.mk new file mode 100644 index 000000000..f613b8c9a --- /dev/null +++ b/build/pymake/tests/static-pattern.mk @@ -0,0 +1,5 @@ +#T returncode: 2 + +out/host_foo.o: host_%.o: host_%.c out + cp $< $@ + @echo TEST-FAIL diff --git a/build/pymake/tests/static-pattern2.mk b/build/pymake/tests/static-pattern2.mk new file mode 100644 index 000000000..08ed834fd --- /dev/null +++ b/build/pymake/tests/static-pattern2.mk @@ -0,0 +1,10 @@ +all: foo.out + test -f $^ + @echo TEST-PASS + +foo.out: %.out: %.in + test "$*" = "foo" + cp $^ $@ + +foo.in: + touch $@ diff --git a/build/pymake/tests/subdir/pymod.py b/build/pymake/tests/subdir/pymod.py new file mode 100644 index 000000000..1a47d8af2 --- /dev/null +++ b/build/pymake/tests/subdir/pymod.py @@ -0,0 +1,5 @@ +import testmodule + +def writetofile(args): + with open(args[0], 'w') as f: + f.write(' '.join(args[1:])) diff --git a/build/pymake/tests/subdir/testmodule.py b/build/pymake/tests/subdir/testmodule.py new file mode 100644 index 000000000..05b2f821a --- /dev/null +++ b/build/pymake/tests/subdir/testmodule.py @@ -0,0 +1,3 @@ +# This is an empty module. It is imported by pymod.py to test that if a module +# is loaded from the PYCOMMANDPATH, it can import other modules from the same +# directory correctly. diff --git a/build/pymake/tests/submake-path.makefile2 b/build/pymake/tests/submake-path.makefile2 new file mode 100644 index 000000000..1266db7d1 --- /dev/null +++ b/build/pymake/tests/submake-path.makefile2 @@ -0,0 +1,11 @@ +# -*- Mode: Makefile -*- + +shellresult := $(shell pathtest) +ifneq (2f7cdd0b-7277-48c1-beaf-56cb0dbacb24,$(filter $(shellresult),2f7cdd0b-7277-48c1-beaf-56cb0dbacb24)) +$(error pathtest not found in submake shell function) +endif + +all: + @pathtest + @pathtest | grep -q 2f7cdd0b-7277-48c1-beaf-56cb0dbacb24 + @echo TEST-PASS diff --git a/build/pymake/tests/submake-path.mk b/build/pymake/tests/submake-path.mk new file mode 100644 index 000000000..b6432276d --- /dev/null +++ b/build/pymake/tests/submake-path.mk @@ -0,0 +1,16 @@ +#T gmake skip +#T grep-for: "2f7cdd0b-7277-48c1-beaf-56cb0dbacb24" + +ifdef __WIN32__ +PS:=; +else +PS:=: +endif + +export PATH := $(TESTPATH)/pathdir$(PS)$(PATH) + +# This is similar to subprocess-path.mk, except we also check $(shell) +# invocations since they're affected by exported environment variables too, +# but only in submakes! +all: + $(MAKE) -f $(TESTPATH)/submake-path.makefile2 diff --git a/build/pymake/tests/submake.makefile2 b/build/pymake/tests/submake.makefile2 new file mode 100644 index 000000000..12ce94834 --- /dev/null +++ b/build/pymake/tests/submake.makefile2 @@ -0,0 +1,24 @@ +# -*- Mode: Makefile -*- + +$(info MAKEFLAGS = '$(MAKEFLAGS)') +$(info MAKE = '$(MAKE)') +$(info value MAKE = "$(value MAKE)") + +shellresult := $(shell echo -n $$EVAR) +ifneq ($(shellresult),eval) +$(error EVAR should be eval, is instead $(shellresult)) +endif + +all: + env + test "$(MAKELEVEL)" = "1" + echo "value(MAKE)" '$(value MAKE)' + echo "value(MAKE_COMMAND)" = '$(value MAKE_COMMAND)' + test "$(origin CVAR)" = "command line" + test "$(CVAR)" = "c val=spac\ed" + test "$(origin EVAR)" = "environment" + test "$(EVAR)" = "eval" + test "$(OVAL)" = "cline" + test "$(OVAL2)" = "cline2" + test "$(ALLVAR)" = "allspecific" + @echo TEST-PASS diff --git a/build/pymake/tests/submake.mk b/build/pymake/tests/submake.mk new file mode 100644 index 000000000..41e47134b --- /dev/null +++ b/build/pymake/tests/submake.mk @@ -0,0 +1,16 @@ +#T commandline: ['CVAR=c val=spac\\ed', 'OVAL=cline', 'OVAL2=cline2'] + +export EVAR = eval +override OVAL = makefile + +# exporting an override variable doesn't mean it's an override variable +override OVAL2 = makefile2 +export OVAL2 + +export ALLVAR +ALLVAR = general +all: ALLVAR = allspecific + +all: + test "$(MAKELEVEL)" = "0" + $(MAKE) -f $(TESTPATH)/submake.makefile2 diff --git a/build/pymake/tests/subprocess-path.mk b/build/pymake/tests/subprocess-path.mk new file mode 100644 index 000000000..f63921414 --- /dev/null +++ b/build/pymake/tests/subprocess-path.mk @@ -0,0 +1,32 @@ +#T gmake skip +#T grep-for: "2f7cdd0b-7277-48c1-beaf-56cb0dbacb24" + +ifdef __WIN32__ +PS:=; +else +PS:=: +endif + +export PATH := $(TESTPATH)/pathdir$(PS)$(PATH) + +# Test two commands. The first one shouldn't go through the shell and the +# second one should. The pathdir subdirectory has a Windows executable called +# pathtest.exe and a shell script called pathtest. We don't care which one is +# run, just that one of the two is (we use a uuid + grep-for to make sure +# that happens). +# +# FAQ: +# Q. Why skip GNU Make? +# A. Because $(TESTPATH) is a Windows-style path, and MSYS make doesn't take +# too kindly to Windows paths in the PATH environment variable. +# +# Q. Why use an exe and not a batch file? +# A. The use cases here were all exe files without the extension. Batch file +# lookup has broken semantics if the .bat extension isn't passed. +# +# Q. Why are the commands silent? +# A. So that we don't pass the grep-for test by mistake. +all: + @pathtest + @pathtest | grep -q 2f7cdd0b-7277-48c1-beaf-56cb0dbacb24 + @echo TEST-PASS diff --git a/build/pymake/tests/tab-intro.mk b/build/pymake/tests/tab-intro.mk new file mode 100644 index 000000000..1c25ce747 --- /dev/null +++ b/build/pymake/tests/tab-intro.mk @@ -0,0 +1,16 @@ +# Initial tab characters should be treated well. + + THIS = a value + + ifdef THIS + VAR = conditional value + endif + +all: + test "$(THIS)" = "another value" + test "$(VAR)" = "conditional value" + @echo TEST-PASS + +THAT = makefile syntax + + THIS = another value diff --git a/build/pymake/tests/target-specific.mk b/build/pymake/tests/target-specific.mk new file mode 100644 index 000000000..217ed155e --- /dev/null +++ b/build/pymake/tests/target-specific.mk @@ -0,0 +1,30 @@ +TESTVAR = anonval + +all: target.suffix target.suffix2 dummy host_test.py my.test1 my.test2 + @echo TEST-PASS + +target.suffix: TESTVAR = testval + +%.suffix: + test "$(TESTVAR)" = "testval" + +%.suffix2: TESTVAR = testval2 + +%.suffix2: + test "$(TESTVAR)" = "testval2" + +%my: TESTVAR = dummyval + +dummy: + test "$(TESTVAR)" = "dummyval" + +%.py: TESTVAR = pyval +host_%.py: TESTVAR = hostval + +host_test.py: + test "$(TESTVAR)" = "hostval" + +%.test1 %.test2: TESTVAR = %val + +my.test1 my.test2: + test "$(TESTVAR)" = "%val" diff --git a/build/pymake/tests/unexport.mk b/build/pymake/tests/unexport.mk new file mode 100644 index 000000000..424411603 --- /dev/null +++ b/build/pymake/tests/unexport.mk @@ -0,0 +1,15 @@ +#T environment: {'ENVVAR': 'envval'} + +VAR1 = val1 +VAR2 = val2 +VAR3 = val3 + +unexport VAR3 +export VAR1 VAR2 VAR3 +unexport VAR2 ENVVAR +unexport + +all: + test "$(ENVVAR)" = "envval" # unexport.mk + $(MAKE) -f $(TESTPATH)/unexport.submk + @echo TEST-PASS diff --git a/build/pymake/tests/unexport.submk b/build/pymake/tests/unexport.submk new file mode 100644 index 000000000..8db6163de --- /dev/null +++ b/build/pymake/tests/unexport.submk @@ -0,0 +1,15 @@ +# -@- Mode: Makefile -@- + +unexport VAR1 + +all: + env + test "$(VAR1)" = "val1" + test "$(origin VAR1)" = "environment" + test "$(VAR2)" = "" # VAR2 + test "$(VAR3)" = "val3" + test "$(ENVVAR)" = "" + $(MAKE) -f $(TESTPATH)/unexport.submk subt + +subt: + test "$(VAR1)" = "" diff --git a/build/pymake/tests/unterminated-dollar.mk b/build/pymake/tests/unterminated-dollar.mk new file mode 100644 index 000000000..dee9a207b --- /dev/null +++ b/build/pymake/tests/unterminated-dollar.mk @@ -0,0 +1,6 @@ +VAR = value$ +VAR2 = other + +all: + test "$(VAR)" = "value" + @echo TEST-PASS diff --git a/build/pymake/tests/var-change-flavor.mk b/build/pymake/tests/var-change-flavor.mk new file mode 100644 index 000000000..0cccf0bd6 --- /dev/null +++ b/build/pymake/tests/var-change-flavor.mk @@ -0,0 +1,12 @@ +VAR = value1 +VAR := value2 + +VAR2 := val1 +VAR2 = val2 + +default: + test "$(flavor VAR)" = "simple" + test "$(VAR)" = "value2" + test "$(flavor VAR2)" = "recursive" + test "$(VAR2)" = "val2" + @echo "TEST-PASS" diff --git a/build/pymake/tests/var-commandline.mk b/build/pymake/tests/var-commandline.mk new file mode 100644 index 000000000..e2cdad457 --- /dev/null +++ b/build/pymake/tests/var-commandline.mk @@ -0,0 +1,8 @@ +#T commandline: ['TESTVAR=$(MAKEVAL)', 'TESTVAR2:=$(MAKEVAL)'] + +MAKEVAL=testvalue + +all: + test "$(TESTVAR)" = "testvalue" + test "$(TESTVAR2)" = "" + @echo "TEST-PASS"
\ No newline at end of file diff --git a/build/pymake/tests/var-overrides.mk b/build/pymake/tests/var-overrides.mk new file mode 100644 index 000000000..bd0765d19 --- /dev/null +++ b/build/pymake/tests/var-overrides.mk @@ -0,0 +1,21 @@ +#T commandline: ['CLINEVAR=clineval', 'CLINEVAR2=clineval2'] + +# this doesn't actually test overrides yet, because they aren't implemented in pymake, +# but testing origins in general is important + +MVAR = mval +CLINEVAR = deadbeef + +override CLINEVAR2 = mval2 + +all: + test "$(origin NOVAR)" = "undefined" + test "$(CLINEVAR)" = "clineval" + test "$(origin CLINEVAR)" = "command line" + test "$(MVAR)" = "mval" + test "$(origin MVAR)" = "file" + test "$(@)" = "all" + test "$(origin @)" = "automatic" + test "$(origin CLINEVAR2)" = "override" + test "$(CLINEVAR2)" = "mval2" + @echo TEST-PASS diff --git a/build/pymake/tests/var-ref.mk b/build/pymake/tests/var-ref.mk new file mode 100644 index 000000000..3bc1886f9 --- /dev/null +++ b/build/pymake/tests/var-ref.mk @@ -0,0 +1,19 @@ +VAR = value +VAR2 == value + +VAR5 = $(NULL) $(NULL) +VARC = value # comment + +$(VAR3) + $(VAR4) +$(VAR5) + +VAR6$(VAR5) = val6 + +all: + test "$( VAR)" = "" + test "$(VAR2)" = "= value" + test "${VAR2}" = "= value" + test "$(VAR6 )" = "val6" + test "$(VARC)" = "value " + @echo TEST-PASS diff --git a/build/pymake/tests/var-set.mk b/build/pymake/tests/var-set.mk new file mode 100644 index 000000000..1603e7a35 --- /dev/null +++ b/build/pymake/tests/var-set.mk @@ -0,0 +1,55 @@ +#T commandline: ['OBASIC=oval'] + +BASIC = val + +TEST = $(TEST) + +TEST2 = $(TES +TEST2 += T) + +TES T = val + +RECVAR = foo +RECVAR += var baz + +IMMVAR := bloo +IMMVAR += $(RECVAR) + +BASIC ?= notval + +all: BASIC = valall +all: RECVAR += $(BASIC) +all: IMMVAR += $(BASIC) +all: UNSET += more +all: OBASIC += allmore + +CHECKLIT = $(NULL) check +all: CHECKLIT += appendliteral + +RECVAR = blimey + +TESTEMPTY = \ + $(NULL) + +all: other + test "$(TEST2)" = "val" + test '$(value TEST2)' = '$$(TES T)' + test "$(RECVAR)" = "blimey valall" + test "$(IMMVAR)" = "bloo foo var baz valall" + test "$(UNSET)" = "more" + test "$(OBASIC)" = "oval" + test "$(CHECKLIT)" = " check appendliteral" + test "$(TESTEMPTY)" = "" + @echo TEST-PASS + +OVAR = oval +OVAR ?= onotval + +other: OVAR ?= ooval +other: LATERVAR ?= lateroverride + +LATERVAR = olater + +other: + test "$(OVAR)" = "oval" + test "$(LATERVAR)" = "lateroverride" diff --git a/build/pymake/tests/var-substitutions.mk b/build/pymake/tests/var-substitutions.mk new file mode 100644 index 000000000..d5627d7bd --- /dev/null +++ b/build/pymake/tests/var-substitutions.mk @@ -0,0 +1,49 @@ +SIMPLEVAR = aabb.cc +SIMPLEPERCENT = test_value%extra + +SIMPLE3SUBSTNAME = SIMPLEVAR:.dd +$(SIMPLE3SUBSTNAME) = weirdval + +PERCENT = dummy + +SIMPLESUBST = $(SIMPLEVAR:.cc=.dd) +SIMPLE2SUBST = $(SIMPLEVAR:.cc) +SIMPLE3SUBST = $(SIMPLEVAR:.dd) +SIMPLE4SUBST = $(SIMPLEVAR:.cc=.dd=.ee) +SIMPLE5SUBST = $(SIMPLEVAR:.cc=%.dd) +PERCENTSUBST = $(SIMPLEVAR:%.cc=%.ee) +PERCENT2SUBST = $(SIMPLEVAR:aa%.cc=ff%.f) +PERCENT3SUBST = $(SIMPLEVAR:aa%.dd=gg%.gg) +PERCENT4SUBST = $(SIMPLEVAR:aa%.cc=gg) +PERCENT5SUBST = $(SIMPLEVAR:aa) +PERCENT6SUBST = $(SIMPLEVAR:%.cc=%.dd=%.ee) +PERCENT7SUBST = $(SIMPLEVAR:$(PERCENT).cc=%.dd) +PERCENT8SUBST = $(SIMPLEVAR:%.cc=$(PERCENT).dd) +PERCENT9SUBST = $(SIMPLEVAR:$(PERCENT).cc=$(PERCENT).dd) +PERCENT10SUBST = $(SIMPLEVAR:%%.bb.cc=zz.bb.cc) +PERCENT11SUBST = $(SIMPLEPERCENT:test%value%extra=other%value%extra) + +SPACEDVAR = $(NULL) ex1.c ex2.c $(NULL) +SPACEDSUBST = $(SPACEDVAR:.c=.o) + +all: + test "$(SIMPLESUBST)" = "aabb.dd" + test "$(SIMPLE2SUBST)" = "" + test "$(SIMPLE3SUBST)" = "weirdval" + test "$(SIMPLE4SUBST)" = "aabb.dd=.ee" + test "$(SIMPLE5SUBST)" = "aabb%.dd" + test "$(PERCENTSUBST)" = "aabb.ee" + test "$(PERCENT2SUBST)" = "ffbb.f" + test "$(PERCENT3SUBST)" = "aabb.cc" + test "$(PERCENT4SUBST)" = "gg" + test "$(PERCENT5SUBST)" = "" + test "$(PERCENT6SUBST)" = "aabb.dd=%.ee" + test "$(PERCENT7SUBST)" = "aabb.dd" + test "$(PERCENT8SUBST)" = "aabb.dd" + test "$(PERCENT9SUBST)" = "aabb.dd" + test "$(PERCENT10SUBST)" = "aabb.cc" + test "$(PERCENT11SUBST)" = "other_value%extra" + test "$(SPACEDSUBST)" = "ex1.o ex2.o" + @echo TEST-PASS + +PERCENT = % diff --git a/build/pymake/tests/vpath-directive-dynamic.mk b/build/pymake/tests/vpath-directive-dynamic.mk new file mode 100644 index 000000000..9aa1bf956 --- /dev/null +++ b/build/pymake/tests/vpath-directive-dynamic.mk @@ -0,0 +1,12 @@ +$(shell \ +mkdir subd1; \ +touch subd1/test.in; \ +) + +VVAR = %.in subd1 + +vpath $(VVAR) + +all: test.in + test "$<" = "subd1/test.in" + @echo TEST-PASS diff --git a/build/pymake/tests/vpath-directive.mk b/build/pymake/tests/vpath-directive.mk new file mode 100644 index 000000000..4c7d4bf39 --- /dev/null +++ b/build/pymake/tests/vpath-directive.mk @@ -0,0 +1,31 @@ +# On Windows, MSYS make takes Unix paths but Pymake takes Windows paths +VPSEP := $(if $(and $(__WIN32__),$(.PYMAKE)),;,:) + +$(shell \ +mkdir subd1 subd2 subd3; \ +printf "reallybaddata" >subd1/foo.in; \ +printf "gooddata" >subd2/foo.in; \ +printf "baddata" >subd3/foo.in; \ +touch subd1/foo.in2 subd2/foo.in2 subd3/foo.in2; \ +) + +vpath %.in subd + +vpath +vpath %.in subd2$(VPSEP)subd3 + +vpath %.in2 subd0 +vpath f%.in2 subd1 +vpath %.in2 $(VPSEP)subd2 + +%.out: %.in + test "$<" = "subd2/foo.in" + cp $< $@ + +%.out2: %.in2 + test "$<" = "subd1/foo.in2" + cp $< $@ + +all: foo.out foo.out2 + test "$$(cat foo.out)" = "gooddata" + @echo TEST-PASS diff --git a/build/pymake/tests/vpath.mk b/build/pymake/tests/vpath.mk new file mode 100644 index 000000000..06f52180c --- /dev/null +++ b/build/pymake/tests/vpath.mk @@ -0,0 +1,18 @@ +VPATH = foo bar + +$(shell \ +mkdir foo; touch foo/tfile1; \ +mkdir bar; touch bar/tfile2 bar/tfile3 bar/test.objtest; \ +sleep 2; \ +touch bar/test.source; \ +) + +all: tfile1 tfile2 tfile3 test.objtest test.source + test "$^" = "foo/tfile1 bar/tfile2 tfile3 test.objtest bar/test.source" + @echo TEST-PASS + +tfile3: test.objtest + +%.objtest: %.source + test "$<" = bar/test.source + test "$@" = test.objtest diff --git a/build/pymake/tests/vpath2.mk b/build/pymake/tests/vpath2.mk new file mode 100644 index 000000000..be73ffe5c --- /dev/null +++ b/build/pymake/tests/vpath2.mk @@ -0,0 +1,18 @@ +VPATH = foo bar + +$(shell \ +mkdir bar; touch bar/test.source; \ +sleep 2; \ +mkdir foo; touch foo/tfile1; \ +touch bar/tfile2 bar/tfile3 bar/test.objtest; \ +) + +all: tfile1 tfile2 tfile3 test.objtest test.source + test "$^" = "foo/tfile1 bar/tfile2 bar/tfile3 bar/test.objtest bar/test.source" + @echo TEST-PASS + +tfile3: test.objtest + +%.objtest: %.source + test "$<" = bar/test.source + test "$@" = test.objtest diff --git a/build/pymake/tests/wildcards.mk b/build/pymake/tests/wildcards.mk new file mode 100644 index 000000000..24ff3f14c --- /dev/null +++ b/build/pymake/tests/wildcards.mk @@ -0,0 +1,22 @@ +$(shell \ +mkdir foo; \ +touch a.c b.c c.out foo/d.c; \ +sleep 2; \ +touch c.in; \ +) + +VPATH = foo + +all: c.out prog + cat $< + test "$$(cat $<)" = "remadec.out" + @echo TEST-PASS + +*.out: %.out: %.in + test "$@" = c.out + test "$<" = c.in + printf "remade$@" >$@ + +prog: *.c + test "$^" = "a.c b.c" + touch $@ diff --git a/build/pymake/tests/windows-paths.mk b/build/pymake/tests/windows-paths.mk new file mode 100644 index 000000000..5f33a9050 --- /dev/null +++ b/build/pymake/tests/windows-paths.mk @@ -0,0 +1,5 @@ +all: + touch file.in + printf "%s: %s\n\ttrue" '$(CURDIR)/file.out' '$(CURDIR)/file.in' >test.mk + $(MAKE) -f test.mk $(CURDIR)/file.out + @echo TEST-PASS diff --git a/build/qemu-wrap b/build/qemu-wrap new file mode 100644 index 000000000..e33938955 --- /dev/null +++ b/build/qemu-wrap @@ -0,0 +1,24 @@ +#!/bin/bash +# this script creates a wrapper shell script for an executable. The idea is the actual executable cannot be +# executed natively (it was cross compiled), but we want to run tests natively. Running this script +# as part of the compilation process will move the non-native executable to a new location, and replace it +# with a script that will run it under qemu. +while [[ -n $1 ]]; do + case $1 in + --qemu) QEMU="$2"; shift 2;; + --libdir) LIBDIR="$2"; shift 2;; + --ld) LD="$2"; shift 2;; + *) exe="$1"; shift;; + esac +done +if [[ -z $LIBDIR ]]; then + echo "You need to specify a directory for the cross libraries when you configure the shell" + echo "You can do this with --with-cross-lib=" + exit 1 +fi +LD=${LD:-$LIBDIR/ld-linux.so.3} +mv $exe $exe.target +# Just hardcode the path to the executable. It'll be pretty obvious if it is doing the wrong thing. + +echo $'#!/bin/bash\n' $QEMU -E LD_LIBRARY_PATH="${LIBDIR}" "$LD" "$(readlink -f "$exe.target")" '"$@"' >"$exe" +chmod +x $exe
\ No newline at end of file diff --git a/build/stlport/Makefile.in b/build/stlport/Makefile.in new file mode 100644 index 000000000..d26b7bbdf --- /dev/null +++ b/build/stlport/Makefile.in @@ -0,0 +1,28 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at http://mozilla.org/MPL/2.0/. + +DEPTH = @DEPTH@ +topsrcdir = @top_srcdir@ +srcdir = @srcdir@ +VPATH = @srcdir@ + +include $(DEPTH)/config/autoconf.mk + +MODULES = stlport +FORCE_STATIC_LIB = 1 +STL_FLAGS = + +# Force to build a static library, instead of a fake library, without +# installing it in dist/lib. +LIBRARY = $(LIB_PREFIX)$(LIBRARY_NAME).$(LIB_SUFFIX) + +VPATH += $(STLPORT_SOURCES)/src + +CSRCS = $(notdir $(wildcard $(STLPORT_SOURCES)/src/*.c)) + +include $(topsrcdir)/config/rules.mk + +DEFINES += -D_GNU_SOURCE +CXXFLAGS += -fuse-cxa-atexit +INCLUDES += -I$(STLPORT_SOURCES)/stlport diff --git a/build/stlport/moz.build b/build/stlport/moz.build new file mode 100644 index 000000000..a39456035 --- /dev/null +++ b/build/stlport/moz.build @@ -0,0 +1,14 @@ +# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +CONFIGURE_SUBST_FILES += ['stl/config/_android.h'] + +CPP_SOURCES += [ + '$(notdir $(wildcard $(STLPORT_SOURCES)/src/*.cpp))', +] + +LIBRARY_NAME = 'stlport_static' + diff --git a/build/stlport/stl/config/_android.h.in b/build/stlport/stl/config/_android.h.in new file mode 100644 index 000000000..17ca9896f --- /dev/null +++ b/build/stlport/stl/config/_android.h.in @@ -0,0 +1,31 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_stl_config__android_h +#define mozilla_stl_config__android_h + +#include "@STLPORT_SOURCES@/stlport/stl/config/_android.h" + +// No rtti support +#undef _STLP_NO_RTTI +#define _STLP_NO_RTTI 1 + +// No throwing exceptions +#undef _STLP_NO_EXCEPTIONS +#define _STLP_NO_EXCEPTIONS 1 +#undef _STLP_NO_EXCEPTION_HEADER +#define _STLP_NO_EXCEPTION_HEADER 1 +#undef _STLP_NO_UNCAUGHT_EXCEPT_SUPPORT +#define _STLP_NO_UNCAUGHT_EXCEPT_SUPPORT 1 + +#undef _STLP_NATIVE_CPP_C_HEADER +#define _STLP_NATIVE_CPP_C_HEADER(header) <../../system/include/header> +#undef _STLP_NATIVE_CPP_RUNTIME_HEADER +#define _STLP_NATIVE_CPP_RUNTIME_HEADER(header) <../../system/include/header> + +// Use operator new instead of stlport own node allocator +#undef _STLP_USE_NEWALLOC +#define _STLP_USE_NEWALLOC 1 + +#endif /* mozilla_stl_config__android_h */ diff --git a/build/unix/Makefile.in b/build/unix/Makefile.in new file mode 100644 index 000000000..5f2f4ff83 --- /dev/null +++ b/build/unix/Makefile.in @@ -0,0 +1,21 @@ +# -*- makefile -*- +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +DEPTH = @DEPTH@ +topsrcdir = @top_srcdir@ +srcdir = @srcdir@ +VPATH = @srcdir@ + +include $(DEPTH)/config/autoconf.mk + +SDK_BINARY = run-mozilla.sh + +include $(topsrcdir)/config/rules.mk + +libs:: $(srcdir)/run-mozilla.sh + $(INSTALL) $< $(DIST)/bin + +# EOF diff --git a/build/unix/abs2rel.pl b/build/unix/abs2rel.pl new file mode 100644 index 000000000..1d511a03b --- /dev/null +++ b/build/unix/abs2rel.pl @@ -0,0 +1,35 @@ +#!env perl + +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +use File::Spec::Unix; +use strict; + +print "Usage: $0 dest_path start_path\n" if ($#ARGV+1 != 2); +my $finish = my_canonpath(shift); +my $start = my_canonpath(shift); + +my $res = File::Spec::Unix->abs2rel($finish, $start); + +#print STDERR "abs2rel($finish,$start) = $res\n"; +print "$res\n"; + +sub my_canonpath($) { + my ($file) = @_; + my (@inlist, @outlist, $dir); + + # Do what File::Spec::Unix->no_upwards should do + my @inlist = split(/\//, File::Spec::Unix->canonpath($file)); + foreach $dir (@inlist) { + if ($dir eq '..') { + pop @outlist; + } else { + push @outlist, $dir; + } + } + $file = join '/',@outlist; + return $file; +} + diff --git a/build/unix/add_phony_targets.py b/build/unix/add_phony_targets.py new file mode 100644 index 000000000..86fc328a5 --- /dev/null +++ b/build/unix/add_phony_targets.py @@ -0,0 +1,33 @@ +import pymake.data +import pymake.parser +import pymake.parserdata +import sys + +''' +Modifies the output of Sun Studio's -xM to look more like the output +of gcc's -MD -MP, adding phony targets for dependencies. +''' + + +def add_phony_targets(path): + print path + deps = set() + targets = set() + for stmt in pymake.parser.parsefile(path): + if isinstance(stmt, pymake.parserdata.Rule): + assert isinstance(stmt.depexp, pymake.data.StringExpansion) + assert isinstance(stmt.targetexp, pymake.data.StringExpansion) + for d in stmt.depexp.s.split(): + deps.add(d) + for t in stmt.targetexp.s.split(): + targets.add(t) + phony_targets = deps - targets + if not phony_targets: + return + with open(path, 'a') as f: + f.writelines('%s:\n' % d for d in phony_targets) + + +if __name__ == '__main__': + for f in sys.argv[1:]: + add_phony_targets(f) diff --git a/build/unix/aix.exp b/build/unix/aix.exp new file mode 100644 index 000000000..20f7cb431 --- /dev/null +++ b/build/unix/aix.exp @@ -0,0 +1,5 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +NSGetModule diff --git a/build/unix/build-clang/build-clang.py b/build/unix/build-clang/build-clang.py new file mode 100644 index 000000000..ca8029e66 --- /dev/null +++ b/build/unix/build-clang/build-clang.py @@ -0,0 +1,162 @@ +#!/usr/bin/python +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +llvm_revision = "170890" +moz_version = "moz0" + +############################################## + +import os +import os.path +import shutil +import tarfile +import subprocess +import platform +import sys +import json +import collections + +def check_run(args): + r = subprocess.call(args) + assert r == 0 + +def run_in(path, args): + d = os.getcwd() + os.chdir(path) + check_run(args) + os.chdir(d) + +def patch(patch, plevel, srcdir): + patch = os.path.realpath(patch) + check_run(['patch', '-d', srcdir, '-p%s' % plevel, '-i', patch, '--fuzz=0', + '-s']) + +def build_package(package_source_dir, package_build_dir, configure_args, + make_args): + if not os.path.exists(package_build_dir): + os.mkdir(package_build_dir) + run_in(package_build_dir, + ["%s/configure" % package_source_dir] + configure_args) + run_in(package_build_dir, ["make", "-j8"] + make_args) + run_in(package_build_dir, ["make", "install"]) + +def with_env(env, f): + old_env = os.environ.copy() + os.environ.update(env) + f() + os.environ.clear() + os.environ.update(old_env) + +def build_tar_package(tar, name, base, directory): + name = os.path.realpath(name) + run_in(base, [tar, "-cjf", name, directory]) + +def svn_co(url, directory, revision): + check_run(["svn", "co", "-r", revision, url, directory]) + +# The directories end up in the debug info, so the easy way of getting +# a reproducible build is to run it in a know absolute directory. +# We use a directory in /builds/slave because the mozilla infrastructure +# cleans it up automatically. +base_dir = "/builds/slave/moz-toolchain" + +source_dir = base_dir + "/src" +build_dir = base_dir + "/build" + +llvm_source_dir = source_dir + "/llvm" +clang_source_dir = source_dir + "/clang" +compiler_rt_source_dir = source_dir + "/compiler-rt" + +def build_one_stage(env, stage_dir, is_stage_one): + def f(): + build_one_stage_aux(stage_dir, is_stage_one) + with_env(env, f) + +def build_tooltool_manifest(): + basedir = os.path.split(os.path.realpath(sys.argv[0]))[0] + tooltool = basedir + '/tooltool.py' + setup = basedir + '/setup.sh' + manifest = 'clang.manifest' + check_run(['python', tooltool, '-m', manifest, 'add', + setup, 'clang.tar.bz2']) + data = json.load(file(manifest), object_pairs_hook=collections.OrderedDict) + data = [{'clang_version' : 'r%s' % llvm_revision }] + data + out = file(manifest,'w') + json.dump(data, out, indent=0) + out.write('\n') + + assert data[2]['filename'] == 'clang.tar.bz2' + os.rename('clang.tar.bz2', data[2]['digest']) + +isDarwin = platform.system() == "Darwin" + +def build_one_stage_aux(stage_dir, is_stage_one): + os.mkdir(stage_dir) + + build_dir = stage_dir + "/build" + inst_dir = stage_dir + "/clang" + + targets = ["x86", "x86_64"] + # The Darwin equivalents of binutils appear to have intermittent problems + # with objects in compiler-rt that are compiled for arm. Since the arm + # support is only necessary for iOS (which we don't support), only enable + # arm support on Linux. + if not isDarwin: + targets.append("arm") + + configure_opts = ["--enable-optimized", + "--enable-targets=" + ",".join(targets), + "--disable-assertions", + "--prefix=%s" % inst_dir, + "--with-gcc-toolchain=/tools/gcc-4.7.3-0moz1"] + build_package(llvm_source_dir, build_dir, configure_opts, + []) + +if isDarwin: + os.environ['MACOSX_DEPLOYMENT_TARGET'] = '10.7' + +if not os.path.exists(source_dir): + os.makedirs(source_dir) + svn_co("http://llvm.org/svn/llvm-project/llvm/branches/release_32", + llvm_source_dir, llvm_revision) + svn_co("http://llvm.org/svn/llvm-project/cfe/branches/release_32", + clang_source_dir, llvm_revision) + svn_co("http://llvm.org/svn/llvm-project/compiler-rt/branches/release_32", + compiler_rt_source_dir, llvm_revision) + os.symlink("../../clang", llvm_source_dir + "/tools/clang") + os.symlink("../../compiler-rt", llvm_source_dir + "/projects/compiler-rt") + patch("llvm-debug-frame.patch", 1, llvm_source_dir) + if not isDarwin: + patch("no-sse-on-linux.patch", 0, clang_source_dir) + +if os.path.exists(build_dir): + shutil.rmtree(build_dir) +os.makedirs(build_dir) + +stage1_dir = build_dir + '/stage1' +stage1_inst_dir = stage1_dir + '/clang' + +if isDarwin: + extra_cflags = "" + extra_cxxflags = "" + cc = "/usr/bin/clang" + cxx = "/usr/bin/clang++" +else: + extra_cflags = "-static-libgcc" + extra_cxxflags = "-static-libgcc -static-libstdc++" + cc = "/usr/bin/gcc" + cxx = "/usr/bin/g++" + +build_one_stage({"CC" : cc, + "CXX" : cxx }, + stage1_dir, True) + +stage2_dir = build_dir + '/stage2' +build_one_stage({"CC" : stage1_inst_dir + "/bin/clang %s" % extra_cflags, + "CXX" : stage1_inst_dir + "/bin/clang++ %s" % extra_cxxflags}, + stage2_dir, False) + +build_tar_package("tar", "clang.tar.bz2", stage2_dir, "clang") +build_tooltool_manifest() diff --git a/build/unix/build-clang/llvm-debug-frame.patch b/build/unix/build-clang/llvm-debug-frame.patch new file mode 100644 index 000000000..91a45b683 --- /dev/null +++ b/build/unix/build-clang/llvm-debug-frame.patch @@ -0,0 +1,13 @@ +diff --git a/lib/CodeGen/AsmPrinter/AsmPrinter.cpp b/lib/CodeGen/AsmPrinter/AsmPrinter.cpp +index d6d4510..c488d4a 100644 +--- a/lib/CodeGen/AsmPrinter/AsmPrinter.cpp ++++ b/lib/CodeGen/AsmPrinter/AsmPrinter.cpp +@@ -172,6 +172,8 @@ bool AsmPrinter::doInitialization(Module &M) { + OutStreamer.EmitFileDirective(M.getModuleIdentifier()); + } + ++ OutStreamer.EmitCFISections(true, true); ++ + GCModuleInfo *MI = getAnalysisIfAvailable<GCModuleInfo>(); + assert(MI && "AsmPrinter didn't require GCModuleInfo?"); + for (GCModuleInfo::iterator I = MI->begin(), E = MI->end(); I != E; ++I) diff --git a/build/unix/build-clang/no-sse-on-linux.patch b/build/unix/build-clang/no-sse-on-linux.patch new file mode 100644 index 000000000..1d6f9149f --- /dev/null +++ b/build/unix/build-clang/no-sse-on-linux.patch @@ -0,0 +1,13 @@ +Index: lib/Driver/Tools.cpp +=================================================================== +--- lib/Driver/Tools.cpp (revision 167750) ++++ lib/Driver/Tools.cpp (working copy) +@@ -1179,7 +1179,7 @@ + else if (getToolChain().getArch() == llvm::Triple::x86) + // All x86 devices running Android have core2 as their common + // denominator. This makes a better choice than pentium4. +- CPUName = isAndroid ? "core2" : "pentium4"; ++ CPUName = "i686"; + } + } + diff --git a/build/unix/build-clang/setup.sh b/build/unix/build-clang/setup.sh new file mode 100644 index 000000000..5269a5c12 --- /dev/null +++ b/build/unix/build-clang/setup.sh @@ -0,0 +1,4 @@ +#!/bin/sh + +rm -rf clang +tar -xjf clang.tar.bz2 diff --git a/build/unix/build-clang/tooltool.py b/build/unix/build-clang/tooltool.py new file mode 100644 index 000000000..1ef3f97a0 --- /dev/null +++ b/build/unix/build-clang/tooltool.py @@ -0,0 +1,564 @@ +#!/usr/bin/env python + +#tooltool is a lookaside cache implemented in Python +#Copyright (C) 2011 John H. Ford <john@johnford.info> +# +#This program is free software; you can redistribute it and/or +#modify it under the terms of the GNU General Public License +#as published by the Free Software Foundation version 2 +# +#This program is distributed in the hope that it will be useful, +#but WITHOUT ANY WARRANTY; without even the implied warranty of +#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +#GNU General Public License for more details. +# +#You should have received a copy of the GNU General Public License +#along with this program; if not, write to the Free Software +#Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +# An manifest file specifies files in that directory that are stored +# elsewhere. This file should only contain file in the directory +# which the manifest file resides in and it should be called 'manifest.manifest' + +__version__ = '1' + +import os +import optparse +import logging +import hashlib +import urllib2 +import ConfigParser +try: + import simplejson as json # I hear simplejson is faster +except ImportError: + import json + +log = logging.getLogger(__name__) + +class FileRecordJSONEncoderException(Exception): pass +class InvalidManifest(Exception): pass +class ExceptionWithFilename(Exception): + def __init__(self, filename): + Exception.__init__(self) + self.filename = filename + +class DigestMismatchException(ExceptionWithFilename): pass +class MissingFileException(ExceptionWithFilename): pass + +class FileRecord(object): + def __init__(self, filename, size, digest, algorithm): + object.__init__(self) + self.filename = filename + self.size = size + self.digest = digest + self.algorithm = algorithm + log.debug("creating %s 0x%x" % (self.__class__.__name__, id(self))) + + def __eq__(self, other): + if self is other: + return True + if self.filename == other.filename and \ + self.size == other.size and \ + self.digest == other.digest and \ + self.algorithm == other.algorithm: + return True + else: + return False + + def __ne__(self, other): + return not self.__eq__(other) + + def __str__(self): + return repr(self) + + def __repr__(self): + return "%s.%s(filename='%s', size='%s', digest='%s', algorithm='%s')" % (__name__, + self.__class__.__name__, + self.filename, self.size, self.digest, self.algorithm) + + def present(self): + # Doesn't check validity + return os.path.exists(self.filename) + + def validate_size(self): + if self.present(): + return self.size == os.path.getsize(self.filename) + else: + log.debug("trying to validate size on a missing file, %s", self.filename) + raise MissingFileException(filename=self.filename) + + def validate_digest(self): + if self.present(): + with open(self.filename, 'rb') as f: + return self.digest == digest_file(f, self.algorithm) + else: + log.debug("trying to validate digest on a missing file, %s', self.filename") + raise MissingFileException(filename=self.filename) + + def validate(self): + if self.validate_size(): + if self.validate_digest(): + return True + return False + + def describe(self): + if self.present() and self.validate(): + return "'%s' is present and valid" % self.filename + elif self.present(): + return "'%s' is present and invalid" % self.filename + else: + return "'%s' is absent" % self.filename + + +def create_file_record(filename, algorithm): + fo = open(filename, 'rb') + stored_filename = os.path.split(filename)[1] + fr = FileRecord(stored_filename, os.path.getsize(filename), digest_file(fo, algorithm), algorithm) + fo.close() + return fr + + +class FileRecordJSONEncoder(json.JSONEncoder): + def encode_file_record(self, obj): + if not issubclass(type(obj), FileRecord): + err = "FileRecordJSONEncoder is only for FileRecord and lists of FileRecords, not %s" % obj.__class__.__name__ + log.warn(err) + raise FileRecordJSONEncoderException(err) + else: + return {'filename': obj.filename, 'size': obj.size, 'algorithm': obj.algorithm, 'digest': obj.digest} + + def default(self, f): + if issubclass(type(f), list): + record_list = [] + for i in f: + record_list.append(self.encode_file_record(i)) + return record_list + else: + return self.encode_file_record(f) + + +class FileRecordJSONDecoder(json.JSONDecoder): + """I help the json module materialize a FileRecord from + a JSON file. I understand FileRecords and lists of + FileRecords. I ignore things that I don't expect for now""" + # TODO: make this more explicit in what it's looking for + # and error out on unexpected things + def process_file_records(self, obj): + if isinstance(obj, list): + record_list = [] + for i in obj: + record = self.process_file_records(i) + if issubclass(type(record), FileRecord): + record_list.append(record) + return record_list + if isinstance(obj, dict) and \ + len(obj.keys()) == 4 and \ + obj.has_key('filename') and \ + obj.has_key('size') and \ + obj.has_key('algorithm') and \ + obj.has_key('digest'): + rv = FileRecord(obj['filename'], obj['size'], obj['digest'], obj['algorithm']) + log.debug("materialized %s" % rv) + return rv + return obj + + def decode(self, s): + decoded = json.JSONDecoder.decode(self, s) + rv = self.process_file_records(decoded) + return rv + + +class Manifest(object): + + valid_formats = ('json',) + + def __init__(self, file_records=[]): + self.file_records = file_records + + def __eq__(self, other): + if self is other: + return True + if len(self.file_records) != len(other.file_records): + log.debug('Manifests differ in number of files') + return False + #TODO: Lists in a different order should be equal + for record in range(0,len(self.file_records)): + if self.file_records[record] != other.file_records[record]: + log.debug('FileRecords differ, %s vs %s' % (self.file_records[record], + other.file_records[record])) + return False + return True + + def __deepcopy__(self, memo): + # This is required for a deep copy + return Manifest(self.file_records[:]) + + def __copy__(self): + return Manifest(self.file_records) + + def copy(self): + return Manifest(self.file_records[:]) + + def present(self): + return all(i.present() for i in self.file_records) + + def validate_sizes(self): + return all(i.validate_size() for i in self.file_records) + + def validate_digests(self): + return all(i.validate_digest() for i in self.file_records) + + def validate(self): + return all(i.validate() for i in self.file_records) + + def sort(self): + #TODO: WRITE TESTS + self.file_records.sort(key=lambda x: x.size) + + def load(self, data_file, fmt='json'): + assert fmt in self.valid_formats + if fmt == 'json': + try: + self.file_records.extend(json.load(data_file, cls=FileRecordJSONDecoder)) + self.sort() + except ValueError: + raise InvalidManifest("trying to read invalid manifest file") + + def loads(self, data_string, fmt='json'): + assert fmt in self.valid_formats + if fmt == 'json': + try: + self.file_records.extend(json.loads(data_string, cls=FileRecordJSONDecoder)) + self.sort() + except ValueError: + raise InvalidManifest("trying to read invalid manifest file") + + def dump(self, output_file, fmt='json'): + assert fmt in self.valid_formats + self.sort() + if fmt == 'json': + rv = json.dump(self.file_records, output_file, indent=0, cls=FileRecordJSONEncoder) + print >> output_file, '' + return rv + + def dumps(self, fmt='json'): + assert fmt in self.valid_formats + self.sort() + if fmt == 'json': + return json.dumps(self.file_records, cls=FileRecordJSONEncoder) + + +def digest_file(f, a): + """I take a file like object 'f' and return a hex-string containing + of the result of the algorithm 'a' applied to 'f'.""" + h = hashlib.new(a) + chunk_size = 1024*10 + data = f.read(chunk_size) + while data: + h.update(data) + data = f.read(chunk_size) + if hasattr(f, 'name'): + log.debug('hashed %s with %s to be %s', f.name, a, h.hexdigest()) + else: + log.debug('hashed a file with %s to be %s', a, h.hexdigest()) + return h.hexdigest() + +# TODO: write tests for this function +def open_manifest(manifest_file): + """I know how to take a filename and load it into a Manifest object""" + if os.path.exists(manifest_file): + manifest = Manifest() + with open(manifest_file) as f: + manifest.load(f) + log.debug("loaded manifest from file '%s'" % manifest_file) + return manifest + else: + log.debug("tried to load absent file '%s' as manifest" % manifest_file) + raise InvalidManifest("manifest file '%s' does not exist" % manifest_file) + +# TODO: write tests for this function +def list_manifest(manifest_file): + """I know how print all the files in a location""" + try: + manifest = open_manifest(manifest_file) + except InvalidManifest: + log.error("failed to load manifest file at '%s'" % manifest_file) + return False + for f in manifest.file_records: + print "%s\t%s\t%s" % ("P" if f.present() else "-", + "V" if f.present() and f.validate() else "-", + f.filename) + return True + +def validate_manifest(manifest_file): + """I validate that all files in a manifest are present and valid but + don't fetch or delete them if they aren't""" + try: + manifest = open_manifest(manifest_file) + except InvalidManifest: + log.error("failed to load manifest file at '%s'" % manifest_file) + return False + invalid_files = [] + absent_files = [] + for f in manifest.file_records: + if not f.present(): + absent_files.append(f) + else: + if not f.validate(): + invalid_files.append(f) + if len(invalid_files + absent_files) == 0: + return True + else: + return False + +# TODO: write tests for this function +def add_files(manifest_file, algorithm, filenames): + # returns True if all files successfully added, False if not + # and doesn't catch library Exceptions. If any files are already + # tracked in the manifest, return will be False because they weren't + # added + all_files_added = True + # Create a old_manifest object to add to + if os.path.exists(manifest_file): + old_manifest = open_manifest(manifest_file) + else: + old_manifest = Manifest() + log.debug("creating a new manifest file") + new_manifest = Manifest() # use a different manifest for the output + for filename in filenames: + log.debug("adding %s" % filename) + path, name = os.path.split(filename) + new_fr = create_file_record(filename, algorithm) + log.debug("appending a new file record to manifest file") + add = True + for fr in old_manifest.file_records: + log.debug("manifest file has '%s'" % "', ".join([x.filename for x in old_manifest.file_records])) + if new_fr == fr and new_fr.validate(): + # TODO: Decide if this case should really cause a False return + log.info("file already in old_manifest file and matches") + add = False + elif new_fr == fr and not new_fr.validate(): + log.error("file already in old_manifest file but is invalid") + add = False + if filename == fr.filename: + log.error("manifest already contains file named %s" % filename) + add = False + if add: + new_manifest.file_records.append(new_fr) + log.debug("added '%s' to manifest" % filename) + else: + all_files_added = False + with open(manifest_file, 'wb') as output: + new_manifest.dump(output, fmt='json') + return all_files_added + + +# TODO: write tests for this function +def fetch_file(base_url, file_record, overwrite=False, grabchunk=1024*4): + # A file which is requested to be fetched that exists locally will be hashed. + # If the hash matches the requested file's hash, nothing will be done and the + # function will return. If the function is told to overwrite and there is a + # digest mismatch, the exiting file will be overwritten + if file_record.present(): + if file_record.validate(): + log.info("existing '%s' is valid, not fetching" % file_record.filename) + return True + if overwrite: + log.info("overwriting '%s' as requested" % file_record.filename) + else: + # All of the following is for a useful error message + with open(file_record.filename, 'rb') as f: + d = digest_file(f, file_record.algorithm) + log.error("digest mismatch between manifest(%s...) and local file(%s...)" % \ + (file_record.digest[:8], d[:8])) + log.debug("full digests: manifest (%s) local file (%s)" % (file_record.digest, d)) + # Let's bail! + return False + + # Generate the URL for the file on the server side + url = "%s/%s/%s" % (base_url, file_record.algorithm, file_record.digest) + + log.debug("fetching from '%s'" % url) + + # TODO: This should be abstracted to make generic retreival protocol handling easy + # Well, the file doesn't exist locally. Lets fetch it. + try: + f = urllib2.urlopen(url) + log.debug("opened %s for reading" % url) + with open(file_record.filename, 'wb') as out: + k = True + size = 0 + while k: + # TODO: print statistics as file transfers happen both for info and to stop + # buildbot timeouts + indata = f.read(grabchunk) + out.write(indata) + size += len(indata) + if indata == '': + k = False + if size != file_record.size: + log.error("transfer from %s to %s failed due to a difference of %d bytes" % (url, + file_record.filename, file_record.size - size)) + return False + log.info("fetched %s" % file_record.filename) + except (urllib2.URLError, urllib2.HTTPError) as e: + log.error("failed to fetch '%s': %s" % (file_record.filename, e), + exc_info=True) + return False + except IOError: + log.error("failed to write to '%s'" % file_record.filename, + exc_info=True) + return False + return True + + +# TODO: write tests for this function +def fetch_files(manifest_file, base_url, overwrite, filenames=[]): + # Lets load the manifest file + try: + manifest = open_manifest(manifest_file) + except InvalidManifest: + log.error("failed to load manifest file at '%s'" % manifest_file) + return False + # We want to track files that fail to be fetched as well as + # files that are fetched + failed_files = [] + + # Lets go through the manifest and fetch the files that we want + fetched_files = [] + for f in manifest.file_records: + if f.filename in filenames or len(filenames) == 0: + log.debug("fetching %s" % f.filename) + if fetch_file(base_url, f, overwrite): + fetched_files.append(f) + else: + failed_files.append(f.filename) + else: + log.debug("skipping %s" % f.filename) + + # Even if we get the file, lets ensure that it matches what the + # manifest specified + for localfile in fetched_files: + if not localfile.validate(): + log.error("'%s'" % localfile.describe()) + + # If we failed to fetch or validate a file, we need to fail + if len(failed_files) > 0: + log.error("The following files failed: '%s'" % "', ".join(failed_files)) + return False + return True + + +# TODO: write tests for this function +def process_command(options, args): + """ I know how to take a list of program arguments and + start doing the right thing with them""" + cmd = args[0] + cmd_args = args[1:] + log.debug("processing '%s' command with args '%s'" % (cmd, '", "'.join(cmd_args))) + log.debug("using options: %s" % options) + if cmd == 'list': + return list_manifest(options['manifest']) + if cmd == 'validate': + return validate_manifest(options['manifest']) + elif cmd == 'add': + return add_files(options['manifest'], options['algorithm'], cmd_args) + elif cmd == 'fetch': + if not options.has_key('base_url') or options.get('base_url') is None: + log.critical('fetch command requires url option') + return False + return fetch_files(options['manifest'], options['base_url'], options['overwrite'], cmd_args) + else: + log.critical('command "%s" is not implemented' % cmd) + return False + +# fetching api: +# http://hostname/algorithm/hash +# example: http://people.mozilla.org/sha1/1234567890abcedf +# This will make it possible to have the server allow clients to +# use different algorithms than what was uploaded to the server + +# TODO: Implement the following features: +# -optimization: do small files first, justification is that they are faster +# and cause a faster failure if they are invalid +# -store permissions +# -local renames i.e. call the file one thing on the server and +# something different locally +# -deal with the cases: +# -local data matches file requested with different filename +# -two different files with same name, different hash +# -?only ever locally to digest as filename, symlink to real name +# -?maybe deal with files as a dir of the filename with all files in that dir as the versions of that file +# - e.g. ./python-2.6.7.dmg/0123456789abcdef and ./python-2.6.7.dmg/abcdef0123456789 + +def main(): + # Set up logging, for now just to the console + ch = logging.StreamHandler() + cf = logging.Formatter("%(levelname)s - %(message)s") + ch.setFormatter(cf) + + # Set up option parsing + parser = optparse.OptionParser() + # I wish there was a way to say "only allow args to be + # sequential and at the end of the argv. + # OH! i could step through sys.argv and check for things starting without -/-- before things starting with them + parser.add_option('-q', '--quiet', default=False, + dest='quiet', action='store_true') + parser.add_option('-v', '--verbose', default=False, + dest='verbose', action='store_true') + parser.add_option('-m', '--manifest', default='manifest.tt', + dest='manifest', action='store', + help='specify the manifest file to be operated on') + parser.add_option('-d', '--algorithm', default='sha512', + dest='algorithm', action='store', + help='openssl hashing algorithm to use') + parser.add_option('-o', '--overwrite', default=False, + dest='overwrite', action='store_true', + help='if fetching, remote copy will overwrite a local copy that is different. ') + parser.add_option('--url', dest='base_url', action='store', + help='base url for fetching files') + parser.add_option('--ignore-config-files', action='store_true', default=False, + dest='ignore_cfg_files') + (options_obj, args) = parser.parse_args() + # Dictionaries are easier to work with + options = vars(options_obj) + + + # Use some of the option parser to figure out application + # log level + if options.get('verbose'): + ch.setLevel(logging.DEBUG) + elif options.get('quiet'): + ch.setLevel(logging.ERROR) + else: + ch.setLevel(logging.INFO) + log.addHandler(ch) + + cfg_file = ConfigParser.SafeConfigParser() + if not options.get("ignore_cfg_files"): + read_files = cfg_file.read(['/etc/tooltool', os.path.expanduser('~/.tooltool'), + os.path.join(os.getcwd(), '.tooltool')]) + log.debug("read in the config files '%s'" % '", '.join(read_files)) + else: + log.debug("skipping config files") + + for option in ('base_url', 'algorithm'): + if not options.get(option): + try: + options[option] = cfg_file.get('general', option) + log.debug("read '%s' as '%s' from cfg_file" % (option, options[option])) + except (ConfigParser.NoSectionError, ConfigParser.NoOptionError) as e: + log.debug("%s in config file" % e, exc_info=True) + + if not options.has_key('manifest'): + parser.error("no manifest file specified") + + if len(args) < 1: + parser.error('You must specify a command') + exit(0 if process_command(options, args) else 1) + +if __name__ == "__main__": + main() +else: + log.addHandler(logging.NullHandler()) + #log.addHandler(logging.StreamHandler()) diff --git a/build/unix/build-toolchain/binutils-deterministic.patch b/build/unix/build-toolchain/binutils-deterministic.patch new file mode 100644 index 000000000..f602013cb --- /dev/null +++ b/build/unix/build-toolchain/binutils-deterministic.patch @@ -0,0 +1,22 @@ +diff -ru a/binutils/ar.c b/binutils/ar.c +--- a/binutils/ar.c 2011-03-16 04:35:58.000000000 -0400 ++++ b/binutils/ar.c 2012-01-19 15:44:46.211226017 -0500 +@@ -98,7 +98,7 @@ + /* Operate in deterministic mode: write zero for timestamps, uids, + and gids for archive members and the archive symbol table, and write + consistent file modes. */ +-int deterministic = 0; ++int deterministic = TRUE; + + /* Nonzero means it's the name of an existing member; position new or moved + files with respect to this one. */ +@@ -634,9 +634,6 @@ + if (newer_only && operation != replace) + fatal (_("`u' is only meaningful with the `r' option.")); + +- if (newer_only && deterministic) +- fatal (_("`u' is not meaningful with the `D' option.")); +- + if (postype != pos_default) + posname = argv[arg_index++]; + diff --git a/build/unix/build-toolchain/build-gcc.py b/build/unix/build-toolchain/build-gcc.py new file mode 100644 index 000000000..cd1e63f9d --- /dev/null +++ b/build/unix/build-toolchain/build-gcc.py @@ -0,0 +1,315 @@ +#!/usr/bin/python +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + + +# The directories end up in the debug info, so the easy way of getting +# a reproducible build is to run it in a know absolute directory. +# We use a directory in /builds/slave because the mozilla infrastructure +# cleans it up automatically. +base_dir = "/builds/slave/moz-toolchain" + +source_dir = base_dir + "/src" +build_dir = base_dir + "/build" +aux_inst_dir = build_dir + '/aux_inst' +old_make = aux_inst_dir + '/bin/make' + +############################################## + +import urllib +import os +import os.path +import shutil +import tarfile +import subprocess + +def download_uri(uri): + fname = uri.split('/')[-1] + if (os.path.exists(fname)): + return fname + urllib.urlretrieve(uri, fname) + return fname + +def extract(tar, path): + t = tarfile.open(tar) + t.extractall(path) + +def check_run(args): + r = subprocess.call(args) + assert r == 0 + +def run_in(path, args): + d = os.getcwd() + os.chdir(path) + check_run(args) + os.chdir(d) + +def patch(patch, plevel, srcdir): + patch = os.path.realpath(patch) + check_run(['patch', '-d', srcdir, '-p%s' % plevel, '-i', patch, '--fuzz=0', + '-s']) + +def build_package(package_source_dir, package_build_dir, configure_args, + make = old_make): + if not os.path.exists(package_build_dir): + os.mkdir(package_build_dir) + run_in(package_build_dir, + ["%s/configure" % package_source_dir] + configure_args) + run_in(package_build_dir, [make, "-j8"]) + run_in(package_build_dir, [make, "install"]) + +def build_aux_tools(base_dir): + make_build_dir = base_dir + '/make_build' + build_package(make_source_dir, make_build_dir, + ["--prefix=%s" % aux_inst_dir], "make") + + run_in(unifdef_source_dir, ["make"]) + run_in(unifdef_source_dir, ["make", "prefix=%s" % aux_inst_dir, "install"]) + + tar_build_dir = base_dir + '/tar_build' + build_package(tar_source_dir, tar_build_dir, + ["--prefix=%s" % aux_inst_dir]) + + gawk_build_dir = base_dir + '/gawk_build' + build_package(gawk_source_dir, gawk_build_dir, + ["--prefix=%s" % aux_inst_dir]) + +def with_env(env, f): + old_env = os.environ.copy() + os.environ.update(env) + f() + os.environ.clear() + os.environ.update(old_env) + +def build_glibc(env, stage_dir, inst_dir): + def f(): + build_glibc_aux(stage_dir, inst_dir) + with_env(env, f) + +def build_glibc_aux(stage_dir, inst_dir): + glibc_build_dir = stage_dir + '/glibc' + build_package(glibc_source_dir, glibc_build_dir, + ["--disable-profile", + "--enable-add-ons=nptl", + "--without-selinux", + "--enable-kernel=%s" % linux_version, + "--libdir=%s/lib64" % inst_dir, + "--prefix=%s" % inst_dir]) + +def build_linux_headers_aux(inst_dir): + run_in(linux_source_dir, [old_make, "headers_check"]) + run_in(linux_source_dir, [old_make, "INSTALL_HDR_PATH=dest", + "headers_install"]) + shutil.move(linux_source_dir + "/dest/include", inst_dir + '/include') + +def build_linux_headers(inst_dir): + def f(): + build_linux_headers_aux(inst_dir) + with_env({"PATH" : aux_inst_dir + "/bin:%s" % os.environ["PATH"]}, f) + +def build_gcc(stage_dir, is_stage_one): + gcc_build_dir = stage_dir + '/gcc' + tool_inst_dir = stage_dir + '/inst' + lib_inst_dir = stage_dir + '/libinst' + gcc_configure_args = ["--prefix=%s" % tool_inst_dir, + "--enable-__cxa_atexit", + "--with-gmp=%s" % lib_inst_dir, + "--with-mpfr=%s" % lib_inst_dir, + "--with-mpc=%s" % lib_inst_dir, + "--enable-languages=c,c++", + "--disable-lto", + "--disable-multilib", + "--disable-bootstrap"] + if is_stage_one: + # We build the stage1 gcc without shared libraries. Otherwise its + # libgcc.so would depend on the system libc.so, which causes problems + # when it tries to use that libgcc.so and the libc we are about to + # build. + gcc_configure_args.append("--disable-shared") + + build_package(gcc_source_dir, gcc_build_dir, gcc_configure_args) + + if is_stage_one: + # The glibc build system uses -lgcc_eh, but at least in this setup + # libgcc.a has all it needs. + d = tool_inst_dir + "/lib/gcc/x86_64-unknown-linux-gnu/4.5.2/" + os.symlink(d + "libgcc.a", d + "libgcc_eh.a") + +def build_one_stage(env, stage_dir, is_stage_one): + def f(): + build_one_stage_aux(stage_dir, is_stage_one) + with_env(env, f) + +def build_one_stage_aux(stage_dir, is_stage_one): + os.mkdir(stage_dir) + + lib_inst_dir = stage_dir + '/libinst' + + gmp_build_dir = stage_dir + '/gmp' + build_package(gmp_source_dir, gmp_build_dir, + ["--prefix=%s" % lib_inst_dir, "--disable-shared"]) + mpfr_build_dir = stage_dir + '/mpfr' + build_package(mpfr_source_dir, mpfr_build_dir, + ["--prefix=%s" % lib_inst_dir, "--disable-shared", + "--with-gmp=%s" % lib_inst_dir]) + mpc_build_dir = stage_dir + '/mpc' + build_package(mpc_source_dir, mpc_build_dir, + ["--prefix=%s" % lib_inst_dir, "--disable-shared", + "--with-gmp=%s" % lib_inst_dir, + "--with-mpfr=%s" % lib_inst_dir]) + + tool_inst_dir = stage_dir + '/inst' + os.mkdir(tool_inst_dir) + os.mkdir(tool_inst_dir + '/lib64') + os.symlink('lib64', tool_inst_dir + '/lib') + + build_linux_headers(tool_inst_dir) + + # zlib's configure only works if run from the source dir, copy the source + zlib_build_dir = stage_dir + '/zlib' + shutil.copytree(zlib_source_dir, zlib_build_dir) + build_package(zlib_build_dir, zlib_build_dir, + ["--prefix=%s" % tool_inst_dir]) + + binutils_build_dir = stage_dir + '/binutils' + build_package(binutils_source_dir, binutils_build_dir, + ["--prefix=%s" % tool_inst_dir, + "--without-zlib"]) + + # During stage one we have to build gcc first, this glibc doesn't even + # build with gcc 4.6. During stage two, we have to build glibc first. + # The problem is that libstdc++ is built with xgcc and if glibc has + # not been built yet xgcc will use the system one. + if is_stage_one: + build_gcc(stage_dir, is_stage_one) + build_glibc({"CC" : tool_inst_dir + "/bin/gcc", + "CXX" : tool_inst_dir + "/bin/g++"}, + stage_dir, tool_inst_dir) + else: + build_glibc({}, stage_dir, tool_inst_dir) + build_gcc(stage_dir, is_stage_one) + +def build_tar_package(tar, name, base, directory): + name = os.path.realpath(name) + run_in(base, [tar, "-cf", name, "--mtime=2012-01-01", "--owner=root", + directory]) + +############################################## + +def build_source_dir(prefix, version): + return source_dir + '/' + prefix + version + +binutils_version = "2.21.1" +glibc_version = "2.5.1" +linux_version = "2.6.18" +tar_version = "1.26" +gawk_version = "3.1.5" +make_version = "3.81" +gcc_version = "4.5.2" +mpfr_version = "2.4.2" +zlib_version = "1.2.3" +gmp_version = "5.0.1" +mpc_version = "0.8.1" +unifdef_version = "2.6" + +binutils_source_uri = "http://ftp.gnu.org/gnu/binutils/binutils-%sa.tar.bz2" % \ + binutils_version +glibc_source_uri = "http://ftp.gnu.org/gnu/glibc/glibc-%s.tar.bz2" % \ + glibc_version +linux_source_uri = "http://www.kernel.org/pub/linux/kernel/v2.6/linux-%s.tar.bz2" % \ + linux_version +tar_source_uri = "http://ftp.gnu.org/gnu/tar/tar-%s.tar.bz2" % \ + tar_version +gawk_source_uri = "http://ftp.gnu.org/gnu/gawk/gawk-%s.tar.bz2" % \ + gawk_version +make_source_uri = "http://ftp.gnu.org/gnu/make/make-%s.tar.bz2" % \ + make_version +unifdef_source_uri = "http://dotat.at/prog/unifdef/unifdef-%s.tar.gz" % \ + unifdef_version +gcc_source_uri = "http://ftp.gnu.org/gnu/gcc/gcc-%s/gcc-%s.tar.bz2" % \ + (gcc_version, gcc_version) +mpfr_source_uri = "http://www.mpfr.org/mpfr-%s/mpfr-%s.tar.bz2" % \ + (mpfr_version, mpfr_version) +zlib_source_uri = "http://iweb.dl.sourceforge.net/project/libpng/zlib/%s/zlib-%s.tar.bz2" % (zlib_version, zlib_version) +gmp_source_uri = "http://ftp.gnu.org/gnu/gmp/gmp-%s.tar.bz2" % gmp_version +mpc_source_uri = "http://www.multiprecision.org/mpc/download/mpc-%s.tar.gz" % \ + mpc_version + +binutils_source_tar = download_uri(binutils_source_uri) +glibc_source_tar = download_uri(glibc_source_uri) +linux_source_tar = download_uri(linux_source_uri) +tar_source_tar = download_uri(tar_source_uri) +gawk_source_tar = download_uri(gawk_source_uri) +make_source_tar = download_uri(make_source_uri) +unifdef_source_tar = download_uri(unifdef_source_uri) +mpc_source_tar = download_uri(mpc_source_uri) +mpfr_source_tar = download_uri(mpfr_source_uri) +zlib_source_tar = download_uri(zlib_source_uri) +gmp_source_tar = download_uri(gmp_source_uri) +gcc_source_tar = download_uri(gcc_source_uri) + +binutils_source_dir = build_source_dir('binutils-', binutils_version) +glibc_source_dir = build_source_dir('glibc-', glibc_version) +linux_source_dir = build_source_dir('linux-', linux_version) +tar_source_dir = build_source_dir('tar-', tar_version) +gawk_source_dir = build_source_dir('gawk-', gawk_version) +make_source_dir = build_source_dir('make-', make_version) +unifdef_source_dir = build_source_dir('unifdef-', unifdef_version) +mpc_source_dir = build_source_dir('mpc-', mpc_version) +mpfr_source_dir = build_source_dir('mpfr-', mpfr_version) +zlib_source_dir = build_source_dir('zlib-', zlib_version) +gmp_source_dir = build_source_dir('gmp-', gmp_version) +gcc_source_dir = build_source_dir('gcc-', gcc_version) + +if not os.path.exists(source_dir): + os.makedirs(source_dir) + extract(binutils_source_tar, source_dir) + patch('binutils-deterministic.patch', 1, binutils_source_dir) + extract(glibc_source_tar, source_dir) + extract(linux_source_tar, source_dir) + patch('glibc-deterministic.patch', 1, glibc_source_dir) + run_in(glibc_source_dir, ["autoconf"]) + extract(tar_source_tar, source_dir) + extract(gawk_source_tar, source_dir) + extract(make_source_tar, source_dir) + extract(unifdef_source_tar, source_dir) + extract(mpc_source_tar, source_dir) + extract(mpfr_source_tar, source_dir) + extract(zlib_source_tar, source_dir) + extract(gmp_source_tar, source_dir) + extract(gcc_source_tar, source_dir) + patch('plugin_finish_decl.diff', 0, gcc_source_dir) + patch('libtool-74c8993c178a1386ea5e2363a01d919738402f30.patch', 1, gcc_source_dir) + patch('pr49911.diff', 1, gcc_source_dir) + patch('r159628-r163231-r171807.patch', 1, gcc_source_dir) + patch('gcc-fixinc.patch', 1, gcc_source_dir) + patch('gcc-include.patch', 1, gcc_source_dir) + +if os.path.exists(build_dir): + shutil.rmtree(build_dir) +os.makedirs(build_dir) + +build_aux_tools(build_dir) + +basic_path = aux_inst_dir + "/bin:/bin:/usr/bin" + +stage1_dir = build_dir + '/stage1' +build_one_stage({"PATH" : basic_path, + "CC" : "gcc", + "CXX" : "g++" }, + stage1_dir, True) + +for stage_num in range(2, 4): + prev_stage_dir = build_dir + '/stage' + str(stage_num - 1) + prev_stage_inst_dir = prev_stage_dir + '/inst' + cur_stage_dir = build_dir + '/stage' + str(stage_num) + build_one_stage({"PATH" : prev_stage_inst_dir + "/bin:" + basic_path, + "CC" : "gcc -fgnu89-inline", + "CXX" : "g++", + "RANLIB" : "true" }, + cur_stage_dir, False) + +stage3_dir = build_dir + '/stage3' +build_tar_package(aux_inst_dir + "/bin/tar", + "toolchain.tar", stage3_dir, "inst") diff --git a/build/unix/build-toolchain/gcc-fixinc.patch b/build/unix/build-toolchain/gcc-fixinc.patch new file mode 100644 index 000000000..3fb7f5654 --- /dev/null +++ b/build/unix/build-toolchain/gcc-fixinc.patch @@ -0,0 +1,12 @@ +diff -ru a/fixincludes/Makefile.in b/fixincludes/Makefile.in +--- a/fixincludes/Makefile.in 2009-07-30 18:33:49.000000000 -0400 ++++ b/fixincludes/Makefile.in 2012-02-27 14:59:09.371875951 -0500 +@@ -126,7 +126,7 @@ + fixlib.o : fixlib.c + + fixinc.sh : fixinc.in mkfixinc.sh Makefile +- srcdir="$(srcdir)" $(SHELL) $(srcdir)/mkfixinc.sh $(target) ++ echo "#!/bin/sh" > $@ + + $(srcdir)/fixincl.x: @MAINT@ fixincl.tpl inclhack.def + cd $(srcdir) ; $(SHELL) ./genfixes diff --git a/build/unix/build-toolchain/gcc-include.patch b/build/unix/build-toolchain/gcc-include.patch new file mode 100644 index 000000000..35c5147a9 --- /dev/null +++ b/build/unix/build-toolchain/gcc-include.patch @@ -0,0 +1,24 @@ +diff -ru a/configure b/configure +--- a/configure 2010-10-06 06:29:55.000000000 -0400 ++++ b/configure 2012-02-27 20:46:26.303460301 -0500 +@@ -8047,7 +8047,7 @@ + # being built; programs in there won't even run. + if test "${build}" = "${host}" && test -d ${srcdir}/gcc; then + # Search for pre-installed headers if nothing else fits. +- FLAGS_FOR_TARGET=$FLAGS_FOR_TARGET' -B$(build_tooldir)/bin/ -B$(build_tooldir)/lib/ -isystem $(build_tooldir)/include -isystem $(build_tooldir)/sys-include' ++ FLAGS_FOR_TARGET=$FLAGS_FOR_TARGET' -B$(exec_prefix)/bin/ -B$(exec_prefix)/lib/ -isystem $(exec_prefix)/include -isystem $(exec_prefix)/sys-include' + fi + + if test "x${use_gnu_ld}" = x && +diff -ru a/configure.ac b/configure.ac +--- a/configure.ac 2010-10-06 06:29:55.000000000 -0400 ++++ b/configure.ac 2012-02-27 20:46:22.587442745 -0500 +@@ -3100,7 +3100,7 @@ + # being built; programs in there won't even run. + if test "${build}" = "${host}" && test -d ${srcdir}/gcc; then + # Search for pre-installed headers if nothing else fits. +- FLAGS_FOR_TARGET=$FLAGS_FOR_TARGET' -B$(build_tooldir)/bin/ -B$(build_tooldir)/lib/ -isystem $(build_tooldir)/include -isystem $(build_tooldir)/sys-include' ++ FLAGS_FOR_TARGET=$FLAGS_FOR_TARGET' -B$(exec_prefix)/bin/ -B$(exec_prefix)/lib/ -isystem $(exec_prefix)/include -isystem $(exec_prefix)/sys-include' + fi + + if test "x${use_gnu_ld}" = x && diff --git a/build/unix/build-toolchain/glibc-deterministic.patch b/build/unix/build-toolchain/glibc-deterministic.patch new file mode 100644 index 000000000..bc5a11f09 --- /dev/null +++ b/build/unix/build-toolchain/glibc-deterministic.patch @@ -0,0 +1,115 @@ +diff -ru a/configure.in b/configure.in +--- a/configure.in 2011-01-17 23:34:07.000000000 -0500 ++++ b/configure.in 2012-01-25 20:40:27.919485606 -0500 +@@ -841,14 +841,6 @@ + LIBC_PROG_BINUTILS + AC_SUBST(MIG)dnl Needed by sysdeps/mach/configure.in + +-# Accept binutils 2.13 or newer. +-AC_CHECK_PROG_VER(AS, $AS, --version, +- [GNU assembler.* \([0-9]*\.[0-9.]*\)], +- [2.1[3-9]*], AS=: critic_missing="$critic_missing as") +-AC_CHECK_PROG_VER(LD, $LD, --version, +- [GNU ld.* \([0-9][0-9]*\.[0-9.]*\)], +- [2.1[3-9]*], LD=: critic_missing="$critic_missing ld") +- + # We need the physical current working directory. We cannot use the + # "pwd -P" shell builtin since that's not portable. Instead we try to + # find a pwd binary. Note that assigning to the PWD environment +@@ -2175,6 +2167,7 @@ + fi + AC_SUBST(old_glibc_headers) + ++libc_cv_slibdir=${prefix}/lib64 + AC_SUBST(libc_cv_slibdir) + AC_SUBST(libc_cv_localedir) + AC_SUBST(libc_cv_sysconfdir) +diff -ru a/csu/Makefile b/csu/Makefile +--- a/csu/Makefile 2011-01-17 23:34:07.000000000 -0500 ++++ b/csu/Makefile 2012-01-23 13:58:28.957792633 -0500 +@@ -223,8 +223,7 @@ + if [ -z "$$os" ]; then \ + os=Linux; \ + fi; \ +- printf '"Compiled on a %s %s system on %s.\\n"\n' \ +- "$$os" "$$version" "`date +%Y-%m-%d`";; \ ++ ;; \ + *) ;; \ + esac; \ + files="$(all-Banner-files)"; \ +diff -ru a/elf/Makefile b/elf/Makefile +--- a/elf/Makefile 2008-10-31 16:35:11.000000000 -0400 ++++ b/elf/Makefile 2012-02-16 12:20:00.038593752 -0500 +@@ -295,18 +295,11 @@ + z-now-yes = -Wl,-z,now + + $(objpfx)ld.so: $(objpfx)librtld.os $(ld-map) +- @rm -f $@.lds +- $(LINK.o) -nostdlib -nostartfiles -shared $(z-now-$(bind-now)) \ +- $(LDFLAGS-rtld) -Wl,-z,defs -Wl,--verbose 2>&1 | \ +- LC_ALL=C \ +- sed -e '/^=========/,/^=========/!d;/^=========/d' \ +- -e 's/\. = 0 + SIZEOF_HEADERS;/& _begin = . - SIZEOF_HEADERS;/' \ +- > $@.lds + $(LINK.o) -nostdlib -nostartfiles -shared -o $@ \ + $(LDFLAGS-rtld) -Wl,-z,defs $(z-now-$(bind-now)) \ + $(filter-out $(map-file),$^) $(load-map-file) \ +- -Wl,-soname=$(rtld-installed-name) -T $@.lds +- rm -f $@.lds ++ -Wl,-soname=$(rtld-installed-name) \ ++ -Wl,-defsym=_begin=0 + + # interp.c exists just to get this string into the libraries. + CFLAGS-interp.c = -D'RUNTIME_LINKER="$(slibdir)/$(rtld-installed-name)"' \ +diff -ru a/localedata/Makefile b/localedata/Makefile +--- a/localedata/Makefile 2006-04-26 01:14:03.000000000 -0400 ++++ b/localedata/Makefile 2012-02-17 10:31:24.592345047 -0500 +@@ -113,7 +113,7 @@ + $(make-target-directory) + rm -f $(@:.gz=) $@ + $(INSTALL_DATA) $< $(@:.gz=) +- gzip -9 $(@:.gz=) ++ gzip -9n $(@:.gz=) + + # Install the locale source files in the appropriate directory. + $(inst_i18ndir)/locales/%: locales/% $(+force); $(do-install) +diff -ru a/Makeconfig b/Makeconfig +--- a/Makeconfig 2006-07-10 17:42:27.000000000 -0400 ++++ b/Makeconfig 2012-02-17 08:28:31.859584817 -0500 +@@ -674,7 +674,7 @@ + $(foreach lib,$(libof-$(basename $(@F))) \ + $(libof-$(<F)) $(libof-$(@F)),$(CPPFLAGS-$(lib))) \ + $(CPPFLAGS-$(<F)) $(CPPFLAGS-$(@F)) $(CPPFLAGS-$(basename $(@F))) +-override CFLAGS = -std=gnu99 \ ++override CFLAGS = -std=gnu99 -fgnu89-inline \ + $(filter-out %frame-pointer,$(+cflags)) $(+gccwarn-c) \ + $(sysdep-CFLAGS) $(CFLAGS-$(suffix $@)) $(CFLAGS-$(<F)) \ + $(CFLAGS-$(@F)) +diff -ru a/Makerules b/Makerules +--- a/Makerules 2011-01-17 23:34:07.000000000 -0500 ++++ b/Makerules 2012-01-30 08:47:56.565068903 -0500 +@@ -977,9 +977,9 @@ + echo ' Use the shared library, but some functions are only in';\ + echo ' the static library, so try that secondarily. */';\ + cat $<; \ +- echo 'GROUP ( $(slibdir)/libc.so$(libc.so-version)' \ +- '$(libdir)/$(patsubst %,$(libtype.oS),$(libprefix)$(libc-name))'\ +- ' AS_NEEDED (' $(slibdir)/$(rtld-installed-name) ') )' \ ++ echo 'GROUP ( libc.so$(libc.so-version)' \ ++ '$(patsubst %,$(libtype.oS),$(libprefix)$(libc-name))'\ ++ ' AS_NEEDED (' $(rtld-installed-name) ') )' \ + ) > $@.new + mv -f $@.new $@ + +diff -ru a/nscd/nscd_stat.c b/nscd/nscd_stat.c +--- a/nscd/nscd_stat.c 2011-01-17 23:34:07.000000000 -0500 ++++ b/nscd/nscd_stat.c 2012-01-23 15:54:45.231607606 -0500 +@@ -38,7 +38,7 @@ + + + /* We use this to make sure the receiver is the same. */ +-static const char compilation[21] = __DATE__ " " __TIME__; ++static const char compilation[21] = "don't need this"; + + /* Statistic data for one database. */ + struct dbstat diff --git a/build/unix/build-toolchain/libtool-74c8993c178a1386ea5e2363a01d919738402f30.patch b/build/unix/build-toolchain/libtool-74c8993c178a1386ea5e2363a01d919738402f30.patch new file mode 100644 index 000000000..15187f6a7 --- /dev/null +++ b/build/unix/build-toolchain/libtool-74c8993c178a1386ea5e2363a01d919738402f30.patch @@ -0,0 +1,12 @@ +diff -ruN a/ltmain.sh b/ltmain.sh +--- a/ltmain.sh 2009-12-05 12:18:53.000000000 -0500 ++++ b/ltmain.sh 2012-05-07 16:19:31.871827967 -0400 +@@ -2932,7 +2932,7 @@ + func_extract_an_archive "$my_xdir" "$my_xabs" + ;; + esac +- my_oldobjs="$my_oldobjs "`find $my_xdir -name \*.$objext -print -o -name \*.lo -print | $NL2SP` ++ my_oldobjs="$my_oldobjs "`find $my_xdir -name \*.$objext -print -o -name \*.lo -print | sort | $NL2SP` + done + + func_extract_archives_result="$my_oldobjs" diff --git a/build/unix/build-toolchain/plugin_finish_decl.diff b/build/unix/build-toolchain/plugin_finish_decl.diff new file mode 100644 index 000000000..711ba4712 --- /dev/null +++ b/build/unix/build-toolchain/plugin_finish_decl.diff @@ -0,0 +1,179 @@ +Index: gcc/doc/plugins.texi +=================================================================== +--- gcc/doc/plugins.texi (revision 162126) ++++ gcc/doc/plugins.texi (working copy) +@@ -144,6 +144,7 @@ + @{ + PLUGIN_PASS_MANAGER_SETUP, /* To hook into pass manager. */ + PLUGIN_FINISH_TYPE, /* After finishing parsing a type. */ ++ PLUGIN_FINISH_DECL, /* After finishing parsing a declaration. */ + PLUGIN_FINISH_UNIT, /* Useful for summary processing. */ + PLUGIN_PRE_GENERICIZE, /* Allows to see low level AST in C and C++ frontends. */ + PLUGIN_FINISH, /* Called before GCC exits. */ +Index: gcc/plugin.def +=================================================================== +--- gcc/plugin.def (revision 162126) ++++ gcc/plugin.def (working copy) +@@ -24,6 +24,9 @@ + /* After finishing parsing a type. */ + DEFEVENT (PLUGIN_FINISH_TYPE) + ++/* After finishing parsing a declaration. */ ++DEFEVENT (PLUGIN_FINISH_DECL) ++ + /* Useful for summary processing. */ + DEFEVENT (PLUGIN_FINISH_UNIT) + +Index: gcc/testsuite/g++.dg/plugin/plugin.exp +=================================================================== +--- gcc/testsuite/g++.dg/plugin/plugin.exp (revision 162126) ++++ gcc/testsuite/g++.dg/plugin/plugin.exp (working copy) +@@ -51,7 +51,8 @@ + { pragma_plugin.c pragma_plugin-test-1.C } \ + { selfassign.c self-assign-test-1.C self-assign-test-2.C self-assign-test-3.C } \ + { dumb_plugin.c dumb-plugin-test-1.C } \ +- { header_plugin.c header-plugin-test.C } ] ++ { header_plugin.c header-plugin-test.C } \ ++ { decl_plugin.c decl-plugin-test.C } ] + + foreach plugin_test $plugin_test_list { + # Replace each source file with its full-path name +Index: gcc/testsuite/g++.dg/plugin/decl-plugin-test.C +=================================================================== +--- gcc/testsuite/g++.dg/plugin/decl-plugin-test.C (revision 0) ++++ gcc/testsuite/g++.dg/plugin/decl-plugin-test.C (revision 0) +@@ -0,0 +1,32 @@ ++ ++ ++extern int global; // { dg-warning "Decl Global global" } ++int global_array[] = { 1, 2, 3 }; // { dg-warning "Decl Global global_array" } ++ ++int takes_args(int arg1, int arg2) ++{ ++ int local = arg1 + arg2 + global; // { dg-warning "Decl Local local" } ++ return local + 1; ++} ++ ++int global = 12; // { dg-warning "Decl Global global" } ++ ++struct test_str { ++ int field; // { dg-warning "Decl Field field" } ++}; ++ ++class test_class { ++ int class_field1; // { dg-warning "Decl Field class_field1" } ++ int class_field2; // { dg-warning "Decl Field class_field2" } ++ ++ test_class() // { dg-warning "Decl Function test_class" } ++ : class_field1(0), class_field2(0) ++ {} ++ ++ void swap_fields(int bias) // { dg-warning "Decl Function swap_fields" } ++ { ++ int temp = class_field1 + bias; // { dg-warning "Decl Local temp" } ++ class_field1 = class_field2 - bias; ++ class_field2 = temp; ++ } ++}; +Index: gcc/testsuite/g++.dg/plugin/decl_plugin.c +=================================================================== +--- gcc/testsuite/g++.dg/plugin/decl_plugin.c (revision 0) ++++ gcc/testsuite/g++.dg/plugin/decl_plugin.c (revision 0) +@@ -0,0 +1,51 @@ ++/* A plugin example that shows which declarations are caught by FINISH_DECL */ ++ ++#include "gcc-plugin.h" ++#include <stdlib.h> ++#include "config.h" ++#include "system.h" ++#include "coretypes.h" ++#include "tree.h" ++#include "tree-pass.h" ++#include "intl.h" ++ ++int plugin_is_GPL_compatible; ++ ++/* Callback function to invoke after GCC finishes a declaration. */ ++ ++void plugin_finish_decl (void *event_data, void *data) ++{ ++ tree decl = (tree) event_data; ++ ++ const char *kind = NULL; ++ switch (TREE_CODE(decl)) { ++ case FUNCTION_DECL: ++ kind = "Function"; break; ++ case PARM_DECL: ++ kind = "Parameter"; break; ++ case VAR_DECL: ++ if (DECL_CONTEXT(decl) != NULL) ++ kind = "Local"; ++ else ++ kind = "Global"; ++ break; ++ case FIELD_DECL: ++ kind = "Field"; break; ++ default: ++ kind = "Unknown"; ++ } ++ ++ warning (0, G_("Decl %s %s"), ++ kind, IDENTIFIER_POINTER (DECL_NAME (decl))); ++} ++ ++int ++plugin_init (struct plugin_name_args *plugin_info, ++ struct plugin_gcc_version *version) ++{ ++ const char *plugin_name = plugin_info->base_name; ++ ++ register_callback (plugin_name, PLUGIN_FINISH_DECL, ++ plugin_finish_decl, NULL); ++ return 0; ++} +Index: gcc/cp/decl.c +=================================================================== +--- gcc/cp/decl.c (revision 162126) ++++ gcc/cp/decl.c (working copy) +@@ -5967,6 +5967,8 @@ + /* If this was marked 'used', be sure it will be output. */ + if (lookup_attribute ("used", DECL_ATTRIBUTES (decl))) + mark_decl_referenced (decl); ++ ++ invoke_plugin_callbacks (PLUGIN_FINISH_DECL, decl); + } + + /* Returns a declaration for a VAR_DECL as if: +Index: gcc/c-decl.c +=================================================================== +--- gcc/c-decl.c (revision 162126) ++++ gcc/c-decl.c (working copy) +@@ -4392,6 +4392,8 @@ + && DECL_INITIAL (decl) == NULL_TREE) + warning_at (DECL_SOURCE_LOCATION (decl), OPT_Wc___compat, + "uninitialized const %qD is invalid in C++", decl); ++ ++ invoke_plugin_callbacks (PLUGIN_FINISH_DECL, decl); + } + + /* Given a parsed parameter declaration, decode it into a PARM_DECL. */ +Index: gcc/plugin.c +=================================================================== +--- gcc/plugin.c (revision 162126) ++++ gcc/plugin.c (working copy) +@@ -400,6 +400,7 @@ + } + /* Fall through. */ + case PLUGIN_FINISH_TYPE: ++ case PLUGIN_FINISH_DECL: + case PLUGIN_START_UNIT: + case PLUGIN_FINISH_UNIT: + case PLUGIN_PRE_GENERICIZE: +@@ -481,6 +482,7 @@ + gcc_assert (event < event_last); + /* Fall through. */ + case PLUGIN_FINISH_TYPE: ++ case PLUGIN_FINISH_DECL: + case PLUGIN_START_UNIT: + case PLUGIN_FINISH_UNIT: + case PLUGIN_PRE_GENERICIZE: diff --git a/build/unix/build-toolchain/pr49911.diff b/build/unix/build-toolchain/pr49911.diff new file mode 100644 index 000000000..3583f00aa --- /dev/null +++ b/build/unix/build-toolchain/pr49911.diff @@ -0,0 +1,274 @@ +diff -ru gcc-4.5.2/gcc/double-int.c gcc-4.5.2-new/gcc/double-int.c +--- gcc-4.5.2/gcc/double-int.c 2009-11-25 05:55:54.000000000 -0500 ++++ gcc-4.5.2-new/gcc/double-int.c 2011-11-29 10:20:27.625583810 -0500 +@@ -296,7 +296,12 @@ + tree + double_int_to_tree (tree type, double_int cst) + { +- cst = double_int_ext (cst, TYPE_PRECISION (type), TYPE_UNSIGNED (type)); ++ /* Size types *are* sign extended. */ ++ bool sign_extended_type = (!TYPE_UNSIGNED (type) ++ || (TREE_CODE (type) == INTEGER_TYPE ++ && TYPE_IS_SIZETYPE (type))); ++ ++ cst = double_int_ext (cst, TYPE_PRECISION (type), !sign_extended_type); + + return build_int_cst_wide (type, cst.low, cst.high); + } +diff -ru gcc-4.5.2/gcc/tree.c gcc-4.5.2-new/gcc/tree.c +--- gcc-4.5.2/gcc/tree.c 2010-07-07 11:24:27.000000000 -0400 ++++ gcc-4.5.2-new/gcc/tree.c 2011-11-29 10:20:27.626583813 -0500 +@@ -9750,7 +9750,7 @@ + tree + upper_bound_in_type (tree outer, tree inner) + { +- unsigned HOST_WIDE_INT lo, hi; ++ double_int high; + unsigned int det = 0; + unsigned oprec = TYPE_PRECISION (outer); + unsigned iprec = TYPE_PRECISION (inner); +@@ -9797,18 +9797,18 @@ + /* Compute 2^^prec - 1. */ + if (prec <= HOST_BITS_PER_WIDE_INT) + { +- hi = 0; +- lo = ((~(unsigned HOST_WIDE_INT) 0) ++ high.high = 0; ++ high.low = ((~(unsigned HOST_WIDE_INT) 0) + >> (HOST_BITS_PER_WIDE_INT - prec)); + } + else + { +- hi = ((~(unsigned HOST_WIDE_INT) 0) ++ high.high = ((~(unsigned HOST_WIDE_INT) 0) + >> (2 * HOST_BITS_PER_WIDE_INT - prec)); +- lo = ~(unsigned HOST_WIDE_INT) 0; ++ high.low = ~(unsigned HOST_WIDE_INT) 0; + } + +- return build_int_cst_wide (outer, lo, hi); ++ return double_int_to_tree (outer, high); + } + + /* Returns the smallest value obtainable by casting something in INNER type to +diff -ru gcc-4.5.2/gcc/tree-vrp.c gcc-4.5.2-new/gcc/tree-vrp.c +--- gcc-4.5.2/gcc/tree-vrp.c 2010-06-14 11:23:31.000000000 -0400 ++++ gcc-4.5.2-new/gcc/tree-vrp.c 2011-11-29 10:20:27.628583820 -0500 +@@ -127,10 +127,10 @@ + static inline tree + vrp_val_max (const_tree type) + { +- if (!INTEGRAL_TYPE_P (type)) +- return NULL_TREE; ++ if (INTEGRAL_TYPE_P (type)) ++ return upper_bound_in_type (CONST_CAST_TREE (type), CONST_CAST_TREE (type)); + +- return TYPE_MAX_VALUE (type); ++ return NULL_TREE; + } + + /* Return the minimum value for TYPE. */ +@@ -138,10 +138,10 @@ + static inline tree + vrp_val_min (const_tree type) + { +- if (!INTEGRAL_TYPE_P (type)) +- return NULL_TREE; ++ if (INTEGRAL_TYPE_P (type)) ++ return lower_bound_in_type (CONST_CAST_TREE (type), CONST_CAST_TREE (type)); + +- return TYPE_MIN_VALUE (type); ++ return NULL_TREE; + } + + /* Return whether VAL is equal to the maximum value of its type. This +@@ -539,7 +539,7 @@ + set_value_range (vr, VR_RANGE, zero, + (overflow_infinity + ? positive_overflow_infinity (type) +- : TYPE_MAX_VALUE (type)), ++ : vrp_val_max (type)), + vr->equiv); + } + +@@ -1595,7 +1595,7 @@ + } + else if (cond_code == LE_EXPR || cond_code == LT_EXPR) + { +- min = TYPE_MIN_VALUE (type); ++ min = vrp_val_min (type); + + if (limit_vr == NULL || limit_vr->type == VR_ANTI_RANGE) + max = limit; +@@ -1630,7 +1630,7 @@ + } + else if (cond_code == GE_EXPR || cond_code == GT_EXPR) + { +- max = TYPE_MAX_VALUE (type); ++ max = vrp_val_max (type); + + if (limit_vr == NULL || limit_vr->type == VR_ANTI_RANGE) + min = limit; +@@ -2047,11 +2047,11 @@ + || code == ROUND_DIV_EXPR) + return (needs_overflow_infinity (TREE_TYPE (res)) + ? positive_overflow_infinity (TREE_TYPE (res)) +- : TYPE_MAX_VALUE (TREE_TYPE (res))); ++ : vrp_val_max (TREE_TYPE (res))); + else + return (needs_overflow_infinity (TREE_TYPE (res)) + ? negative_overflow_infinity (TREE_TYPE (res)) +- : TYPE_MIN_VALUE (TREE_TYPE (res))); ++ : vrp_val_min (TREE_TYPE (res))); + } + + return res; +@@ -2750,8 +2750,8 @@ + && TYPE_PRECISION (inner_type) < TYPE_PRECISION (outer_type)) + { + vr0.type = VR_RANGE; +- vr0.min = TYPE_MIN_VALUE (inner_type); +- vr0.max = TYPE_MAX_VALUE (inner_type); ++ vr0.min = vrp_val_min (inner_type); ++ vr0.max = vrp_val_max (inner_type); + } + + /* If VR0 is a constant range or anti-range and the conversion is +@@ -2836,7 +2836,7 @@ + } + } + else +- min = TYPE_MIN_VALUE (type); ++ min = vrp_val_min (type); + + if (is_positive_overflow_infinity (vr0.min)) + max = negative_overflow_infinity (type); +@@ -2855,7 +2855,7 @@ + } + } + else +- max = TYPE_MIN_VALUE (type); ++ max = vrp_val_min (type); + } + else if (code == NEGATE_EXPR + && TYPE_UNSIGNED (type)) +@@ -2897,7 +2897,7 @@ + else if (!vrp_val_is_min (vr0.min)) + min = fold_unary_to_constant (code, type, vr0.min); + else if (!needs_overflow_infinity (type)) +- min = TYPE_MAX_VALUE (type); ++ min = vrp_val_max (type); + else if (supports_overflow_infinity (type)) + min = positive_overflow_infinity (type); + else +@@ -2911,7 +2911,7 @@ + else if (!vrp_val_is_min (vr0.max)) + max = fold_unary_to_constant (code, type, vr0.max); + else if (!needs_overflow_infinity (type)) +- max = TYPE_MAX_VALUE (type); ++ max = vrp_val_max (type); + else if (supports_overflow_infinity (type) + /* We shouldn't generate [+INF, +INF] as set_value_range + doesn't like this and ICEs. */ +@@ -2941,7 +2941,7 @@ + TYPE_MIN_VALUE, remember -TYPE_MIN_VALUE = TYPE_MIN_VALUE. */ + if (TYPE_OVERFLOW_WRAPS (type)) + { +- tree type_min_value = TYPE_MIN_VALUE (type); ++ tree type_min_value = vrp_val_min (type); + + min = (vr0.min != type_min_value + ? int_const_binop (PLUS_EXPR, type_min_value, +@@ -2953,7 +2953,7 @@ + if (overflow_infinity_range_p (&vr0)) + min = negative_overflow_infinity (type); + else +- min = TYPE_MIN_VALUE (type); ++ min = vrp_val_min (type); + } + } + else +@@ -2974,7 +2974,7 @@ + } + } + else +- max = TYPE_MAX_VALUE (type); ++ max = vrp_val_max (type); + } + } + +@@ -3258,11 +3258,11 @@ + if (POINTER_TYPE_P (type) || !TYPE_MIN_VALUE (type)) + tmin = lower_bound_in_type (type, type); + else +- tmin = TYPE_MIN_VALUE (type); ++ tmin = vrp_val_min (type); + if (POINTER_TYPE_P (type) || !TYPE_MAX_VALUE (type)) + tmax = upper_bound_in_type (type, type); + else +- tmax = TYPE_MAX_VALUE (type); ++ tmax = vrp_val_max (type); + + if (vr->type == VR_VARYING || vr->type == VR_UNDEFINED) + { +@@ -4146,8 +4146,8 @@ + if ((comp_code == GT_EXPR || comp_code == LT_EXPR) + && INTEGRAL_TYPE_P (TREE_TYPE (val))) + { +- tree min = TYPE_MIN_VALUE (TREE_TYPE (val)); +- tree max = TYPE_MAX_VALUE (TREE_TYPE (val)); ++ tree min = vrp_val_min (TREE_TYPE (val)); ++ tree max = vrp_val_max (TREE_TYPE (val)); + + if (comp_code == GT_EXPR + && (!max +@@ -6426,13 +6426,13 @@ + VARYING. Same if the previous max value was invalid for + the type and we'd end up with vr_result.min > vr_result.max. */ + if (vrp_val_is_max (vr_result.max) +- || compare_values (TYPE_MIN_VALUE (TREE_TYPE (vr_result.min)), ++ || compare_values (vrp_val_min (TREE_TYPE (vr_result.min)), + vr_result.max) > 0) + goto varying; + + if (!needs_overflow_infinity (TREE_TYPE (vr_result.min)) + || !vrp_var_may_overflow (lhs, phi)) +- vr_result.min = TYPE_MIN_VALUE (TREE_TYPE (vr_result.min)); ++ vr_result.min = vrp_val_min (TREE_TYPE (vr_result.min)); + else if (supports_overflow_infinity (TREE_TYPE (vr_result.min))) + vr_result.min = + negative_overflow_infinity (TREE_TYPE (vr_result.min)); +@@ -6448,13 +6448,13 @@ + VARYING. Same if the previous min value was invalid for + the type and we'd end up with vr_result.max < vr_result.min. */ + if (vrp_val_is_min (vr_result.min) +- || compare_values (TYPE_MAX_VALUE (TREE_TYPE (vr_result.max)), ++ || compare_values (vrp_val_max (TREE_TYPE (vr_result.max)), + vr_result.min) < 0) + goto varying; + + if (!needs_overflow_infinity (TREE_TYPE (vr_result.max)) + || !vrp_var_may_overflow (lhs, phi)) +- vr_result.max = TYPE_MAX_VALUE (TREE_TYPE (vr_result.max)); ++ vr_result.max = vrp_val_max (TREE_TYPE (vr_result.max)); + else if (supports_overflow_infinity (TREE_TYPE (vr_result.max))) + vr_result.max = + positive_overflow_infinity (TREE_TYPE (vr_result.max)); +@@ -6782,7 +6782,7 @@ + { + /* This should not be negative infinity; there is no overflow + here. */ +- min = TYPE_MIN_VALUE (TREE_TYPE (op0)); ++ min = vrp_val_min (TREE_TYPE (op0)); + + max = op1; + if (cond_code == LT_EXPR && !is_overflow_infinity (max)) +@@ -6797,7 +6797,7 @@ + { + /* This should not be positive infinity; there is no overflow + here. */ +- max = TYPE_MAX_VALUE (TREE_TYPE (op0)); ++ max = vrp_val_max (TREE_TYPE (op0)); + + min = op1; + if (cond_code == GT_EXPR && !is_overflow_infinity (min)) diff --git a/build/unix/build-toolchain/r159628-r163231-r171807.patch b/build/unix/build-toolchain/r159628-r163231-r171807.patch new file mode 100644 index 000000000..b8ea2f148 --- /dev/null +++ b/build/unix/build-toolchain/r159628-r163231-r171807.patch @@ -0,0 +1,98 @@ +diff -ru gcc-4.5.2/libstdc++-v3/include/bits/stl_pair.h gcc-4.5.2-new/libstdc++-v3/include/bits/stl_pair.h +--- gcc-4.5.2/libstdc++-v3/include/bits/stl_pair.h 2010-06-10 06:26:14.000000000 -0400 ++++ gcc-4.5.2-new/libstdc++-v3/include/bits/stl_pair.h 2011-11-29 10:25:51.203597393 -0500 +@@ -88,6 +88,8 @@ + : first(__a), second(__b) { } + + #ifdef __GXX_EXPERIMENTAL_CXX0X__ ++ pair(const pair&) = default; ++ + // DR 811. + template<class _U1, class = typename + std::enable_if<std::is_convertible<_U1, _T1>::value>::type> +@@ -131,6 +133,15 @@ + + template<class _U1, class _U2> + pair& ++ operator=(const pair<_U1, _U2>& __p) ++ { ++ first = __p.first; ++ second = __p.second; ++ return *this; ++ } ++ ++ template<class _U1, class _U2> ++ pair& + operator=(pair<_U1, _U2>&& __p) + { + first = std::move(__p.first); +diff -ru gcc-4.5.2/libstdc++-v3/include/bits/stl_queue.h gcc-4.5.2-new/libstdc++-v3/include/bits/stl_queue.h +--- gcc-4.5.2/libstdc++-v3/include/bits/stl_queue.h 2010-02-04 13:20:34.000000000 -0500 ++++ gcc-4.5.2-new/libstdc++-v3/include/bits/stl_queue.h 2011-11-29 10:26:22.511695475 -0500 +@@ -1,6 +1,6 @@ + // Queue implementation -*- C++ -*- + +-// Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 ++// Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 + // Free Software Foundation, Inc. + // + // This file is part of the GNU ISO C++ Library. This library is free +@@ -137,16 +137,6 @@ + explicit + queue(_Sequence&& __c = _Sequence()) + : c(std::move(__c)) { } +- +- queue(queue&& __q) +- : c(std::move(__q.c)) { } +- +- queue& +- operator=(queue&& __q) +- { +- c = std::move(__q.c); +- return *this; +- } + #endif + + /** +@@ -451,17 +441,6 @@ + c.insert(c.end(), __first, __last); + std::make_heap(c.begin(), c.end(), comp); + } +- +- priority_queue(priority_queue&& __pq) +- : c(std::move(__pq.c)), comp(std::move(__pq.comp)) { } +- +- priority_queue& +- operator=(priority_queue&& __pq) +- { +- c = std::move(__pq.c); +- comp = std::move(__pq.comp); +- return *this; +- } + #endif + + /** +diff -ru gcc-4.5.2/libstdc++-v3/libsupc++/exception_ptr.h gcc-4.5.2-new/libstdc++-v3/libsupc++/exception_ptr.h +--- gcc-4.5.2/libstdc++-v3/libsupc++/exception_ptr.h 2009-11-09 17:09:30.000000000 -0500 ++++ gcc-4.5.2-new/libstdc++-v3/libsupc++/exception_ptr.h 2011-11-29 10:26:10.878659023 -0500 +@@ -129,7 +129,7 @@ + operator==(const exception_ptr&, const exception_ptr&) throw() + __attribute__ ((__pure__)); + +- const type_info* ++ const class type_info* + __cxa_exception_type() const throw() __attribute__ ((__pure__)); + }; + +diff -ru gcc-4.5.2/libstdc++-v3/libsupc++/nested_exception.h gcc-4.5.2-new/libstdc++-v3/libsupc++/nested_exception.h +--- gcc-4.5.2/libstdc++-v3/libsupc++/nested_exception.h 2010-02-18 12:20:16.000000000 -0500 ++++ gcc-4.5.2-new/libstdc++-v3/libsupc++/nested_exception.h 2011-11-29 10:26:10.879659026 -0500 +@@ -119,7 +119,7 @@ + // with a type that has an accessible nested_exception base. + template<typename _Ex> + inline void +- __throw_with_nested(_Ex&& __ex, const nested_exception* = 0) ++ __throw_with_nested(_Ex&& __ex, const nested_exception*) + { throw __ex; } + + template<typename _Ex> diff --git a/build/unix/elfhack/Makefile.in b/build/unix/elfhack/Makefile.in new file mode 100644 index 000000000..20f0248ac --- /dev/null +++ b/build/unix/elfhack/Makefile.in @@ -0,0 +1,102 @@ +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +DEPTH = @DEPTH@ +topsrcdir = @top_srcdir@ +srcdir = @srcdir@ +VPATH = @srcdir@ + +include $(DEPTH)/config/autoconf.mk + +INTERNAL_TOOLS = 1 + +HOST_PROGRAM = elfhack +NO_DIST_INSTALL = 1 +NO_PROFILE_GUIDED_OPTIMIZE = 1 + +VPATH += $(topsrcdir)/build + +HOST_CPPSRCS = \ + elf.cpp \ + elfhack.cpp \ + $(NULL) + +OS_CXXFLAGS := $(filter-out -fno-exceptions,$(OS_CXXFLAGS)) -fexceptions + +ifneq (,$(filter %86,$(TARGET_CPU))) +CPU := x86 +else +ifneq (,$(filter arm%,$(TARGET_CPU))) +CPU := arm +else +CPU := $(TARGET_CPU) +endif +endif + +CSRCS := \ + inject/$(CPU).c \ + inject/$(CPU)-noinit.c \ + test-ctors.c \ + test-array.c \ + $(NULL) + +ifndef CROSS_COMPILE +CSRCS += dummy.c +endif + +WRAP_LDFLAGS= + +include $(topsrcdir)/config/rules.mk + +test-array$(DLL_SUFFIX) test-ctors$(DLL_SUFFIX): %$(DLL_SUFFIX): %.$(OBJ_SUFFIX) elfhack $(filter inject/%,$(CSRCS:.c=.$(OBJ_SUFFIX))) + $(MKSHLIB) $(LDFLAGS) $< -nostartfiles + @echo === + @echo === If you get failures below, please file a bug describing the error + @echo === and your environment \(compiler and linker versions\), and use + @echo === --disable-elf-hack until this is fixed. + @echo === + # Fail if the library doesn't have $(DT_TYPE) .dynamic info + $(TOOLCHAIN_PREFIX)readelf -d $@ | grep '($(DT_TYPE))' + @rm -f $@.bak + $(CURDIR)/elfhack -b -f $@ + # Fail if the backup file doesn't exist + [ -f "$@.bak" ] + # Fail if the new library doesn't contain less relocations + [ $$($(TOOLCHAIN_PREFIX)objdump -R $@.bak | wc -l) -gt $$(objdump -R $@ | wc -l) ] + +test-array$(DLL_SUFFIX): DT_TYPE=INIT_ARRAY +test-ctors$(DLL_SUFFIX): DT_TYPE=INIT + +.PRECIOUS: test-array$(DLL_SUFFIX) test-ctors$(DLL_SUFFIX) + +GARBAGE += test-array$(DLL_SUFFIX) test-ctors$(DLL_SUFFIX) test-array$(DLL_SUFFIX).bak test-ctors$(DLL_SUFFIX).bak + +libs:: test-array$(DLL_SUFFIX) test-ctors$(DLL_SUFFIX) + +ifndef CROSS_COMPILE +dummy: dummy.$(OBJ_SUFFIX) + $(CC) -o $@ $^ $(LDFLAGS) + +libs:: dummy + # Will either crash or return exit code 1 if elfhack is broken + LD_PRELOAD=$(CURDIR)/test-array$(DLL_SUFFIX) $(CURDIR)/dummy + LD_PRELOAD=$(CURDIR)/test-ctors$(DLL_SUFFIX) $(CURDIR)/dummy + +GARBAGE += dummy +endif + +inject: + $(NSINSTALL) -D $@ + +inject/%.c: inject.c $(call mkdir_deps,inject) + cp $< $@ + +GARBAGE_DIRS += inject + +inject/%.$(OBJ_SUFFIX): DEFINES += -DBITS=$(if $(HAVE_64BIT_OS),64,32) +inject/%.$(OBJ_SUFFIX): CFLAGS := -O2 -fno-stack-protector $(filter -m% -I%,$(CFLAGS)) +inject/$(CPU)-noinit.$(OBJ_SUFFIX): DEFINES += -DNOINIT +test.$(OBJ_SUFFIX): CFLAGS := -O0 +host_elf.$(OBJ_SUFFIX) host_elfhack.$(OBJ_SUFFIX): elfxx.h diff --git a/build/unix/elfhack/README b/build/unix/elfhack/README new file mode 100644 index 000000000..8c68031e3 --- /dev/null +++ b/build/unix/elfhack/README @@ -0,0 +1,28 @@ +Elfhack is a program to optimize ELF binaries for size and cold startup +speed. + +Presently, it is quite experimental, though it works well for the target +it was created for: Firefox's libxul.so. + +Elfhack currently only does one thing: packing dynamic relocations ; +which ends up being a quite complex task, that can be summarized this +way: +- Remove RELATIVE relocations from the .rel.dyn/.rela.dyn section. +- Inject a small code able to apply relative relocations "by hand" + after the .rel.dyn/.rela.dyn section. +- Inject a section containing relocative relocations in a different + and more packed format, after the small code. +- Register the small code as DT_INIT function. Make the small code call + what was initially the DT_INIT function, if there was one. +- Remove the hole between the new section containing relative + relocations and the following sections, adjusting offsets and base + addresses accordingly. +- Adjust PT_LOAD entries to fit new offsets, and add an additional + PT_LOAD entry when that is necessary to handle the discrepancy between + offsets and base addresses, meaning the section offsets may yet again + need adjustments. +- Adjust various DT_* dynamic tags to fit the new ELF layout. +- Adjust section headers. +- Adjust ELF headers. + +See http://glandium.org/blog/?p=1177#relocations for some figures. diff --git a/build/unix/elfhack/dummy.c b/build/unix/elfhack/dummy.c new file mode 100644 index 000000000..f30fcb5b1 --- /dev/null +++ b/build/unix/elfhack/dummy.c @@ -0,0 +1,9 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +extern __attribute__((visibility("default"), weak)) int print_status(); + +int main() { + return print_status(); +} diff --git a/build/unix/elfhack/elf.cpp b/build/unix/elfhack/elf.cpp new file mode 100644 index 000000000..571209b3c --- /dev/null +++ b/build/unix/elfhack/elf.cpp @@ -0,0 +1,918 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#undef NDEBUG +#include <cstring> +#include <assert.h> +#include "elfxx.h" + +template <class endian, typename R, typename T> +void Elf_Ehdr_Traits::swap(T &t, R &r) +{ + memcpy(r.e_ident, t.e_ident, sizeof(r.e_ident)); + r.e_type = endian::swap(t.e_type); + r.e_machine = endian::swap(t.e_machine); + r.e_version = endian::swap(t.e_version); + r.e_entry = endian::swap(t.e_entry); + r.e_phoff = endian::swap(t.e_phoff); + r.e_shoff = endian::swap(t.e_shoff); + r.e_flags = endian::swap(t.e_flags); + r.e_ehsize = endian::swap(t.e_ehsize); + r.e_phentsize = endian::swap(t.e_phentsize); + r.e_phnum = endian::swap(t.e_phnum); + r.e_shentsize = endian::swap(t.e_shentsize); + r.e_shnum = endian::swap(t.e_shnum); + r.e_shstrndx = endian::swap(t.e_shstrndx); +} + +template <class endian, typename R, typename T> +void Elf_Phdr_Traits::swap(T &t, R &r) +{ + r.p_type = endian::swap(t.p_type); + r.p_offset = endian::swap(t.p_offset); + r.p_vaddr = endian::swap(t.p_vaddr); + r.p_paddr = endian::swap(t.p_paddr); + r.p_filesz = endian::swap(t.p_filesz); + r.p_memsz = endian::swap(t.p_memsz); + r.p_flags = endian::swap(t.p_flags); + r.p_align = endian::swap(t.p_align); +} + +template <class endian, typename R, typename T> +void Elf_Shdr_Traits::swap(T &t, R &r) +{ + r.sh_name = endian::swap(t.sh_name); + r.sh_type = endian::swap(t.sh_type); + r.sh_flags = endian::swap(t.sh_flags); + r.sh_addr = endian::swap(t.sh_addr); + r.sh_offset = endian::swap(t.sh_offset); + r.sh_size = endian::swap(t.sh_size); + r.sh_link = endian::swap(t.sh_link); + r.sh_info = endian::swap(t.sh_info); + r.sh_addralign = endian::swap(t.sh_addralign); + r.sh_entsize = endian::swap(t.sh_entsize); +} + +template <class endian, typename R, typename T> +void Elf_Dyn_Traits::swap(T &t, R &r) +{ + r.d_tag = endian::swap(t.d_tag); + r.d_un.d_val = endian::swap(t.d_un.d_val); +} + +template <class endian, typename R, typename T> +void Elf_Sym_Traits::swap(T &t, R &r) +{ + r.st_name = endian::swap(t.st_name); + r.st_value = endian::swap(t.st_value); + r.st_size = endian::swap(t.st_size); + r.st_info = t.st_info; + r.st_other = t.st_other; + r.st_shndx = endian::swap(t.st_shndx); +} + +template <class endian> +struct _Rel_info { + static inline void swap(Elf32_Word &t, Elf32_Word &r) { r = endian::swap(t); } + static inline void swap(Elf64_Xword &t, Elf64_Xword &r) { r = endian::swap(t); } + static inline void swap(Elf64_Xword &t, Elf32_Word &r) { + r = endian::swap(ELF32_R_INFO(ELF64_R_SYM(t), ELF64_R_TYPE(t))); + } + static inline void swap(Elf32_Word &t, Elf64_Xword &r) { + r = endian::swap(ELF64_R_INFO(ELF32_R_SYM(t), ELF32_R_TYPE(t))); + } +}; + +template <class endian, typename R, typename T> +void Elf_Rel_Traits::swap(T &t, R &r) +{ + r.r_offset = endian::swap(t.r_offset); + _Rel_info<endian>::swap(t.r_info, r.r_info); +} + +template <class endian, typename R, typename T> +void Elf_Rela_Traits::swap(T &t, R &r) +{ + r.r_offset = endian::swap(t.r_offset); + _Rel_info<endian>::swap(t.r_info, r.r_info); + r.r_addend = endian::swap(t.r_addend); +} + +static const Elf32_Shdr null32_section = + { 0, SHT_NULL, 0, 0, 0, 0, SHN_UNDEF, 0, 0, 0 }; + +Elf_Shdr null_section(null32_section); + +Elf_Ehdr::Elf_Ehdr(std::ifstream &file, char ei_class, char ei_data) +: serializable<Elf_Ehdr_Traits>(file, ei_class, ei_data), + ElfSection(null_section, NULL, NULL) +{ + shdr.sh_size = Elf_Ehdr::size(ei_class); +} + +Elf::Elf(std::ifstream &file) +{ + if (!file.is_open()) + throw std::runtime_error("Error opening file"); + + file.exceptions(std::ifstream::eofbit | std::ifstream::failbit | std::ifstream::badbit); + // Read ELF magic number and identification information + char e_ident[EI_VERSION]; + file.seekg(0); + file.read(e_ident, sizeof(e_ident)); + file.seekg(0); + ehdr = new Elf_Ehdr(file, e_ident[EI_CLASS], e_ident[EI_DATA]); + + // ELFOSABI_LINUX is kept unsupported because I haven't looked whether + // STB_GNU_UNIQUE or STT_GNU_IFUNC would need special casing. + if ((ehdr->e_ident[EI_OSABI] != ELFOSABI_NONE) && (ehdr->e_ident[EI_ABIVERSION] != 0)) + throw std::runtime_error("unsupported ELF ABI"); + + if (ehdr->e_version != 1) + throw std::runtime_error("unsupported ELF version"); + + // Sanity checks + if (ehdr->e_shnum == 0) + throw std::runtime_error("sstripped ELF files aren't supported"); + + if (ehdr->e_ehsize != Elf_Ehdr::size(e_ident[EI_CLASS])) + throw std::runtime_error("unsupported ELF inconsistency: ehdr.e_ehsize != sizeof(ehdr)"); + + if (ehdr->e_shentsize != Elf_Shdr::size(e_ident[EI_CLASS])) + throw std::runtime_error("unsupported ELF inconsistency: ehdr.e_shentsize != sizeof(shdr)"); + + if (ehdr->e_phnum == 0) { + if (ehdr->e_phoff != 0) + throw std::runtime_error("unsupported ELF inconsistency: e_phnum == 0 && e_phoff != 0"); + if (ehdr->e_phentsize != 0) + throw std::runtime_error("unsupported ELF inconsistency: e_phnum == 0 && e_phentsize != 0"); + } else if (ehdr->e_phoff != ehdr->e_ehsize) + throw std::runtime_error("unsupported ELF inconsistency: ehdr->e_phoff != ehdr->e_ehsize"); + else if (ehdr->e_phentsize != Elf_Phdr::size(e_ident[EI_CLASS])) + throw std::runtime_error("unsupported ELF inconsistency: ehdr->e_phentsize != sizeof(phdr)"); + + // Read section headers + Elf_Shdr **shdr = new Elf_Shdr *[ehdr->e_shnum]; + file.seekg(ehdr->e_shoff); + for (int i = 0; i < ehdr->e_shnum; i++) + shdr[i] = new Elf_Shdr(file, e_ident[EI_CLASS], e_ident[EI_DATA]); + + // Sanity check in section header for index 0 + if ((shdr[0]->sh_name != 0) || (shdr[0]->sh_type != SHT_NULL) || + (shdr[0]->sh_flags != 0) || (shdr[0]->sh_addr != 0) || + (shdr[0]->sh_offset != 0) || (shdr[0]->sh_size != 0) || + (shdr[0]->sh_link != SHN_UNDEF) || (shdr[0]->sh_info != 0) || + (shdr[0]->sh_addralign != 0) || (shdr[0]->sh_entsize != 0)) + throw std::runtime_error("Section header for index 0 contains unsupported values"); + + if ((shdr[ehdr->e_shstrndx]->sh_link != 0) || (shdr[ehdr->e_shstrndx]->sh_info != 0)) + throw std::runtime_error("unsupported ELF content: string table with sh_link != 0 || sh_info != 0"); + + // Store these temporarily + tmp_shdr = shdr; + tmp_file = &file; + + // Fill sections list + sections = new ElfSection *[ehdr->e_shnum]; + for (int i = 0; i < ehdr->e_shnum; i++) + sections[i] = NULL; + for (int i = 1; i < ehdr->e_shnum; i++) { + if (sections[i] != NULL) + continue; + getSection(i); + } + Elf_Shdr s; + s.sh_name = 0; + s.sh_type = SHT_NULL; + s.sh_flags = 0; + s.sh_addr = 0; + s.sh_offset = ehdr->e_shoff; + s.sh_entsize = Elf_Shdr::size(e_ident[EI_CLASS]); + s.sh_size = s.sh_entsize * ehdr->e_shnum; + s.sh_link = 0; + s.sh_info = 0; + s.sh_addralign = (e_ident[EI_CLASS] == ELFCLASS32) ? 4 : 8; + shdr_section = new ElfSection(s, NULL, NULL); + + // Fake section for program headers + s.sh_offset = ehdr->e_phoff; + s.sh_addr = ehdr->e_phoff; + s.sh_entsize = Elf_Phdr::size(e_ident[EI_CLASS]); + s.sh_size = s.sh_entsize * ehdr->e_phnum; + phdr_section = new ElfSection(s, NULL, NULL); + + phdr_section->insertAfter(ehdr, false); + + sections[1]->insertAfter(phdr_section, false); + for (int i = 2; i < ehdr->e_shnum; i++) { + // TODO: this should be done in a better way + if ((shdr_section->getPrevious() == NULL) && (shdr[i]->sh_offset > ehdr->e_shoff)) { + shdr_section->insertAfter(sections[i - 1], false); + sections[i]->insertAfter(shdr_section, false); + } else + sections[i]->insertAfter(sections[i - 1], false); + } + if (shdr_section->getPrevious() == NULL) + shdr_section->insertAfter(sections[ehdr->e_shnum - 1], false); + + tmp_file = NULL; + tmp_shdr = NULL; + for (int i = 0; i < ehdr->e_shnum; i++) + delete shdr[i]; + delete[] shdr; + + eh_shstrndx = (ElfStrtab_Section *)sections[ehdr->e_shstrndx]; + + // Skip reading program headers if there aren't any + if (ehdr->e_phnum == 0) + return; + + // Read program headers + file.seekg(ehdr->e_phoff); + for (int i = 0; i < ehdr->e_phnum; i++) { + Elf_Phdr phdr(file, e_ident[EI_CLASS], e_ident[EI_DATA]); + if (phdr.p_type == PT_LOAD) { + // Default alignment for PT_LOAD on x86-64 prevents elfhack from + // doing anything useful. However, the system doesn't actually + // require such a big alignment, so in order for elfhack to work + // efficiently, reduce alignment when it's originally the default + // one. + if ((ehdr->e_machine == EM_X86_64) && (phdr.p_align == 0x200000)) + phdr.p_align = 0x1000; + } + ElfSegment *segment = new ElfSegment(&phdr); + // Some segments aren't entirely filled (if at all) by sections + // For those, we use fake sections + if ((phdr.p_type == PT_LOAD) && (phdr.p_offset == 0)) { + // Use a fake section for ehdr and phdr + ehdr->getShdr().sh_addr = phdr.p_vaddr; + phdr_section->getShdr().sh_addr += phdr.p_vaddr; + segment->addSection(ehdr); + segment->addSection(phdr_section); + } + if (phdr.p_type == PT_PHDR) + segment->addSection(phdr_section); + for (int j = 1; j < ehdr->e_shnum; j++) + if (phdr.contains(sections[j])) + segment->addSection(sections[j]); + // Make sure that our view of segments corresponds to the original + // ELF file. + assert(segment->getFileSize() == phdr.p_filesz); + // gold makes TLS segments end on an aligned virtual address, even + // when the underlying section ends before that, while bfd ld + // doesn't. It's fine if we don't keep that alignment. + unsigned int memsize = segment->getMemSize(); + if (phdr.p_type == PT_TLS && memsize != phdr.p_memsz) { + unsigned int align = segment->getAlign(); + memsize = (memsize + align - 1) & ~(align - 1); + } + assert(memsize == phdr.p_memsz); + segments.push_back(segment); + } + + new (&eh_entry) ElfLocation(ehdr->e_entry, this); +} + +Elf::~Elf() +{ + for (std::vector<ElfSegment *>::iterator seg = segments.begin(); seg != segments.end(); seg++) + delete *seg; + delete[] sections; + ElfSection *section = ehdr; + while (section != NULL) { + ElfSection *next = section->getNext(); + delete section; + section = next; + } +} + +// TODO: This shouldn't fail after inserting sections +ElfSection *Elf::getSection(int index) +{ + if ((index < -1) || (index >= ehdr->e_shnum)) + throw std::runtime_error("Section index out of bounds"); + if (index == -1) + index = ehdr->e_shstrndx; // TODO: should be fixed to use the actual current number + // Special case: the section at index 0 is void + if (index == 0) + return NULL; + // Infinite recursion guard + if (sections[index] == (ElfSection *)this) + return NULL; + if (sections[index] == NULL) { + sections[index] = (ElfSection *)this; + switch (tmp_shdr[index]->sh_type) { + case SHT_DYNAMIC: + sections[index] = new ElfDynamic_Section(*tmp_shdr[index], tmp_file, this); + break; + case SHT_REL: + sections[index] = new ElfRel_Section<Elf_Rel>(*tmp_shdr[index], tmp_file, this); + break; + case SHT_RELA: + sections[index] = new ElfRel_Section<Elf_Rela>(*tmp_shdr[index], tmp_file, this); + break; + case SHT_DYNSYM: + case SHT_SYMTAB: + sections[index] = new ElfSymtab_Section(*tmp_shdr[index], tmp_file, this); + break; + case SHT_STRTAB: + sections[index] = new ElfStrtab_Section(*tmp_shdr[index], tmp_file, this); + break; + default: + sections[index] = new ElfSection(*tmp_shdr[index], tmp_file, this); + } + } + return sections[index]; +} + +ElfSection *Elf::getSectionAt(unsigned int offset) +{ + for (int i = 1; i < ehdr->e_shnum; i++) { + ElfSection *section = getSection(i); + if ((section != NULL) && (section->getFlags() & SHF_ALLOC) && !(section->getFlags() & SHF_TLS) && + (offset >= section->getAddr()) && (offset < section->getAddr() + section->getSize())) + return section; + } + return NULL; +} + +ElfSegment *Elf::getSegmentByType(unsigned int type, ElfSegment *last) +{ + std::vector<ElfSegment *>::iterator seg; + if (last) { + seg = std::find(segments.begin(), segments.end(), last); + ++seg; + } else + seg = segments.begin(); + for (; seg != segments.end(); seg++) + if ((*seg)->getType() == type) + return *seg; + return NULL; +} + +void Elf::removeSegment(ElfSegment *segment) +{ + if (!segment) + return; + std::vector<ElfSegment *>::iterator seg; + seg = std::find(segments.begin(), segments.end(), segment); + if (seg == segments.end()) + return; + segment->clear(); + segments.erase(seg); +} + +ElfDynamic_Section *Elf::getDynSection() +{ + for (std::vector<ElfSegment *>::iterator seg = segments.begin(); seg != segments.end(); seg++) + if (((*seg)->getType() == PT_DYNAMIC) && ((*seg)->getFirstSection() != NULL) && + (*seg)->getFirstSection()->getType() == SHT_DYNAMIC) + return (ElfDynamic_Section *)(*seg)->getFirstSection(); + + return NULL; +} + +void Elf::normalize() +{ + // fixup section headers sh_name; TODO: that should be done by sections + // themselves + for (ElfSection *section = ehdr; section != NULL; section = section->getNext()) { + if (section->getIndex() == 0) + continue; + else + ehdr->e_shnum = section->getIndex() + 1; + section->getShdr().sh_name = eh_shstrndx->getStrIndex(section->getName()); + } + ehdr->markDirty(); + // Check segments consistency + int i = 0; + for (std::vector<ElfSegment *>::iterator seg = segments.begin(); seg != segments.end(); seg++, i++) { + std::list<ElfSection *>::iterator it = (*seg)->begin(); + for (ElfSection *last = *(it++); it != (*seg)->end(); last = *(it++)) { + if (((*it)->getType() != SHT_NOBITS) && + ((*it)->getAddr() - last->getAddr()) != ((*it)->getOffset() - last->getOffset())) { + throw std::runtime_error("Segments inconsistency"); + } + } + } + // fixup ehdr before writing + if (ehdr->e_phnum != segments.size()) { + ehdr->e_phnum = segments.size(); + phdr_section->getShdr().sh_size = segments.size() * Elf_Phdr::size(ehdr->e_ident[EI_CLASS]); + phdr_section->getNext()->markDirty(); + } + // fixup shdr before writing + if (ehdr->e_shnum != shdr_section->getSize() / shdr_section->getEntSize()) + shdr_section->getShdr().sh_size = ehdr->e_shnum * Elf_Shdr::size(ehdr->e_ident[EI_CLASS]); + ehdr->e_shoff = shdr_section->getOffset(); + ehdr->e_entry = eh_entry.getValue(); + ehdr->e_shstrndx = eh_shstrndx->getIndex(); +} + +void Elf::write(std::ofstream &file) +{ + normalize(); + for (ElfSection *section = ehdr; + section != NULL; section = section->getNext()) { + file.seekp(section->getOffset()); + if (section == phdr_section) { + for (std::vector<ElfSegment *>::iterator seg = segments.begin(); seg != segments.end(); seg++) { + Elf_Phdr phdr; + phdr.p_type = (*seg)->getType(); + phdr.p_flags = (*seg)->getFlags(); + phdr.p_offset = (*seg)->getOffset(); + phdr.p_vaddr = (*seg)->getAddr(); + phdr.p_paddr = phdr.p_vaddr + (*seg)->getVPDiff(); + phdr.p_filesz = (*seg)->getFileSize(); + phdr.p_memsz = (*seg)->getMemSize(); + phdr.p_align = (*seg)->getAlign(); + phdr.serialize(file, ehdr->e_ident[EI_CLASS], ehdr->e_ident[EI_DATA]); + } + } else if (section == shdr_section) { + null_section.serialize(file, ehdr->e_ident[EI_CLASS], ehdr->e_ident[EI_DATA]); + for (ElfSection *sec = ehdr; sec!= NULL; sec = sec->getNext()) { + if (sec->getType() != SHT_NULL) + sec->getShdr().serialize(file, ehdr->e_ident[EI_CLASS], ehdr->e_ident[EI_DATA]); + } + } else + section->serialize(file, ehdr->e_ident[EI_CLASS], ehdr->e_ident[EI_DATA]); + } +} + +ElfSection::ElfSection(Elf_Shdr &s, std::ifstream *file, Elf *parent) +: shdr(s), + link(shdr.sh_link == SHN_UNDEF ? NULL : parent->getSection(shdr.sh_link)), + next(NULL), previous(NULL), index(-1) +{ + if ((file == NULL) || (shdr.sh_type == SHT_NULL) || (shdr.sh_type == SHT_NOBITS)) + data = NULL; + else { + data = new char[shdr.sh_size]; + int pos = file->tellg(); + file->seekg(shdr.sh_offset); + file->read(data, shdr.sh_size); + file->seekg(pos); + } + if (shdr.sh_name == 0) + name = NULL; + else { + ElfStrtab_Section *strtab = (ElfStrtab_Section *) parent->getSection(-1); + // Special case (see elfgeneric.cpp): if strtab is NULL, the + // section being created is the strtab. + if (strtab == NULL) + name = &data[shdr.sh_name]; + else + name = strtab->getStr(shdr.sh_name); + } + // Only SHT_REL/SHT_RELA sections use sh_info to store a section + // number. + if ((shdr.sh_type == SHT_REL) || (shdr.sh_type == SHT_RELA)) + info.section = shdr.sh_info ? parent->getSection(shdr.sh_info) : NULL; + else + info.index = shdr.sh_info; +} + +unsigned int ElfSection::getAddr() +{ + if (shdr.sh_addr != (Elf32_Word)-1) + return shdr.sh_addr; + + // It should be safe to adjust sh_addr for all allocated sections that + // are neither SHT_NOBITS nor SHT_PROGBITS + if ((previous != NULL) && isRelocatable()) { + unsigned int addr = previous->getAddr(); + if (previous->getType() != SHT_NOBITS) + addr += previous->getSize(); + + if (addr & (getAddrAlign() - 1)) + addr = (addr | (getAddrAlign() - 1)) + 1; + + return (shdr.sh_addr = addr); + } + return shdr.sh_addr; +} + +unsigned int ElfSection::getOffset() +{ + if (shdr.sh_offset != (Elf32_Word)-1) + return shdr.sh_offset; + + if (previous == NULL) + return (shdr.sh_offset = 0); + + unsigned int offset = previous->getOffset(); + + ElfSegment *ptload = getSegmentByType(PT_LOAD); + ElfSegment *prev_ptload = previous->getSegmentByType(PT_LOAD); + + if (ptload && (ptload == prev_ptload)) { + offset += getAddr() - previous->getAddr(); + return (shdr.sh_offset = offset); + } + + if (previous->getType() != SHT_NOBITS) + offset += previous->getSize(); + + Elf32_Word align = 0x1000; + for (std::vector<ElfSegment *>::iterator seg = segments.begin(); seg != segments.end(); seg++) + align = std::max(align, (*seg)->getAlign()); + + Elf32_Word mask = align - 1; + // SHF_TLS is used for .tbss which is some kind of special case. + if (((getType() != SHT_NOBITS) || (getFlags() & SHF_TLS)) && (getFlags() & SHF_ALLOC)) { + if ((getAddr() & mask) < (offset & mask)) + offset = (offset | mask) + (getAddr() & mask) + 1; + else + offset = (offset & ~mask) + (getAddr() & mask); + } + if ((getType() != SHT_NOBITS) && (offset & (getAddrAlign() - 1))) + offset = (offset | (getAddrAlign() - 1)) + 1; + + // Two subsequent sections can't be mapped in the same page in memory + // if they aren't in the same 4K block on disk. + if ((getType() != SHT_NOBITS) && getAddr()) { + if (((offset >> 12) != (previous->getOffset() >> 12)) && + ((getAddr() >> 12) == (previous->getAddr() >> 12))) + throw std::runtime_error("Moving section would require overlapping segments"); + } + + return (shdr.sh_offset = offset); +} + +int ElfSection::getIndex() +{ + if (index != -1) + return index; + if (getType() == SHT_NULL) + return (index = 0); + ElfSection *reference; + for (reference = previous; (reference != NULL) && (reference->getType() == SHT_NULL); reference = reference->getPrevious()); + if (reference == NULL) + return (index = 1); + return (index = reference->getIndex() + 1); +} + +Elf_Shdr &ElfSection::getShdr() +{ + getOffset(); + if (shdr.sh_link == (Elf32_Word)-1) + shdr.sh_link = getLink() ? getLink()->getIndex() : 0; + if (shdr.sh_info == (Elf32_Word)-1) + shdr.sh_info = ((getType() == SHT_REL) || (getType() == SHT_RELA)) ? + (getInfo().section ? getInfo().section->getIndex() : 0) : + getInfo().index; + + return shdr; +} + +ElfSegment::ElfSegment(Elf_Phdr *phdr) +: type(phdr->p_type), v_p_diff(phdr->p_paddr - phdr->p_vaddr), + flags(phdr->p_flags), align(phdr->p_align), vaddr(phdr->p_vaddr), + filesz(phdr->p_filesz), memsz(phdr->p_memsz) {} + +void ElfSegment::addSection(ElfSection *section) +{ + // Make sure all sections in PT_GNU_RELRO won't be moved by elfhack + assert(!((type == PT_GNU_RELRO) && (section->isRelocatable()))); + + //TODO: Check overlapping sections + std::list<ElfSection *>::iterator i; + for (i = sections.begin(); i != sections.end(); ++i) + if ((*i)->getAddr() > section->getAddr()) + break; + sections.insert(i, section); + section->addToSegment(this); +} + +void ElfSegment::removeSection(ElfSection *section) +{ + sections.remove(section); + section->removeFromSegment(this); +} + +unsigned int ElfSegment::getFileSize() +{ + if (type == PT_GNU_RELRO || isElfHackFillerSegment()) + return filesz; + + if (sections.empty()) + return 0; + // Search the last section that is not SHT_NOBITS + std::list<ElfSection *>::reverse_iterator i; + for (i = sections.rbegin(); (i != sections.rend()) && ((*i)->getType() == SHT_NOBITS); ++i); + // All sections are SHT_NOBITS + if (i == sections.rend()) + return 0; + + unsigned int end = (*i)->getAddr() + (*i)->getSize(); + + return end - sections.front()->getAddr(); +} + +unsigned int ElfSegment::getMemSize() +{ + if (type == PT_GNU_RELRO || isElfHackFillerSegment()) + return memsz; + + if (sections.empty()) + return 0; + + unsigned int end = sections.back()->getAddr() + sections.back()->getSize(); + + return end - sections.front()->getAddr(); +} + +unsigned int ElfSegment::getOffset() +{ + if ((type == PT_GNU_RELRO) && !sections.empty() && + (sections.front()->getAddr() != vaddr)) + throw std::runtime_error("PT_GNU_RELRO segment doesn't start on a section start"); + + // Neither bionic nor glibc linkers seem to like when the offset of that segment is 0 + if (isElfHackFillerSegment()) + return vaddr; + + return sections.empty() ? 0 : sections.front()->getOffset(); +} + +unsigned int ElfSegment::getAddr() +{ + if ((type == PT_GNU_RELRO) && !sections.empty() && + (sections.front()->getAddr() != vaddr)) + throw std::runtime_error("PT_GNU_RELRO segment doesn't start on a section start"); + + if (isElfHackFillerSegment()) + return vaddr; + + return sections.empty() ? 0 : sections.front()->getAddr(); +} + +void ElfSegment::clear() +{ + for (std::list<ElfSection *>::iterator i = sections.begin(); i != sections.end(); ++i) + (*i)->removeFromSegment(this); + sections.clear(); +} + +ElfValue *ElfDynamic_Section::getValueForType(unsigned int tag) +{ + for (unsigned int i = 0; i < shdr.sh_size / shdr.sh_entsize; i++) + if (dyns[i].tag == tag) + return dyns[i].value; + + return NULL; +} + +ElfSection *ElfDynamic_Section::getSectionForType(unsigned int tag) +{ + ElfValue *value = getValueForType(tag); + return value ? value->getSection() : NULL; +} + +bool ElfDynamic_Section::setValueForType(unsigned int tag, ElfValue *val) +{ + unsigned int i; + unsigned int shnum = shdr.sh_size / shdr.sh_entsize; + for (i = 0; (i < shnum) && (dyns[i].tag != DT_NULL); i++) + if (dyns[i].tag == tag) { + delete dyns[i].value; + dyns[i].value = val; + return true; + } + // If we get here, this means we didn't match for the given tag + // Most of the time, there are a few DT_NULL entries, that we can + // use to add our value, but if we are on the last entry, we can't. + if (i >= shnum - 1) + return false; + + dyns[i].tag = tag; + dyns[i].value = val; + return true; +} + +ElfDynamic_Section::ElfDynamic_Section(Elf_Shdr &s, std::ifstream *file, Elf *parent) +: ElfSection(s, file, parent) +{ + int pos = file->tellg(); + dyns.resize(s.sh_size / s.sh_entsize); + file->seekg(shdr.sh_offset); + // Here we assume tags refer to only one section (e.g. DT_RELSZ accounts + // for .rel.dyn size) + for (unsigned int i = 0; i < s.sh_size / s.sh_entsize; i++) { + Elf_Dyn dyn(*file, parent->getClass(), parent->getData()); + dyns[i].tag = dyn.d_tag; + switch (dyn.d_tag) { + case DT_NULL: + case DT_SYMBOLIC: + case DT_TEXTREL: + case DT_BIND_NOW: + dyns[i].value = new ElfValue(); + break; + case DT_NEEDED: + case DT_SONAME: + case DT_RPATH: + case DT_PLTREL: + case DT_RUNPATH: + case DT_FLAGS: + case DT_RELACOUNT: + case DT_RELCOUNT: + case DT_VERDEFNUM: + case DT_VERNEEDNUM: + dyns[i].value = new ElfPlainValue(dyn.d_un.d_val); + break; + case DT_PLTGOT: + case DT_HASH: + case DT_STRTAB: + case DT_SYMTAB: + case DT_RELA: + case DT_INIT: + case DT_FINI: + case DT_REL: + case DT_JMPREL: + case DT_INIT_ARRAY: + case DT_FINI_ARRAY: + case DT_GNU_HASH: + case DT_VERSYM: + case DT_VERNEED: + case DT_VERDEF: + dyns[i].value = new ElfLocation(dyn.d_un.d_ptr, parent); + break; + default: + dyns[i].value = NULL; + } + } + // Another loop to get the section sizes + for (unsigned int i = 0; i < s.sh_size / s.sh_entsize; i++) + switch (dyns[i].tag) { + case DT_PLTRELSZ: + dyns[i].value = new ElfSize(getSectionForType(DT_JMPREL)); + break; + case DT_RELASZ: + dyns[i].value = new ElfSize(getSectionForType(DT_RELA)); + break; + case DT_STRSZ: + dyns[i].value = new ElfSize(getSectionForType(DT_STRTAB)); + break; + case DT_RELSZ: + dyns[i].value = new ElfSize(getSectionForType(DT_REL)); + break; + case DT_INIT_ARRAYSZ: + dyns[i].value = new ElfSize(getSectionForType(DT_INIT_ARRAY)); + break; + case DT_FINI_ARRAYSZ: + dyns[i].value = new ElfSize(getSectionForType(DT_FINI_ARRAY)); + break; + case DT_RELAENT: + dyns[i].value = new ElfEntSize(getSectionForType(DT_RELA)); + break; + case DT_SYMENT: + dyns[i].value = new ElfEntSize(getSectionForType(DT_SYMTAB)); + break; + case DT_RELENT: + dyns[i].value = new ElfEntSize(getSectionForType(DT_REL)); + break; + } + + file->seekg(pos); +} + +ElfDynamic_Section::~ElfDynamic_Section() +{ + for (unsigned int i = 0; i < shdr.sh_size / shdr.sh_entsize; i++) + delete dyns[i].value; +} + +void ElfDynamic_Section::serialize(std::ofstream &file, char ei_class, char ei_data) +{ + for (unsigned int i = 0; i < shdr.sh_size / shdr.sh_entsize; i++) { + Elf_Dyn dyn; + dyn.d_tag = dyns[i].tag; + dyn.d_un.d_val = (dyns[i].value != NULL) ? dyns[i].value->getValue() : 0; + dyn.serialize(file, ei_class, ei_data); + } +} + +ElfSymtab_Section::ElfSymtab_Section(Elf_Shdr &s, std::ifstream *file, Elf *parent) +: ElfSection(s, file, parent) +{ + int pos = file->tellg(); + syms.resize(s.sh_size / s.sh_entsize); + ElfStrtab_Section *strtab = (ElfStrtab_Section *)getLink(); + file->seekg(shdr.sh_offset); + for (unsigned int i = 0; i < shdr.sh_size / shdr.sh_entsize; i++) { + Elf_Sym sym(*file, parent->getClass(), parent->getData()); + syms[i].name = strtab->getStr(sym.st_name); + syms[i].info = sym.st_info; + syms[i].other = sym.st_other; + ElfSection *section = (sym.st_shndx == SHN_ABS) ? NULL : parent->getSection(sym.st_shndx); + new (&syms[i].value) ElfLocation(section, sym.st_value, ElfLocation::ABSOLUTE); + syms[i].size = sym.st_size; + syms[i].defined = (sym.st_shndx != SHN_UNDEF); + } + file->seekg(pos); +} + +void +ElfSymtab_Section::serialize(std::ofstream &file, char ei_class, char ei_data) +{ + ElfStrtab_Section *strtab = (ElfStrtab_Section *)getLink(); + for (unsigned int i = 0; i < shdr.sh_size / shdr.sh_entsize; i++) { + Elf_Sym sym; + sym.st_name = strtab->getStrIndex(syms[i].name); + sym.st_info = syms[i].info; + sym.st_other = syms[i].other; + sym.st_value = syms[i].value.getValue(); + ElfSection *section = syms[i].value.getSection(); + if (syms[i].defined) + sym.st_shndx = section ? section->getIndex() : SHN_ABS; + else + sym.st_shndx = SHN_UNDEF; + sym.st_size = syms[i].size; + sym.serialize(file, ei_class, ei_data); + } +} + +Elf_SymValue * +ElfSymtab_Section::lookup(const char *name, unsigned int type_filter) +{ + for (std::vector<Elf_SymValue>::iterator sym = syms.begin(); + sym != syms.end(); sym++) { + if ((type_filter & (1 << ELF32_ST_TYPE(sym->info))) && + (strcmp(sym->name, name) == 0)) { + return &*sym; + } + } + return NULL; +} + +const char * +ElfStrtab_Section::getStr(unsigned int index) +{ + for (std::vector<table_storage>::iterator t = table.begin(); + t != table.end(); t++) { + if (index < t->used) + return t->buf + index; + index -= t->used; + } + assert(1 == 0); + return NULL; +} + +const char * +ElfStrtab_Section::getStr(const char *string) +{ + if (string == NULL) + return NULL; + + // If the given string is within the section, return it + for (std::vector<table_storage>::iterator t = table.begin(); + t != table.end(); t++) + if ((string >= t->buf) && (string < t->buf + t->used)) + return string; + + // TODO: should scan in the section to find an existing string + + // If not, we need to allocate the string in the section + size_t len = strlen(string) + 1; + + if (table.back().size - table.back().used < len) + table.resize(table.size() + 1); + + char *alloc_str = table.back().buf + table.back().used; + memcpy(alloc_str, string, len); + table.back().used += len; + + shdr.sh_size += len; + markDirty(); + + return alloc_str; +} + +unsigned int +ElfStrtab_Section::getStrIndex(const char *string) +{ + if (string == NULL) + return 0; + + unsigned int index = 0; + string = getStr(string); + for (std::vector<table_storage>::iterator t = table.begin(); + t != table.end(); t++) { + if ((string >= t->buf) && (string < t->buf + t->used)) + return index + (string - t->buf); + index += t->used; + } + + assert(1 == 0); + return 0; +} + +void +ElfStrtab_Section::serialize(std::ofstream &file, char ei_class, char ei_data) +{ + file.seekp(getOffset()); + for (std::vector<table_storage>::iterator t = table.begin(); + t != table.end(); t++) + file.write(t->buf, t->used); +} diff --git a/build/unix/elfhack/elfhack.cpp b/build/unix/elfhack/elfhack.cpp new file mode 100644 index 000000000..ed5fffce9 --- /dev/null +++ b/build/unix/elfhack/elfhack.cpp @@ -0,0 +1,794 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#undef NDEBUG +#include <assert.h> +#include <cstring> +#include <cstdlib> +#include <cstdio> +#include "elfxx.h" + +#define ver "0" +#define elfhack_data ".elfhack.data.v" ver +#define elfhack_text ".elfhack.text.v" ver + +#ifndef R_ARM_V4BX +#define R_ARM_V4BX 0x28 +#endif +#ifndef R_ARM_CALL +#define R_ARM_CALL 0x1c +#endif +#ifndef R_ARM_JUMP24 +#define R_ARM_JUMP24 0x1d +#endif +#ifndef R_ARM_THM_JUMP24 +#define R_ARM_THM_JUMP24 0x1e +#endif + +char *rundir = NULL; + +template <typename T> +struct wrapped { + T value; +}; + +class Elf_Addr_Traits { +public: + typedef wrapped<Elf32_Addr> Type32; + typedef wrapped<Elf64_Addr> Type64; + + template <class endian, typename R, typename T> + static inline void swap(T &t, R &r) { + r.value = endian::swap(t.value); + } +}; + +typedef serializable<Elf_Addr_Traits> Elf_Addr; + +class Elf_RelHack_Traits { +public: + typedef Elf32_Rel Type32; + typedef Elf32_Rel Type64; + + template <class endian, typename R, typename T> + static inline void swap(T &t, R &r) { + r.r_offset = endian::swap(t.r_offset); + r.r_info = endian::swap(t.r_info); + } +}; + +typedef serializable<Elf_RelHack_Traits> Elf_RelHack; + +class ElfRelHack_Section: public ElfSection { +public: + ElfRelHack_Section(Elf_Shdr &s) + : ElfSection(s, NULL, NULL) + { + name = elfhack_data; + }; + + void serialize(std::ofstream &file, char ei_class, char ei_data) + { + for (std::vector<Elf_RelHack>::iterator i = rels.begin(); + i != rels.end(); ++i) + (*i).serialize(file, ei_class, ei_data); + } + + bool isRelocatable() { + return true; + } + + void push_back(Elf_RelHack &r) { + rels.push_back(r); + shdr.sh_size = rels.size() * shdr.sh_entsize; + } +private: + std::vector<Elf_RelHack> rels; +}; + +class ElfRelHackCode_Section: public ElfSection { +public: + ElfRelHackCode_Section(Elf_Shdr &s, Elf &e, unsigned int init) + : ElfSection(s, NULL, NULL), parent(e), init(init) { + std::string file(rundir); + file += "/inject/"; + switch (parent.getMachine()) { + case EM_386: + file += "x86"; + break; + case EM_X86_64: + file += "x86_64"; + break; + case EM_ARM: + file += "arm"; + break; + default: + throw std::runtime_error("unsupported architecture"); + } + if (!init) + file += "-noinit"; + file += ".o"; + std::ifstream inject(file.c_str(), std::ios::in|std::ios::binary); + elf = new Elf(inject); + if (elf->getType() != ET_REL) + throw std::runtime_error("object for injected code is not ET_REL"); + if (elf->getMachine() != parent.getMachine()) + throw std::runtime_error("architecture of object for injected code doesn't match"); + + ElfSymtab_Section *symtab = NULL; + + // Get all executable sections from the injected code object. + // Most of the time, there will only be one for the init function, + // but on e.g. x86, there is a separate section for + // __i686.get_pc_thunk.$reg + // Find the symbol table at the same time. + for (ElfSection *section = elf->getSection(1); section != NULL; + section = section->getNext()) { + if ((section->getType() == SHT_PROGBITS) && + (section->getFlags() & SHF_EXECINSTR)) { + code.push_back(section); + // We need to align this section depending on the greater + // alignment required by code sections. + if (shdr.sh_addralign < section->getAddrAlign()) + shdr.sh_addralign = section->getAddrAlign(); + } else if (section->getType() == SHT_SYMTAB) { + symtab = (ElfSymtab_Section *) section; + } + } + assert(code.size() != 0); + if (symtab == NULL) + throw std::runtime_error("Couldn't find a symbol table for the injected code"); + + // Find the init symbol + entry_point = -1; + int shndx = 0; + Elf_SymValue *sym = symtab->lookup("init"); + if (sym) { + entry_point = sym->value.getValue(); + shndx = sym->value.getSection()->getIndex(); + } else + throw std::runtime_error("Couldn't find an 'init' symbol in the injected code"); + + // Adjust code sections offsets according to their size + std::vector<ElfSection *>::iterator c = code.begin(); + (*c)->getShdr().sh_addr = 0; + for(ElfSection *last = *(c++); c != code.end(); c++) { + unsigned int addr = last->getShdr().sh_addr + last->getSize(); + if (addr & ((*c)->getAddrAlign() - 1)) + addr = (addr | ((*c)->getAddrAlign() - 1)) + 1; + (*c)->getShdr().sh_addr = addr; + } + shdr.sh_size = code.back()->getAddr() + code.back()->getSize(); + data = new char[shdr.sh_size]; + char *buf = data; + for (c = code.begin(); c != code.end(); c++) { + memcpy(buf, (*c)->getData(), (*c)->getSize()); + buf += (*c)->getSize(); + if ((*c)->getIndex() < shndx) + entry_point += (*c)->getSize(); + } + name = elfhack_text; + } + + ~ElfRelHackCode_Section() { + delete elf; + } + + void serialize(std::ofstream &file, char ei_class, char ei_data) + { + // Readjust code offsets + for (std::vector<ElfSection *>::iterator c = code.begin(); c != code.end(); c++) + (*c)->getShdr().sh_addr += getAddr(); + + // Apply relocations + for (ElfSection *rel = elf->getSection(1); rel != NULL; rel = rel->getNext()) + if ((rel->getType() == SHT_REL) || (rel->getType() == SHT_RELA)) { + ElfSection *section = rel->getInfo().section; + if ((section->getType() == SHT_PROGBITS) && (section->getFlags() & SHF_EXECINSTR)) { + if (rel->getType() == SHT_REL) + apply_relocations((ElfRel_Section<Elf_Rel> *)rel, section); + else + apply_relocations((ElfRel_Section<Elf_Rela> *)rel, section); + } + } + + ElfSection::serialize(file, ei_class, ei_data); + } + + bool isRelocatable() { + return true; + } + + unsigned int getEntryPoint() { + return entry_point; + } +private: + class pc32_relocation { + public: + Elf32_Addr operator()(unsigned int base_addr, Elf32_Off offset, + Elf32_Word addend, unsigned int addr) + { + return addr + addend - offset - base_addr; + } + }; + + class arm_plt32_relocation { + public: + Elf32_Addr operator()(unsigned int base_addr, Elf32_Off offset, + Elf32_Word addend, unsigned int addr) + { + // We don't care about sign_extend because the only case where this is + // going to be used only jumps forward. + Elf32_Addr tmp = (Elf32_Addr) (addr - offset - base_addr) >> 2; + tmp = (addend + tmp) & 0x00ffffff; + return (addend & 0xff000000) | tmp; + } + }; + + class arm_thm_jump24_relocation { + public: + Elf32_Addr operator()(unsigned int base_addr, Elf32_Off offset, + Elf32_Word addend, unsigned int addr) + { + /* Follows description of b.w and bl instructions as per + ARM Architecture Reference Manual ARM® v7-A and ARM® v7-R edition, A8.6.16 + We limit ourselves to Encoding T4 of b.w and Encoding T1 of bl. + We don't care about sign_extend because the only case where this is + going to be used only jumps forward. */ + Elf32_Addr tmp = (Elf32_Addr) (addr - offset - base_addr); + unsigned int word0 = addend & 0xffff, + word1 = addend >> 16; + + /* Encoding T4 of B.W is 10x1 ; Encoding T1 of BL is 11x1. */ + unsigned int type = (word1 & 0xd000) >> 12; + if (((word0 & 0xf800) != 0xf000) || ((type & 0x9) != 0x9)) + throw std::runtime_error("R_ARM_THM_JUMP24/R_ARM_THM_CALL relocation only supported for B.W <label> and BL <label>"); + + /* When the target address points to ARM code, switch a BL to a + * BLX. This however can't be done with a B.W without adding a + * trampoline, which is not supported as of now. */ + if ((addr & 0x1) == 0) { + if (type == 0x9) + throw std::runtime_error("R_ARM_THM_JUMP24/R_ARM_THM_CALL relocation only supported for BL <label> when label points to ARM code"); + /* The address of the target is always relative to a 4-bytes + * aligned address, so if the address of the BL instruction is + * not 4-bytes aligned, adjust for it. */ + if ((base_addr + offset) & 0x2) + tmp += 2; + /* Encoding T2 of BLX is 11x0. */ + type = 0xc; + } + + unsigned int s = (word0 & (1 << 10)) >> 10; + unsigned int j1 = (word1 & (1 << 13)) >> 13; + unsigned int j2 = (word1 & (1 << 11)) >> 11; + unsigned int i1 = j1 ^ s ? 0 : 1; + unsigned int i2 = j2 ^ s ? 0 : 1; + + tmp += ((s << 24) | (i1 << 23) | (i2 << 22) | ((word0 & 0x3ff) << 12) | ((word1 & 0x7ff) << 1)); + + s = (tmp & (1 << 24)) >> 24; + j1 = ((tmp & (1 << 23)) >> 23) ^ !s; + j2 = ((tmp & (1 << 22)) >> 22) ^ !s; + + return 0xf000 | (s << 10) | ((tmp & (0x3ff << 12)) >> 12) | + (type << 28) | (j1 << 29) | (j2 << 27) | ((tmp & 0xffe) << 15); + } + }; + + class gotoff_relocation { + public: + Elf32_Addr operator()(unsigned int base_addr, Elf32_Off offset, + Elf32_Word addend, unsigned int addr) + { + return addr + addend; + } + }; + + template <class relocation_type> + void apply_relocation(ElfSection *the_code, char *base, Elf_Rel *r, unsigned int addr) + { + relocation_type relocation; + Elf32_Addr value; + memcpy(&value, base + r->r_offset, 4); + value = relocation(the_code->getAddr(), r->r_offset, value, addr); + memcpy(base + r->r_offset, &value, 4); + } + + template <class relocation_type> + void apply_relocation(ElfSection *the_code, char *base, Elf_Rela *r, unsigned int addr) + { + relocation_type relocation; + Elf32_Addr value = relocation(the_code->getAddr(), r->r_offset, r->r_addend, addr); + memcpy(base + r->r_offset, &value, 4); + } + + template <typename Rel_Type> + void apply_relocations(ElfRel_Section<Rel_Type> *rel, ElfSection *the_code) + { + assert(rel->getType() == Rel_Type::sh_type); + char *buf = data + (the_code->getAddr() - code.front()->getAddr()); + // TODO: various checks on the sections + ElfSymtab_Section *symtab = (ElfSymtab_Section *)rel->getLink(); + for (typename std::vector<Rel_Type>::iterator r = rel->rels.begin(); r != rel->rels.end(); r++) { + // TODO: various checks on the symbol + const char *name = symtab->syms[ELF32_R_SYM(r->r_info)].name; + unsigned int addr; + if (symtab->syms[ELF32_R_SYM(r->r_info)].value.getSection() == NULL) { + if (strcmp(name, "relhack") == 0) { + addr = getNext()->getAddr(); + } else if (strcmp(name, "elf_header") == 0) { + // TODO: change this ungly hack to something better + ElfSection *ehdr = parent.getSection(1)->getPrevious()->getPrevious(); + addr = ehdr->getAddr(); + } else if (strcmp(name, "original_init") == 0) { + addr = init; + } else if (strcmp(name, "_GLOBAL_OFFSET_TABLE_") == 0) { + // We actually don't need a GOT, but need it as a reference for + // GOTOFF relocations. We'll just use the start of the ELF file + addr = 0; + } else if (strcmp(name, "") == 0) { + // This is for R_ARM_V4BX, until we find something better + addr = -1; + } else { + throw std::runtime_error("Unsupported symbol in relocation"); + } + } else { + ElfSection *section = symtab->syms[ELF32_R_SYM(r->r_info)].value.getSection(); + assert((section->getType() == SHT_PROGBITS) && (section->getFlags() & SHF_EXECINSTR)); + addr = symtab->syms[ELF32_R_SYM(r->r_info)].value.getValue(); + } + // Do the relocation +#define REL(machine, type) (EM_ ## machine | (R_ ## machine ## _ ## type << 8)) + switch (elf->getMachine() | (ELF32_R_TYPE(r->r_info) << 8)) { + case REL(X86_64, PC32): + case REL(386, PC32): + case REL(386, GOTPC): + case REL(ARM, GOTPC): + case REL(ARM, REL32): + apply_relocation<pc32_relocation>(the_code, buf, &*r, addr); + break; + case REL(ARM, CALL): + case REL(ARM, JUMP24): + case REL(ARM, PLT32): + apply_relocation<arm_plt32_relocation>(the_code, buf, &*r, addr); + break; + case REL(ARM, THM_PC22 /* THM_CALL */): + case REL(ARM, THM_JUMP24): + apply_relocation<arm_thm_jump24_relocation>(the_code, buf, &*r, addr); + break; + case REL(386, GOTOFF): + case REL(ARM, GOTOFF): + apply_relocation<gotoff_relocation>(the_code, buf, &*r, addr); + break; + case REL(ARM, V4BX): + // Ignore R_ARM_V4BX relocations + break; + default: + throw std::runtime_error("Unsupported relocation type"); + } + } + } + + Elf *elf, &parent; + std::vector<ElfSection *> code; + unsigned int init; + int entry_point; +}; + +unsigned int get_addend(Elf_Rel *rel, Elf *elf) { + ElfLocation loc(rel->r_offset, elf); + Elf_Addr addr(loc.getBuffer(), Elf_Addr::size(elf->getClass()), elf->getClass(), elf->getData()); + return addr.value; +} + +unsigned int get_addend(Elf_Rela *rel, Elf *elf) { + return rel->r_addend; +} + +void set_relative_reloc(Elf_Rel *rel, Elf *elf, unsigned int value) { + ElfLocation loc(rel->r_offset, elf); + Elf_Addr addr; + addr.value = value; + addr.serialize(const_cast<char *>(loc.getBuffer()), Elf_Addr::size(elf->getClass()), elf->getClass(), elf->getData()); +} + +void set_relative_reloc(Elf_Rela *rel, Elf *elf, unsigned int value) { + // ld puts the value of relocated relocations both in the addend and + // at r_offset. For consistency, keep it that way. + set_relative_reloc((Elf_Rel *)rel, elf, value); + rel->r_addend = value; +} + +void maybe_split_segment(Elf *elf, ElfSegment *segment, bool fill) +{ + std::list<ElfSection *>::iterator it = segment->begin(); + for (ElfSection *last = *(it++); it != segment->end(); last = *(it++)) { + // When two consecutive non-SHT_NOBITS sections are apart by more + // than the alignment of the section, the second can be moved closer + // to the first, but this requires the segment to be split. + if (((*it)->getType() != SHT_NOBITS) && (last->getType() != SHT_NOBITS) && + ((*it)->getOffset() - last->getOffset() - last->getSize() > segment->getAlign())) { + // Probably very wrong. + Elf_Phdr phdr; + phdr.p_type = PT_LOAD; + phdr.p_vaddr = 0; + phdr.p_paddr = phdr.p_vaddr + segment->getVPDiff(); + phdr.p_flags = segment->getFlags(); + phdr.p_align = segment->getAlign(); + phdr.p_filesz = (unsigned int)-1; + phdr.p_memsz = (unsigned int)-1; + ElfSegment *newSegment = new ElfSegment(&phdr); + elf->insertSegmentAfter(segment, newSegment); + ElfSection *section = *it; + for (; it != segment->end(); ++it) { + newSegment->addSection(*it); + } + for (it = newSegment->begin(); it != newSegment->end(); it++) { + segment->removeSection(*it); + } + // Fill the virtual address space gap left between the two PT_LOADs + // with a new PT_LOAD with no permissions. This avoids the linker + // (especially bionic's) filling the gap with anonymous memory, + // which breakpad doesn't like. + // /!\ running strip on a elfhacked binary will break this filler + // PT_LOAD. + if (!fill) + break; + // Insert dummy segment to normalize the entire Elf with the header + // sizes adjusted, before inserting a filler segment. + { + memset(&phdr, 0, sizeof(phdr)); + ElfSegment dummySegment(&phdr); + elf->insertSegmentAfter(segment, &dummySegment); + elf->normalize(); + elf->removeSegment(&dummySegment); + } + ElfSection *previous = section->getPrevious(); + phdr.p_type = PT_LOAD; + phdr.p_vaddr = (previous->getAddr() + previous->getSize() + segment->getAlign() - 1) & ~(segment->getAlign() - 1); + phdr.p_paddr = phdr.p_vaddr + segment->getVPDiff(); + phdr.p_flags = 0; + phdr.p_align = 0; + phdr.p_filesz = (section->getAddr() & ~(newSegment->getAlign() - 1)) - phdr.p_vaddr; + phdr.p_memsz = phdr.p_filesz; + if (phdr.p_filesz) { + newSegment = new ElfSegment(&phdr); + assert(newSegment->isElfHackFillerSegment()); + elf->insertSegmentAfter(segment, newSegment); + } else { + elf->normalize(); + } + break; + } + } +} + +template <typename Rel_Type> +int do_relocation_section(Elf *elf, unsigned int rel_type, unsigned int rel_type2, bool force, bool fill) +{ + ElfDynamic_Section *dyn = elf->getDynSection(); + if (dyn ==NULL) { + fprintf(stderr, "Couldn't find SHT_DYNAMIC section\n"); + return -1; + } + + ElfSegment *relro = elf->getSegmentByType(PT_GNU_RELRO); + + ElfRel_Section<Rel_Type> *section = (ElfRel_Section<Rel_Type> *)dyn->getSectionForType(Rel_Type::d_tag); + assert(section->getType() == Rel_Type::sh_type); + + Elf32_Shdr relhack32_section = + { 0, SHT_PROGBITS, SHF_ALLOC, 0, (Elf32_Off)-1, 0, SHN_UNDEF, 0, + Elf_RelHack::size(elf->getClass()), Elf_RelHack::size(elf->getClass()) }; // TODO: sh_addralign should be an alignment, not size + Elf32_Shdr relhackcode32_section = + { 0, SHT_PROGBITS, SHF_ALLOC | SHF_EXECINSTR, 0, (Elf32_Off)-1, 0, + SHN_UNDEF, 0, 1, 0 }; + + unsigned int entry_sz = Elf_Addr::size(elf->getClass()); + + // The injected code needs to be executed before any init code in the + // binary. There are three possible cases: + // - The binary has no init code at all. In this case, we will add a + // DT_INIT entry pointing to the injected code. + // - The binary has a DT_INIT entry. In this case, we will interpose: + // we change DT_INIT to point to the injected code, and have the + // injected code call the original DT_INIT entry point. + // - The binary has no DT_INIT entry, but has a DT_INIT_ARRAY. In this + // case, we interpose as well, by replacing the first entry in the + // array to point to the injected code, and have the injected code + // call the original first entry. + // The binary may have .ctors instead of DT_INIT_ARRAY, for its init + // functions, but this falls into the second case above, since .ctors + // are actually run by DT_INIT code. + ElfValue *value = dyn->getValueForType(DT_INIT); + unsigned int original_init = value ? value->getValue() : 0; + ElfSection *init_array = NULL; + if (!value || !value->getValue()) { + value = dyn->getValueForType(DT_INIT_ARRAYSZ); + if (value && value->getValue() >= entry_sz) + init_array = dyn->getSectionForType(DT_INIT_ARRAY); + } + + Elf_Shdr relhack_section(relhack32_section); + Elf_Shdr relhackcode_section(relhackcode32_section); + ElfRelHack_Section *relhack = new ElfRelHack_Section(relhack_section); + + ElfSymtab_Section *symtab = (ElfSymtab_Section *) section->getLink(); + Elf_SymValue *sym = symtab->lookup("__cxa_pure_virtual"); + + std::vector<Rel_Type> new_rels; + Elf_RelHack relhack_entry; + relhack_entry.r_offset = relhack_entry.r_info = 0; + size_t init_array_reloc = 0; + for (typename std::vector<Rel_Type>::iterator i = section->rels.begin(); + i != section->rels.end(); i++) { + // We don't need to keep R_*_NONE relocations + if (!ELF32_R_TYPE(i->r_info)) + continue; + ElfLocation loc(i->r_offset, elf); + // __cxa_pure_virtual is a function used in vtables to point at pure + // virtual methods. The __cxa_pure_virtual function usually abort()s. + // These functions are however normally never called. In the case + // where they would, jumping to the NULL address instead of calling + // __cxa_pure_virtual is going to work just as well. So we can remove + // relocations for the __cxa_pure_virtual symbol and NULL out the + // content at the offset pointed by the relocation. + if (sym) { + if (sym->defined) { + // If we are statically linked to libstdc++, the + // __cxa_pure_virtual symbol is defined in our lib, and we + // have relative relocations (rel_type) for it. + if (ELF32_R_TYPE(i->r_info) == rel_type) { + Elf_Addr addr(loc.getBuffer(), entry_sz, elf->getClass(), elf->getData()); + if (addr.value == sym->value.getValue()) { + memset((char *)loc.getBuffer(), 0, entry_sz); + continue; + } + } + } else { + // If we are dynamically linked to libstdc++, the + // __cxa_pure_virtual symbol is undefined in our lib, and we + // have absolute relocations (rel_type2) for it. + if ((ELF32_R_TYPE(i->r_info) == rel_type2) && + (sym == &symtab->syms[ELF32_R_SYM(i->r_info)])) { + memset((char *)loc.getBuffer(), 0, entry_sz); + continue; + } + } + } + // Keep track of the relocation associated with the first init_array entry. + if (init_array && i->r_offset == init_array->getAddr()) { + if (init_array_reloc) { + fprintf(stderr, "Found multiple relocations for the first init_array entry. Skipping\n"); + return -1; + } + new_rels.push_back(*i); + init_array_reloc = new_rels.size(); + } else if (!(loc.getSection()->getFlags() & SHF_WRITE) || (ELF32_R_TYPE(i->r_info) != rel_type) || + (relro && (i->r_offset >= relro->getAddr()) && + (i->r_offset < relro->getAddr() + relro->getMemSize()))) { + // Don't pack relocations happening in non writable sections. + // Our injected code is likely not to be allowed to write there. + new_rels.push_back(*i); + } else { + // TODO: check that i->r_addend == *i->r_offset + if (i->r_offset == relhack_entry.r_offset + relhack_entry.r_info * entry_sz) { + relhack_entry.r_info++; + } else { + if (relhack_entry.r_offset) + relhack->push_back(relhack_entry); + relhack_entry.r_offset = i->r_offset; + relhack_entry.r_info = 1; + } + } + } + if (relhack_entry.r_offset) + relhack->push_back(relhack_entry); + // Last entry must be NULL + relhack_entry.r_offset = relhack_entry.r_info = 0; + relhack->push_back(relhack_entry); + + unsigned int old_end = section->getOffset() + section->getSize(); + + if (init_array) { + if (! init_array_reloc) { + fprintf(stderr, "Didn't find relocation for DT_INIT_ARRAY's first entry. Skipping\n"); + return -1; + } + Rel_Type *rel = &new_rels[init_array_reloc - 1]; + unsigned int addend = get_addend(rel, elf); + // Use relocated value of DT_INIT_ARRAY's first entry for the + // function to be called by the injected code. + if (ELF32_R_TYPE(rel->r_info) == rel_type) { + original_init = addend; + } else if (ELF32_R_TYPE(rel->r_info) == rel_type2) { + ElfSymtab_Section *symtab = (ElfSymtab_Section *)section->getLink(); + original_init = symtab->syms[ELF32_R_SYM(rel->r_info)].value.getValue() + addend; + } else { + fprintf(stderr, "Unsupported relocation type for DT_INIT_ARRAY's first entry. Skipping\n"); + return -1; + } + } + + section->rels.assign(new_rels.begin(), new_rels.end()); + section->shrink(new_rels.size() * section->getEntSize()); + + ElfRelHackCode_Section *relhackcode = new ElfRelHackCode_Section(relhackcode_section, *elf, original_init); + relhackcode->insertBefore(section); + relhack->insertAfter(relhackcode); + if (section->getOffset() + section->getSize() >= old_end) { + fprintf(stderr, "No gain. Skipping\n"); + return -1; + } + + // Adjust PT_LOAD segments + for (ElfSegment *segment = elf->getSegmentByType(PT_LOAD); segment; + segment = elf->getSegmentByType(PT_LOAD, segment)) { + maybe_split_segment(elf, segment, fill); + } + + // Ensure Elf sections will be at their final location. + elf->normalize(); + ElfLocation *init = new ElfLocation(relhackcode, relhackcode->getEntryPoint()); + if (init_array) { + // Adjust the first DT_INIT_ARRAY entry to point at the injected code + // by transforming its relocation into a relative one pointing to the + // address of the injected code. + Rel_Type *rel = §ion->rels[init_array_reloc - 1]; + rel->r_info = ELF32_R_INFO(0, rel_type); // Set as a relative relocation + set_relative_reloc(§ion->rels[init_array_reloc - 1], elf, init->getValue()); + } else if (!dyn->setValueForType(DT_INIT, init)) { + fprintf(stderr, "Can't grow .dynamic section to set DT_INIT. Skipping\n"); + return -1; + } + // TODO: adjust the value according to the remaining number of relative relocations + if (dyn->getValueForType(Rel_Type::d_tag_count)) + dyn->setValueForType(Rel_Type::d_tag_count, new ElfPlainValue(0)); + + return 0; +} + +static inline int backup_file(const char *name) +{ + std::string fname(name); + fname += ".bak"; + return rename(name, fname.c_str()); +} + +void do_file(const char *name, bool backup = false, bool force = false, bool fill = false) +{ + std::ifstream file(name, std::ios::in|std::ios::binary); + Elf elf(file); + unsigned int size = elf.getSize(); + fprintf(stderr, "%s: ", name); + if (elf.getType() != ET_DYN) { + fprintf(stderr, "Not a shared object. Skipping\n"); + return; + } + + for (ElfSection *section = elf.getSection(1); section != NULL; + section = section->getNext()) { + if (section->getName() && + (strncmp(section->getName(), ".elfhack.", 9) == 0)) { + fprintf(stderr, "Already elfhacked. Skipping\n"); + return; + } + } + + int exit = -1; + switch (elf.getMachine()) { + case EM_386: + exit = do_relocation_section<Elf_Rel>(&elf, R_386_RELATIVE, R_386_32, force, fill); + break; + case EM_X86_64: + exit = do_relocation_section<Elf_Rela>(&elf, R_X86_64_RELATIVE, R_X86_64_64, force, fill); + break; + case EM_ARM: + exit = do_relocation_section<Elf_Rel>(&elf, R_ARM_RELATIVE, R_ARM_ABS32, force, fill); + break; + } + if (exit == 0) { + if (!force && (elf.getSize() >= size)) { + fprintf(stderr, "No gain. Skipping\n"); + } else if (backup && backup_file(name) != 0) { + fprintf(stderr, "Couln't create backup file\n"); + } else { + std::ofstream ofile(name, std::ios::out|std::ios::binary|std::ios::trunc); + elf.write(ofile); + fprintf(stderr, "Reduced by %d bytes\n", size - elf.getSize()); + } + } +} + +void undo_file(const char *name, bool backup = false) +{ + std::ifstream file(name, std::ios::in|std::ios::binary); + Elf elf(file); + unsigned int size = elf.getSize(); + fprintf(stderr, "%s: ", name); + if (elf.getType() != ET_DYN) { + fprintf(stderr, "Not a shared object. Skipping\n"); + return; + } + + ElfSection *data = NULL, *text = NULL; + for (ElfSection *section = elf.getSection(1); section != NULL; + section = section->getNext()) { + if (section->getName() && + (strcmp(section->getName(), elfhack_data) == 0)) + data = section; + if (section->getName() && + (strcmp(section->getName(), elfhack_text) == 0)) + text = section; + } + + if (!data || !text) { + fprintf(stderr, "Not elfhacked. Skipping\n"); + return; + } + if (data != text->getNext()) { + fprintf(stderr, elfhack_data " section not following " elfhack_text ". Skipping\n"); + return; + } + + ElfSegment *first = elf.getSegmentByType(PT_LOAD); + ElfSegment *second = elf.getSegmentByType(PT_LOAD, first); + ElfSegment *filler = NULL; + // If the second PT_LOAD is a filler from elfhack --fill, check the third. + if (!second->isElfHackFillerSegment()) { + filler = second; + second = elf.getSegmentByType(PT_LOAD, filler); + } + if (second->getFlags() != first->getFlags()) { + fprintf(stderr, "Couldn't identify elfhacked PT_LOAD segments. Skipping\n"); + return; + } + // Move sections from the second PT_LOAD to the first, and remove the + // second PT_LOAD segment. + for (std::list<ElfSection *>::iterator section = second->begin(); + section != second->end(); ++section) + first->addSection(*section); + + elf.removeSegment(second); + if (filler) + elf.removeSegment(filler); + + if (backup && backup_file(name) != 0) { + fprintf(stderr, "Couln't create backup file\n"); + } else { + std::ofstream ofile(name, std::ios::out|std::ios::binary|std::ios::trunc); + elf.write(ofile); + fprintf(stderr, "Grown by %d bytes\n", elf.getSize() - size); + } +} + +int main(int argc, char *argv[]) +{ + int arg; + bool backup = false; + bool force = false; + bool revert = false; + bool fill = false; + char *lastSlash = rindex(argv[0], '/'); + if (lastSlash != NULL) + rundir = strndup(argv[0], lastSlash - argv[0]); + for (arg = 1; arg < argc; arg++) { + if (strcmp(argv[arg], "-f") == 0) + force = true; + else if (strcmp(argv[arg], "-b") == 0) + backup = true; + else if (strcmp(argv[arg], "-r") == 0) + revert = true; + else if (strcmp(argv[arg], "--fill") == 0) + fill = true; + else if (revert) { + undo_file(argv[arg], backup); + } else + do_file(argv[arg], backup, force, fill); + } + + free(rundir); + return 0; +} diff --git a/build/unix/elfhack/elfxx.h b/build/unix/elfhack/elfxx.h new file mode 100644 index 000000000..3d87b0f2f --- /dev/null +++ b/build/unix/elfhack/elfxx.h @@ -0,0 +1,701 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include <stdexcept> +#include <list> +#include <vector> +#include <cstring> +#include <iostream> +#include <fstream> +#include <algorithm> +#include <elf.h> +#include <asm/byteorder.h> + +// Technically, __*_to_cpu and __cpu_to* function are equivalent, +// so swap can use either of both. +#define def_swap(endian, type, bits) \ +static inline type ## bits ## _t swap(type ## bits ## _t i) { \ + return __ ## endian ## bits ## _to_cpu(i); \ +} + +class little_endian { +public: +def_swap(le, uint, 16); +def_swap(le, uint, 32); +def_swap(le, uint, 64); +def_swap(le, int, 16); +def_swap(le, int, 32); +def_swap(le, int, 64); +}; + +class big_endian { +public: +def_swap(be, uint, 16); +def_swap(be, uint, 32); +def_swap(be, uint, 64); +def_swap(be, int, 16); +def_swap(be, int, 32); +def_swap(be, int, 64); +}; + +// forward declaration +class ElfSection; +class ElfSegment; +// TODO: Rename Elf_* types +class Elf_Ehdr; +class Elf_Phdr; +class Elf; +class ElfDynamic_Section; +class ElfStrtab_Section; + +class Elf_Ehdr_Traits { +public: + typedef Elf32_Ehdr Type32; + typedef Elf64_Ehdr Type64; + + template <class endian, typename R, typename T> + static void swap(T &t, R &r); +}; + +class Elf_Phdr_Traits { +public: + typedef Elf32_Phdr Type32; + typedef Elf64_Phdr Type64; + + template <class endian, typename R, typename T> + static void swap(T &t, R &r); +}; + +class Elf_Shdr_Traits { +public: + typedef Elf32_Shdr Type32; + typedef Elf64_Shdr Type64; + + template <class endian, typename R, typename T> + static void swap(T &t, R &r); +}; + +class Elf_Dyn_Traits { +public: + typedef Elf32_Dyn Type32; + typedef Elf64_Dyn Type64; + + template <class endian, typename R, typename T> + static void swap(T &t, R &r); +}; + +class Elf_Sym_Traits { +public: + typedef Elf32_Sym Type32; + typedef Elf64_Sym Type64; + + template <class endian, typename R, typename T> + static void swap(T &t, R &r); +}; + +class Elf_Rel_Traits { +public: + typedef Elf32_Rel Type32; + typedef Elf64_Rel Type64; + + template <class endian, typename R, typename T> + static void swap(T &t, R &r); +}; + +class Elf_Rela_Traits { +public: + typedef Elf32_Rela Type32; + typedef Elf64_Rela Type64; + + template <class endian, typename R, typename T> + static void swap(T &t, R &r); +}; + +class ElfValue { +public: + virtual unsigned int getValue() { return 0; } + virtual ElfSection *getSection() { return NULL; } +}; + +class ElfPlainValue: public ElfValue { + unsigned int value; +public: + ElfPlainValue(unsigned int val): value(val) {}; + unsigned int getValue() { return value; } +}; + +class ElfLocation: public ElfValue { + ElfSection *section; + unsigned int offset; +public: + enum position { ABSOLUTE, RELATIVE }; + ElfLocation(): section(NULL), offset(0) {}; + ElfLocation(ElfSection *section, unsigned int off, enum position pos = RELATIVE); + ElfLocation(unsigned int location, Elf *elf); + unsigned int getValue(); + ElfSection *getSection() { return section; } + const char *getBuffer(); +}; + +class ElfSize: public ElfValue { + ElfSection *section; +public: + ElfSize(ElfSection *s): section(s) {}; + unsigned int getValue(); + ElfSection *getSection() { return section; } +}; + +class ElfEntSize: public ElfValue { + ElfSection *section; +public: + ElfEntSize(ElfSection *s): section(s) {}; + unsigned int getValue(); + ElfSection *getSection() { return section; } +}; + +template <typename T> +class serializable: public T::Type32 { +public: + serializable() {}; + serializable(const typename T::Type32 &p): T::Type32(p) {}; + +private: + template <typename R> + void init(const char *buf, size_t len, char ei_data) + { + R e; + assert(len >= sizeof(e)); + memcpy(&e, buf, sizeof(e)); + if (ei_data == ELFDATA2LSB) { + T::template swap<little_endian>(e, *this); + return; + } else if (ei_data == ELFDATA2MSB) { + T::template swap<big_endian>(e, *this); + return; + } + throw std::runtime_error("Unsupported ELF data encoding"); + } + + template <typename R> + void serialize(const char *buf, size_t len, char ei_data) + { + assert(len >= sizeof(R)); + if (ei_data == ELFDATA2LSB) { + T::template swap<little_endian>(*this, *(R *)buf); + return; + } else if (ei_data == ELFDATA2MSB) { + T::template swap<big_endian>(*this, *(R *)buf); + return; + } + throw std::runtime_error("Unsupported ELF data encoding"); + } + +public: + serializable(const char *buf, size_t len, char ei_class, char ei_data) + { + if (ei_class == ELFCLASS32) { + init<typename T::Type32>(buf, len, ei_data); + return; + } else if (ei_class == ELFCLASS64) { + init<typename T::Type64>(buf, len, ei_data); + return; + } + throw std::runtime_error("Unsupported ELF class"); + } + + serializable(std::ifstream &file, char ei_class, char ei_data) + { + if (ei_class == ELFCLASS32) { + typename T::Type32 e; + file.read((char *)&e, sizeof(e)); + init<typename T::Type32>((char *)&e, sizeof(e), ei_data); + return; + } else if (ei_class == ELFCLASS64) { + typename T::Type64 e; + file.read((char *)&e, sizeof(e)); + init<typename T::Type64>((char *)&e, sizeof(e), ei_data); + return; + } + throw std::runtime_error("Unsupported ELF class or data encoding"); + } + + void serialize(std::ofstream &file, char ei_class, char ei_data) + { + if (ei_class == ELFCLASS32) { + typename T::Type32 e; + serialize<typename T::Type32>((char *)&e, sizeof(e), ei_data); + file.write((char *)&e, sizeof(e)); + return; + } else if (ei_class == ELFCLASS64) { + typename T::Type64 e; + serialize<typename T::Type64>((char *)&e, sizeof(e), ei_data); + file.write((char *)&e, sizeof(e)); + return; + } + throw std::runtime_error("Unsupported ELF class or data encoding"); + } + + void serialize(char *buf, size_t len, char ei_class, char ei_data) + { + if (ei_class == ELFCLASS32) { + serialize<typename T::Type32>(buf, len, ei_data); + return; + } else if (ei_class == ELFCLASS64) { + serialize<typename T::Type64>(buf, len, ei_data); + return; + } + throw std::runtime_error("Unsupported ELF class"); + } + + static inline unsigned int size(char ei_class) + { + if (ei_class == ELFCLASS32) + return sizeof(typename T::Type32); + else if (ei_class == ELFCLASS64) + return sizeof(typename T::Type64); + return 0; + } +}; + +typedef serializable<Elf_Shdr_Traits> Elf_Shdr; + +class Elf { +public: + Elf(std::ifstream &file); + ~Elf(); + + /* index == -1 is treated as index == ehdr.e_shstrndx */ + ElfSection *getSection(int index); + + ElfSection *getSectionAt(unsigned int offset); + + ElfSegment *getSegmentByType(unsigned int type, ElfSegment *last = NULL); + + ElfDynamic_Section *getDynSection(); + + void normalize(); + void write(std::ofstream &file); + + char getClass(); + char getData(); + char getType(); + char getMachine(); + unsigned int getSize(); + + void insertSegmentAfter(ElfSegment *previous, ElfSegment *segment) { + std::vector<ElfSegment *>::iterator prev = std::find(segments.begin(), segments.end(), previous); + segments.insert(prev + 1, segment); + } + + void removeSegment(ElfSegment *segment); + +private: + Elf_Ehdr *ehdr; + ElfLocation eh_entry; + ElfStrtab_Section *eh_shstrndx; + ElfSection **sections; + std::vector<ElfSegment *> segments; + ElfSection *shdr_section, *phdr_section; + /* Values used only during initialization */ + Elf_Shdr **tmp_shdr; + std::ifstream *tmp_file; +}; + +class ElfSection { +public: + typedef union { + ElfSection *section; + int index; + } SectionInfo; + + ElfSection(Elf_Shdr &s, std::ifstream *file, Elf *parent); + + virtual ~ElfSection() { + delete[] data; + } + + const char *getName() { return name; } + unsigned int getType() { return shdr.sh_type; } + unsigned int getFlags() { return shdr.sh_flags; } + unsigned int getAddr(); + unsigned int getSize() { return shdr.sh_size; } + unsigned int getAddrAlign() { return shdr.sh_addralign; } + unsigned int getEntSize() { return shdr.sh_entsize; } + const char *getData() { return data; } + ElfSection *getLink() { return link; } + SectionInfo getInfo() { return info; } + + void shrink(unsigned int newsize) { + if (newsize < shdr.sh_size) + shdr.sh_size = newsize; + } + + unsigned int getOffset(); + int getIndex(); + Elf_Shdr &getShdr(); + + ElfSection *getNext() { return next; } + ElfSection *getPrevious() { return previous; } + + virtual bool isRelocatable() { + return ((getType() == SHT_SYMTAB) || + (getType() == SHT_STRTAB) || + (getType() == SHT_RELA) || + (getType() == SHT_HASH) || + (getType() == SHT_NOTE) || + (getType() == SHT_REL) || + (getType() == SHT_DYNSYM) || + (getType() == SHT_GNU_HASH) || + (getType() == SHT_GNU_verdef) || + (getType() == SHT_GNU_verneed) || + (getType() == SHT_GNU_versym) || + getSegmentByType(PT_INTERP)) && + (getFlags() & SHF_ALLOC); + } + + void insertAfter(ElfSection *section, bool dirty = true) { + if (previous != NULL) + previous->next = next; + if (next != NULL) + next->previous = previous; + previous = section; + if (section != NULL) { + next = section->next; + section->next = this; + } else + next = NULL; + if (next != NULL) + next->previous = this; + if (dirty) + markDirty(); + insertInSegments(section->segments); + } + + void insertBefore(ElfSection *section, bool dirty = true) { + if (previous != NULL) + previous->next = next; + if (next != NULL) + next->previous = previous; + next = section; + if (section != NULL) { + previous = section->previous; + section->previous = this; + } else + previous = NULL; + if (previous != NULL) + previous->next = this; + if (dirty) + markDirty(); + insertInSegments(section->segments); + } + + void markDirty() { + if (link != NULL) + shdr.sh_link = -1; + if (info.index) + shdr.sh_info = -1; + shdr.sh_offset = -1; + if (isRelocatable()) + shdr.sh_addr = -1; + if (next) + next->markDirty(); + } + + virtual void serialize(std::ofstream &file, char ei_class, char ei_data) + { + if (getType() == SHT_NOBITS) + return; + file.seekp(getOffset()); + file.write(data, getSize()); + } + +private: + friend class ElfSegment; + + void addToSegment(ElfSegment *segment) { + segments.push_back(segment); + } + + void removeFromSegment(ElfSegment *segment) { + std::vector<ElfSegment *>::iterator i = std::find(segments.begin(), segments.end(), segment); + segments.erase(i, i + 1); + } + + ElfSegment *getSegmentByType(unsigned int type); + + void insertInSegments(std::vector<ElfSegment *> &segs); + +protected: + Elf_Shdr shdr; + char *data; + const char *name; +private: + ElfSection *link; + SectionInfo info; + ElfSection *next, *previous; + int index; + std::vector<ElfSegment *> segments; +}; + +class ElfSegment { +public: + ElfSegment(Elf_Phdr *phdr); + + unsigned int getType() { return type; } + unsigned int getFlags() { return flags; } + unsigned int getAlign() { return align; } + + ElfSection *getFirstSection() { return sections.empty() ? NULL : sections.front(); } + int getVPDiff() { return v_p_diff; } + unsigned int getFileSize(); + unsigned int getMemSize(); + unsigned int getOffset(); + unsigned int getAddr(); + + void addSection(ElfSection *section); + void removeSection(ElfSection *section); + + std::list<ElfSection *>::iterator begin() { return sections.begin(); } + std::list<ElfSection *>::iterator end() { return sections.end(); } + + void clear(); + + bool isElfHackFillerSegment() { + return type == PT_LOAD && flags == 0; + } +private: + unsigned int type; + int v_p_diff; // Difference between physical and virtual address + unsigned int flags; + unsigned int align; + std::list<ElfSection *> sections; + // The following are only really used for PT_GNU_RELRO until something + // better is found. + unsigned int vaddr; + unsigned int filesz, memsz; +}; + +class Elf_Ehdr: public serializable<Elf_Ehdr_Traits>, public ElfSection { +public: + Elf_Ehdr(std::ifstream &file, char ei_class, char ei_data); + void serialize(std::ofstream &file, char ei_class, char ei_data) + { + serializable<Elf_Ehdr_Traits>::serialize(file, ei_class, ei_data); + } +}; + +class Elf_Phdr: public serializable<Elf_Phdr_Traits> { +public: + Elf_Phdr() {}; + Elf_Phdr(std::ifstream &file, char ei_class, char ei_data) + : serializable<Elf_Phdr_Traits>(file, ei_class, ei_data) {}; + bool contains(ElfSection *section) + { + unsigned int size = section->getSize(); + unsigned int addr = section->getAddr(); + // This may be biased, but should work in most cases + if ((section->getFlags() & SHF_ALLOC) == 0) + return false; + // Special case for PT_DYNAMIC. Eventually, this should + // be better handled than special cases + if ((p_type == PT_DYNAMIC) && (section->getType() != SHT_DYNAMIC)) + return false; + // Special case for PT_TLS. + if ((p_type == PT_TLS) && !(section->getFlags() & SHF_TLS)) + return false; + return (addr >= p_vaddr) && + (addr + size <= p_vaddr + p_memsz); + + } +}; + +typedef serializable<Elf_Dyn_Traits> Elf_Dyn; + +struct Elf_DynValue { + unsigned int tag; + ElfValue *value; +}; + +class ElfDynamic_Section: public ElfSection { +public: + ElfDynamic_Section(Elf_Shdr &s, std::ifstream *file, Elf *parent); + ~ElfDynamic_Section(); + + void serialize(std::ofstream &file, char ei_class, char ei_data); + + ElfValue *getValueForType(unsigned int tag); + ElfSection *getSectionForType(unsigned int tag); + bool setValueForType(unsigned int tag, ElfValue *val); +private: + std::vector<Elf_DynValue> dyns; +}; + +typedef serializable<Elf_Sym_Traits> Elf_Sym; + +struct Elf_SymValue { + const char *name; + unsigned char info; + unsigned char other; + ElfLocation value; + unsigned int size; + bool defined; +}; + +#define STT(type) (1 << STT_ ##type) + +class ElfSymtab_Section: public ElfSection { +public: + ElfSymtab_Section(Elf_Shdr &s, std::ifstream *file, Elf *parent); + + void serialize(std::ofstream &file, char ei_class, char ei_data); + + Elf_SymValue *lookup(const char *name, unsigned int type_filter = STT(OBJECT) | STT(FUNC)); + +//private: // Until we have a real API + std::vector<Elf_SymValue> syms; +}; + +class Elf_Rel: public serializable<Elf_Rel_Traits> { +public: + Elf_Rel(std::ifstream &file, char ei_class, char ei_data) + : serializable<Elf_Rel_Traits>(file, ei_class, ei_data) {}; + + static const unsigned int sh_type = SHT_REL; + static const unsigned int d_tag = DT_REL; + static const unsigned int d_tag_count = DT_RELCOUNT; +}; + +class Elf_Rela: public serializable<Elf_Rela_Traits> { +public: + Elf_Rela(std::ifstream &file, char ei_class, char ei_data) + : serializable<Elf_Rela_Traits>(file, ei_class, ei_data) {}; + + static const unsigned int sh_type = SHT_RELA; + static const unsigned int d_tag = DT_RELA; + static const unsigned int d_tag_count = DT_RELACOUNT; +}; + +template <class Rel> +class ElfRel_Section: public ElfSection { +public: + ElfRel_Section(Elf_Shdr &s, std::ifstream *file, Elf *parent) + : ElfSection(s, file, parent) + { + int pos = file->tellg(); + file->seekg(shdr.sh_offset); + for (unsigned int i = 0; i < s.sh_size / s.sh_entsize; i++) { + Rel r(*file, parent->getClass(), parent->getData()); + rels.push_back(r); + } + file->seekg(pos); + } + + void serialize(std::ofstream &file, char ei_class, char ei_data) + { + for (typename std::vector<Rel>::iterator i = rels.begin(); + i != rels.end(); ++i) + (*i).serialize(file, ei_class, ei_data); + } +//private: // Until we have a real API + std::vector<Rel> rels; +}; + +class ElfStrtab_Section: public ElfSection { +public: + ElfStrtab_Section(Elf_Shdr &s, std::ifstream *file, Elf *parent) + : ElfSection(s, file, parent) + { + table.push_back(table_storage(data, shdr.sh_size)); + } + + ~ElfStrtab_Section() + { + for (std::vector<table_storage>::iterator t = table.begin() + 1; + t != table.end(); t++) + delete[] t->buf; + } + + const char *getStr(unsigned int index); + + const char *getStr(const char *string); + + unsigned int getStrIndex(const char *string); + + void serialize(std::ofstream &file, char ei_class, char ei_data); +private: + struct table_storage { + unsigned int size, used; + char *buf; + + table_storage(): size(4096), used(0), buf(new char[4096]) {} + table_storage(const char *data, unsigned int sz) + : size(sz), used(sz), buf(const_cast<char *>(data)) {} + }; + std::vector<table_storage> table; +}; + +inline char Elf::getClass() { + return ehdr->e_ident[EI_CLASS]; +} + +inline char Elf::getData() { + return ehdr->e_ident[EI_DATA]; +} + +inline char Elf::getType() { + return ehdr->e_type; +} + +inline char Elf::getMachine() { + return ehdr->e_machine; +} + +inline unsigned int Elf::getSize() { + ElfSection *section; + for (section = shdr_section /* It's usually not far from the end */; + section->getNext() != NULL; section = section->getNext()); + return section->getOffset() + section->getSize(); +} + +inline ElfSegment *ElfSection::getSegmentByType(unsigned int type) { + for (std::vector<ElfSegment *>::iterator seg = segments.begin(); seg != segments.end(); seg++) + if ((*seg)->getType() == type) + return *seg; + return NULL; +} + +inline void ElfSection::insertInSegments(std::vector<ElfSegment *> &segs) { + for (std::vector<ElfSegment *>::iterator it = segs.begin(); it != segs.end(); ++it) { + (*it)->addSection(this); + } +} + +inline ElfLocation::ElfLocation(ElfSection *section, unsigned int off, enum position pos) +: section(section) { + if ((pos == ABSOLUTE) && section) + offset = off - section->getAddr(); + else + offset = off; +} + +inline ElfLocation::ElfLocation(unsigned int location, Elf *elf) { + section = elf->getSectionAt(location); + offset = location - (section ? section->getAddr() : 0); +} + +inline unsigned int ElfLocation::getValue() { + return (section ? section->getAddr() : 0) + offset; +} + +inline const char *ElfLocation::getBuffer() { + return section ? section->getData() + offset : NULL; +} + +inline unsigned int ElfSize::getValue() { + return section->getSize(); +} + +inline unsigned int ElfEntSize::getValue() { + return section->getEntSize(); +} diff --git a/build/unix/elfhack/inject.c b/build/unix/elfhack/inject.c new file mode 100644 index 000000000..879cb4500 --- /dev/null +++ b/build/unix/elfhack/inject.c @@ -0,0 +1,41 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include <stdint.h> +#include <elf.h> + +/* The Android NDK headers define those */ +#undef Elf_Ehdr +#undef Elf_Addr + +#if BITS == 32 +#define Elf_Ehdr Elf32_Ehdr +#define Elf_Addr Elf32_Addr +#else +#define Elf_Ehdr Elf64_Ehdr +#define Elf_Addr Elf64_Addr +#endif + +extern __attribute__((visibility("hidden"))) void original_init(int argc, char **argv, char **env); + +extern __attribute__((visibility("hidden"))) Elf32_Rel relhack[]; +extern __attribute__((visibility("hidden"))) Elf_Ehdr elf_header; + +int init(int argc, char **argv, char **env) +{ + Elf32_Rel *rel; + Elf_Addr *ptr, *start; + for (rel = relhack; rel->r_offset; rel++) { + start = (Elf_Addr *)((intptr_t)&elf_header + rel->r_offset); + for (ptr = start; ptr < &start[rel->r_info]; ptr++) + *ptr += (intptr_t)&elf_header; + } + +#ifndef NOINIT + original_init(argc, argv, env); +#endif + // Ensure there is no tail-call optimization, avoiding the use of the + // B.W instruction in Thumb for the call above. + return 0; +} diff --git a/build/unix/elfhack/moz.build b/build/unix/elfhack/moz.build new file mode 100644 index 000000000..895d11993 --- /dev/null +++ b/build/unix/elfhack/moz.build @@ -0,0 +1,6 @@ +# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + diff --git a/build/unix/elfhack/test-array.c b/build/unix/elfhack/test-array.c new file mode 100644 index 000000000..202c26666 --- /dev/null +++ b/build/unix/elfhack/test-array.c @@ -0,0 +1,8 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "test.c" + +__attribute__((section(".init_array"), used)) +static void (*init_array[])() = { end_test, test }; diff --git a/build/unix/elfhack/test-ctors.c b/build/unix/elfhack/test-ctors.c new file mode 100644 index 000000000..4fff05807 --- /dev/null +++ b/build/unix/elfhack/test-ctors.c @@ -0,0 +1,17 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "test.c" + +/* Recent binutils would put .ctors content into a .init_array section */ +__attribute__((section(".manual_ctors"), used)) +static void (*ctors[])() = { (void (*)())-1, end_test, test, NULL }; + +__attribute__((section(".init"))) +void _init() { + void (**func)() = &ctors[sizeof(ctors) / sizeof(void (*)()) - 1]; + while (*(--func) != (void (*)())-1) { + (*func)(); + } +} diff --git a/build/unix/elfhack/test.c b/build/unix/elfhack/test.c new file mode 100644 index 000000000..3393f03b5 --- /dev/null +++ b/build/unix/elfhack/test.c @@ -0,0 +1,162 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifdef DEF +DEF(This) +DEF(is) +DEF(a) +DEF(test) +DEF(of) +DEF(string) +DEF(array) +DEF(for) +DEF(use) +DEF(with) +DEF(elfhack) +DEF(to) +DEF(see) +DEF(whether) +DEF(it) +DEF(breaks) +DEF(anything) +DEF(but) +DEF(one) +DEF(needs) +DEF(quite) +DEF(some) +DEF(strings) +DEF(before) +DEF(the) +DEF(program) +DEF(can) +DEF(do) +DEF(its) +DEF(work) +DEF(efficiently) +DEF(Without) +DEF(enough) +DEF(data) +DEF(relocation) +DEF(sections) +DEF(are) +DEF(not) +DEF(sufficiently) +DEF(large) +DEF(and) +DEF(injected) +DEF(code) +DEF(wouldnt) +DEF(fit) +DEF(Said) +DEF(otherwise) +DEF(we) +DEF(need) +DEF(more) +DEF(words) +DEF(than) +DEF(up) +DEF(here) +DEF(so) +DEF(that) +DEF(relocations) +DEF(take) +DEF(significant) +DEF(bytes) +DEF(amounts) +DEF(which) +DEF(isnt) +DEF(exactly) +DEF(easily) +DEF(achieved) +DEF(like) +DEF(this) +DEF(Actually) +DEF(I) +DEF(must) +DEF(cheat) +DEF(by) +DEF(including) +DEF(these) +DEF(phrases) +DEF(several) +DEF(times) + +#else +#pragma GCC visibility push(default) +#include <stdlib.h> +#include <stdio.h> + +#define DEF(w) static const char str_ ## w[] = #w; +#include "test.c" +#undef DEF + +const char *strings[] = { +#define DEF(w) str_ ## w, +#include "test.c" +#include "test.c" +#include "test.c" +}; + +/* Create a hole between two zones of relative relocations */ +const int hole[] = { + 42, 42, 42, 42 +}; + +const char *strings2[] = { +#include "test.c" +#include "test.c" +#include "test.c" +#include "test.c" +#include "test.c" +#undef DEF +}; + +static int ret = 1; + +int print_status() { + fprintf(stderr, "%s\n", ret ? "FAIL" : "PASS"); + return ret; +} + +/* On ARM, this creates a .tbss section before .init_array, which + * elfhack could then pick instead of .init_array. + * Also, when .tbss is big enough, elfhack may wrongfully consider + * following sections as part of the PT_TLS segment. + * Finally, gold makes TLS segments end on an aligned virtual address, + * even when the underlying section ends before that, and elfhack + * sanity checks may yield an error. */ +__thread int foo; +__thread long long int bar[512]; + +void end_test() { + static int count = 0; + /* Only exit when both constructors have been called */ + if (++count == 2) + ret = 0; +} + +void test() { + int i = 0, j = 0; +#define DEF_(a,i,w) \ + if (a[i++] != str_ ## w) return; +#define DEF(w) DEF_(strings,i,w) +#include "test.c" +#include "test.c" +#include "test.c" +#undef DEF +#define DEF(w) DEF_(strings2,j,w) +#include "test.c" +#include "test.c" +#include "test.c" +#include "test.c" +#include "test.c" +#undef DEF + if (i != sizeof(strings)/sizeof(strings[0]) && + j != sizeof(strings2)/sizeof(strings2[0])) + fprintf(stderr, "WARNING: Test doesn't cover the whole array\n"); + end_test(); +} + +#pragma GCC visibility pop +#endif diff --git a/build/unix/gnu-ld-scripts/components-export-list b/build/unix/gnu-ld-scripts/components-export-list new file mode 100644 index 000000000..8c2470886 --- /dev/null +++ b/build/unix/gnu-ld-scripts/components-export-list @@ -0,0 +1 @@ +_NSModule diff --git a/build/unix/gnu-ld-scripts/components-mapfile b/build/unix/gnu-ld-scripts/components-mapfile new file mode 100644 index 000000000..d8e36ea3a --- /dev/null +++ b/build/unix/gnu-ld-scripts/components-mapfile @@ -0,0 +1,7 @@ +{ + global: + NSModule; + NSGetModule; + __RLD_MAP; + local: *; +}; diff --git a/build/unix/gnu-ld-scripts/components-version-script b/build/unix/gnu-ld-scripts/components-version-script new file mode 100644 index 000000000..bc1d5ec33 --- /dev/null +++ b/build/unix/gnu-ld-scripts/components-version-script @@ -0,0 +1,7 @@ +EXPORTED { + global: + NSModule; + NSGetModule; + __RLD_MAP; + local: *; +}; diff --git a/build/unix/headers/bits/c++config.h b/build/unix/headers/bits/c++config.h new file mode 100644 index 000000000..c0182c128 --- /dev/null +++ b/build/unix/headers/bits/c++config.h @@ -0,0 +1,2 @@ +#include_next <bits/c++config.h> +#undef _GLIBCXX_USE_FLOAT128 diff --git a/build/unix/moz.build b/build/unix/moz.build new file mode 100644 index 000000000..2abda39d2 --- /dev/null +++ b/build/unix/moz.build @@ -0,0 +1,16 @@ +# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +if CONFIG['STDCXX_COMPAT']: + DIRS += ['stdc++compat'] + +if CONFIG['USE_ELF_HACK']: + DIRS += ['elfhack'] + +TEST_DIRS += ['test'] + +MODULE = 'build' + diff --git a/build/unix/mozconfig.asan b/build/unix/mozconfig.asan new file mode 100644 index 000000000..9df4b30bf --- /dev/null +++ b/build/unix/mozconfig.asan @@ -0,0 +1,22 @@ +. "$topsrcdir/build/mozconfig.common" + +# Use Clang as specified in manifest +export CC="$topsrcdir/clang/bin/clang -fgnu89-inline" +export CXX="$topsrcdir/clang/bin/clang++" + +# Mandatory flags for ASan +export ASANFLAGS="-fsanitize=address -Dxmalloc=myxmalloc -fPIC" +export CFLAGS="$ASANFLAGS" +export CXXFLAGS="$ASANFLAGS" +export LDFLAGS="-fsanitize=address" + +# Enable ASan specific code and build workarounds +ac_add_options --enable-address-sanitizer + +# Mandatory options required for ASan builds (both on Linux and Mac) +export MOZ_DEBUG_SYMBOLS=1 +ac_add_options --enable-debug-symbols +ac_add_options --disable-install-strip +ac_add_options --disable-jemalloc +ac_add_options --disable-crashreporter +ac_add_options --disable-elf-hack diff --git a/build/unix/mozconfig.linux b/build/unix/mozconfig.linux new file mode 100644 index 000000000..e1c1bb6a2 --- /dev/null +++ b/build/unix/mozconfig.linux @@ -0,0 +1,6 @@ +. "$topsrcdir/build/mozconfig.common" + +CC="/tools/gcc-4.7.3-0moz1/bin/gcc" +CXX="/tools/gcc-4.7.3-0moz1/bin/g++" + +ac_add_options --enable-elf-hack diff --git a/build/unix/mozconfig.linux32 b/build/unix/mozconfig.linux32 new file mode 100644 index 000000000..3b8c1e455 --- /dev/null +++ b/build/unix/mozconfig.linux32 @@ -0,0 +1,10 @@ +. "$topsrcdir/build/unix/mozconfig.linux" + +if test `uname -m` = "x86_64"; then + # -march=pentiumpro is what our 32-bit native toolchain defaults to + CC="$CC -m32 -march=pentiumpro" + CXX="$CXX -m32 -march=pentiumpro" + ac_add_options --target=i686-pc-linux + ac_add_options --x-libraries=/usr/lib + export PKG_CONFIG_LIBDIR=/usr/lib/pkgconfig:/usr/share/pkgconfig +fi diff --git a/build/unix/mozilla.in b/build/unix/mozilla.in new file mode 100644 index 000000000..d2251eae0 --- /dev/null +++ b/build/unix/mozilla.in @@ -0,0 +1,108 @@ +#!/bin/sh +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +## +## Usage: +## +## $ mozilla [args] +## +## This script is meant to run the application binary from mozilla/dist/bin. +## +## The script will setup all the environment voodoo needed to make +## the application binary to work. +## + +#uncomment for debugging +#set -x + +moz_libdir=%MOZAPPDIR% + +# Use run-mozilla.sh in the current dir if it exists +# If not, then start resolving symlinks until we find run-mozilla.sh +found=0 +progname="$0" +curdir=`dirname "$progname"` +progbase=`basename "$progname"` +run_moz="$curdir/run-mozilla.sh" +if test -x "$run_moz"; then + dist_bin="$curdir" + found=1 +else + here=`/bin/pwd` + while [ -h "$progname" ]; do + bn=`basename "$progname"` + cd `dirname "$progname"` + # Resolve symlink of dirname + cd `/bin/pwd` + progname=`/bin/ls -l "$bn" | sed -e 's/^.* -> //' ` + progbase=`basename "$progname"` + if [ ! -x "$progname" ]; then + break + fi + curdir=`dirname "$progname"` + run_moz="$curdir/run-mozilla.sh" + if [ -x "$run_moz" ]; then + cd "$curdir" + dist_bin=`/bin/pwd` + run_moz="$dist_bin/run-mozilla.sh" + found=1 + break + fi + done + cd "$here" +fi +if [ $found = 0 ]; then + # Check default compile-time libdir + if [ -x "$moz_libdir/run-mozilla.sh" ]; then + dist_bin="$moz_libdir" + run_moz="$moz_libdir/run-mozilla.sh" + else + echo "Cannot find %MOZ_APP_DISPLAYNAME% runtime directory. Exiting." + exit 1 + fi +fi + +script_args="" +debugging=0 +MOZILLA_BIN="${progbase}-bin" + +if [ "$OSTYPE" = "beos" ]; then + mimeset -F "$MOZILLA_BIN" +fi + +pass_arg_count=0 +while [ $# -gt $pass_arg_count ] +do + case "$1" in + -p | --pure | -pure) + MOZILLA_BIN="${MOZILLA_BIN}.pure" + shift + ;; + -g | --debug) + script_args="$script_args -g" + debugging=1 + shift + ;; + -d | --debugger) + script_args="$script_args -d $2" + shift 2 + ;; + *) + # Move the unrecognized argument to the end of the list. + arg="$1" + shift + set -- "$@" "$arg" + pass_arg_count=`expr $pass_arg_count + 1` + ;; + esac +done + +if [ $debugging = 1 ] +then + echo $dist_bin/run-mozilla.sh $script_args $dist_bin/$MOZILLA_BIN "$@" +fi +exec "$dist_bin/run-mozilla.sh" $script_args "$dist_bin/$MOZILLA_BIN" "$@" +# EOF. diff --git a/build/unix/print-depth-path.sh b/build/unix/print-depth-path.sh new file mode 100644 index 000000000..056cb4c61 --- /dev/null +++ b/build/unix/print-depth-path.sh @@ -0,0 +1,56 @@ +#!/bin/sh +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +# +# This script will print the depth path for a mozilla directory based +# on the info in Makefile +# +# It's a hack. It's brute force. It's horrible. +# It don't use Artificial Intelligence. It don't use Virtual Reality. +# It's not perl. It's not python. But it works. +# +# Usage: print-depth-path.sh +# +# Send comments, improvements, bugs to jim_nance@yahoo.com +# + +# Make sure a Makefile exists +if [ ! -f Makefile ] +then + echo + echo "There ain't no 'Makefile' over here: $pwd, dude." + echo + + exit +fi + +# awk can be quite primitave. Try enhanced versions first +for AWK in gawk nawk awk; do + if type $AWK 2>/dev/null 1>/dev/null; then + break; + fi +done + +$AWK -v PWD=`pwd` ' +{ + if($1 == "DEPTH") { + DEPTH=$0 + } +} + +END { + sub("^.*DEPTH.*=[ \t]*", "", DEPTH) + dlen = split(DEPTH, darray, "/") + plen = split(PWD, parray, "/") + + fsep="" + for(i=plen-dlen; i<=plen; i++) { + printf("%s%s", fsep, parray[i]) + fsep="/" + } + printf("\n") +}' Makefile + diff --git a/build/unix/print-failed-commands.sh b/build/unix/print-failed-commands.sh new file mode 100644 index 000000000..7f6b73d33 --- /dev/null +++ b/build/unix/print-failed-commands.sh @@ -0,0 +1,25 @@ +#!/bin/sh +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +# +# Usage from makefile: +# ELOG = . $(topdir)/build/autoconf/print-failed-commands.sh +# $(ELOG) $(CC) $CFLAGS -o $@ $< +# +# This shell script is used by the build system to print out commands that fail +# to execute properly. It is designed to make the "make -s" command more +# useful. +# +# Note that in the example we are sourcing rather than execing the script. +# Since make already started a shell for us, we might as well use it rather +# than starting a new one. + +( exec "$@" ) || { + echo + echo "In the directory " `pwd` + echo "The following command failed to execute properly:" + echo "$@" + exit 1; +} diff --git a/build/unix/print-non-newline.sh b/build/unix/print-non-newline.sh new file mode 100644 index 000000000..5e0cf292d --- /dev/null +++ b/build/unix/print-non-newline.sh @@ -0,0 +1,35 @@ +#!/bin/sh +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +# +# The purpose of this file is to find the files that do not end with a +# newline. Some compilers fail if the source files do not end with a +# newline. +# + +# +test_file=newline_test +test_dummy=newline_testee +inlist="$*" +broken_list= + +if test "$inlist" = ""; then + echo "Usage: $0 *.c *.cpp"; + exit 0; +fi + +echo "" > $test_file + +for f in $inlist; do + if test -f $f; then + tail -c 1 $f > $test_dummy + if ! `cmp -s $test_file $test_dummy`; then + broken_list="$broken_list $f" + fi + fi +done + +rm -f $test_file $test_dummy +echo $broken_list diff --git a/build/unix/run-gprof.sh b/build/unix/run-gprof.sh new file mode 100644 index 000000000..794d146b9 --- /dev/null +++ b/build/unix/run-gprof.sh @@ -0,0 +1,17 @@ +#!/bin/sh +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + + +LD_LIBRARY_PATH=. +export LD_LIBRARY_PATH + +PROG=mozilla-bin +PLIBS="" + +for l in *.so components/*.so; do + PLIBS="$PLIBS -incobj $l" +done + +$ECHO /bin/gprof -L. -Lcomponents -all $PLIBS $PROG $PROG.hiout diff --git a/build/unix/run-hiprof.sh b/build/unix/run-hiprof.sh new file mode 100644 index 000000000..cb4ec851f --- /dev/null +++ b/build/unix/run-hiprof.sh @@ -0,0 +1,25 @@ +#!/bin/sh +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + + +LD_LIBRARY_PATH=. +export LD_LIBRARY_PATH + +PROG=mozilla-bin +PLIBS="-L." + +TOOL=hiprof + +for l in ./*.so components/*.so; do + PLIBS="$PLIBS -incobj $l" +done + +$ECHO atom $PROG -tool $TOOL -env threads -toolargs="-calltime -systime" -all $PLIBS + +cd components && ( + for f in lib*.so; do + mv ../$f.$PROG.$TOOL.threads . + done +) diff --git a/build/unix/run-mozilla.sh b/build/unix/run-mozilla.sh new file mode 100644 index 000000000..c053e60b1 --- /dev/null +++ b/build/unix/run-mozilla.sh @@ -0,0 +1,362 @@ +#!/bin/sh +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +cmdname=`basename "$0"` +MOZ_DIST_BIN=`dirname "$0"` +MOZ_DEFAULT_NAME="./${cmdname}-bin" +MOZ_APPRUNNER_NAME="./mozilla-bin" +MOZ_PROGRAM="" + +exitcode=1 +# +## +## Functions +## +########################################################################## +moz_usage() +{ +echo "Usage: ${cmdname} [options] [program]" +echo "" +echo " options:" +echo "" +echo " -g Run in debugger." +echo " --debug" +echo "" +echo " -d debugger Debugger to use." +echo " --debugger debugger" +echo "" +echo " -a debugger_args Arguments passed to [debugger]." +echo " --debugger-args debugger_args" +echo "" +echo " Examples:" +echo "" +echo " Run the mozilla-bin binary" +echo "" +echo " ${cmdname} mozilla-bin" +echo "" +echo " Debug the mozilla-bin binary in gdb" +echo "" +echo " ${cmdname} -g mozilla-bin -d gdb" +echo "" +echo " Run mozilla-bin under valgrind with arguments" +echo "" +echo " ${cmdname} -g -d valgrind -a '--tool=memcheck --leak-check=full' mozilla-bin" +echo "" + return 0 +} +########################################################################## +moz_bail() +{ + message=$1 + echo + echo "$cmdname: $message" + echo + exit 1 +} +########################################################################## +moz_test_binary() +{ + binary=$1 + if [ -f "$binary" ] + then + if [ -x "$binary" ] + then + return 1 + fi + fi + return 0 +} +########################################################################## +moz_get_debugger() +{ + debuggers="ddd gdb dbx bdb native-gdb" + debugger="notfound" + done="no" + for d in $debuggers + do + moz_test_binary /bin/which + if [ $? -eq 1 ] + then + dpath=`which ${d}` + else + dpath=`LC_MESSAGES=C type ${d} | awk '{print $3;}' | sed -e 's/\.$//'` + fi + if [ -x "$dpath" ] + then + debugger=$dpath + break + fi + done + echo $debugger + return 0 +} +########################################################################## +moz_run_program() +{ + prog=$MOZ_PROGRAM + ## + ## Make sure the program is executable + ## + if [ ! -x "$prog" ] + then + moz_bail "Cannot execute $prog." + fi + ## + ## Run the program + ## + exec "$prog" ${1+"$@"} + exitcode=$? +} +########################################################################## +moz_debug_program() +{ + prog=$MOZ_PROGRAM + ## + ## Make sure the program is executable + ## + if [ ! -x "$prog" ] + then + moz_bail "Cannot execute $prog." + fi + if [ -n "$moz_debugger" ] + then + moz_test_binary /bin/which + if [ $? -eq 1 ] + then + debugger=`which $moz_debugger` + else + debugger=`LC_MESSAGES=C type $moz_debugger | awk '{print $3;}' | sed -e 's/\.$//'` + fi + else + debugger=`moz_get_debugger` + fi + if [ -x "$debugger" ] + then +# If you are not using ddd, gdb and know of a way to convey the arguments +# over to the prog then add that here- Gagan Saksena 03/15/00 + case `basename $debugger` in + native-gdb) echo "$debugger $moz_debugger_args --args $prog" ${1+"$@"} + exec "$debugger" $moz_debugger_args --args "$prog" ${1+"$@"} + exitcode=$? + ;; + gdb) echo "$debugger $moz_debugger_args --args $prog" ${1+"$@"} + exec "$debugger" $moz_debugger_args --args "$prog" ${1+"$@"} + exitcode=$? + ;; + ddd) echo "$debugger $moz_debugger_args --gdb -- --args $prog" ${1+"$@"} + exec "$debugger" $moz_debugger_args --gdb -- --args "$prog" ${1+"$@"} + exitcode=$? + ;; + *) echo "$debugger $moz_debugger_args $prog ${1+"$@"}" + exec $debugger $moz_debugger_args "$prog" ${1+"$@"} + exitcode=$? + ;; + esac + else + moz_bail "Could not find a debugger on your system." + fi +} +########################################################################## +## +## Command line arg defaults +## +moz_debug=0 +moz_debugger="" +moz_debugger_args="" +# +## +## Parse the command line +## +while [ $# -gt 0 ] +do + case $1 in + -g | --debug) + moz_debug=1 + shift + ;; + -d | --debugger) + moz_debugger=$2; + if [ "${moz_debugger}" != "" ]; then + shift 2 + else + echo "-d requires an argument" + exit 1 + fi + ;; + -a | --debugger-args) + moz_debugger_args=$2; + if [ "${moz_debugger_args}" != "" ]; then + shift 2 + else + echo "-a requires an argument" + exit 1 + fi + ;; + *) + break; + ;; + esac +done +# +## +## Program name given in $1 +## +if [ $# -gt 0 ] +then + MOZ_PROGRAM=$1 + shift +fi +## +## Program not given, try to guess a default +## +if [ -z "$MOZ_PROGRAM" ] +then + ## + ## Try this script's name with '-bin' appended + ## + if [ -x "$MOZ_DEFAULT_NAME" ] + then + MOZ_PROGRAM=$MOZ_DEFAULT_NAME + ## + ## Try mozilla-bin + ## + elif [ -x "$MOZ_APPRUNNER_NAME" ] + then + MOZ_PROGRAM=$MOZ_APPRUNNER_NAME + fi +fi +# +# +## +## Make sure the program is executable +## +if [ ! -x "$MOZ_PROGRAM" ] +then + moz_bail "Cannot execute $MOZ_PROGRAM." +fi +# +## +## Set MOZILLA_FIVE_HOME +## +MOZILLA_FIVE_HOME=$MOZ_DIST_BIN + +if [ -z "$MRE_HOME" ]; then + MRE_HOME=$MOZILLA_FIVE_HOME +fi +## +## Set LD_LIBRARY_PATH +## +## On Solaris we use $ORIGIN (set in RUNPATH) instead of LD_LIBRARY_PATH +## to locate shared libraries. +## +## When a shared library is a symbolic link, $ORIGIN will be replaced with +## the real path (i.e., what the symbolic link points to) by the runtime +## linker. For example, if dist/bin/libxul.so is a symbolic link to +## toolkit/library/libxul.so, $ORIGIN will be "toolkit/library" instead of "dist/bin". +## So the runtime linker will use "toolkit/library" NOT "dist/bin" to locate the +## other shared libraries that libxul.so depends on. This only happens +## when a user (developer) tries to start firefox, thunderbird, or seamonkey +## under dist/bin. To solve the problem, we should rely on LD_LIBRARY_PATH +## to locate shared libraries. +## +## Note: +## We test $MOZ_DIST_BIN/*.so. If any of them is a symbolic link, +## we need to set LD_LIBRARY_PATH. +########################################################################## +moz_should_set_ld_library_path() +{ + [ `uname -s` != "SunOS" ] && return 0 + for sharedlib in $MOZ_DIST_BIN/*.so + do + [ -h $sharedlib ] && return 0 + done + return 1 +} +if moz_should_set_ld_library_path +then + LD_LIBRARY_PATH=${MOZ_DIST_BIN}:${MOZ_DIST_BIN}/plugins:${MRE_HOME}${LD_LIBRARY_PATH:+":$LD_LIBRARY_PATH"} +fi + +if [ -n "$LD_LIBRARYN32_PATH" ] +then + LD_LIBRARYN32_PATH=${MOZ_DIST_BIN}:${MOZ_DIST_BIN}/plugins:${MRE_HOME}${LD_LIBRARYN32_PATH:+":$LD_LIBRARYN32_PATH"} +fi +if [ -n "$LD_LIBRARYN64_PATH" ] +then + LD_LIBRARYN64_PATH=${MOZ_DIST_BIN}:${MOZ_DIST_BIN}/plugins:${MRE_HOME}${LD_LIBRARYN64_PATH:+":$LD_LIBRARYN64_PATH"} +fi +if [ -n "$LD_LIBRARY_PATH_64" ]; then + LD_LIBRARY_PATH_64=${MOZ_DIST_BIN}:${MOZ_DIST_BIN}/plugins:${MRE_HOME}${LD_LIBRARY_PATH_64:+":$LD_LIBRARY_PATH_64"} +fi +# +# +## Set SHLIB_PATH for HPUX +SHLIB_PATH=${MOZ_DIST_BIN}:${MRE_HOME}${SHLIB_PATH:+":$SHLIB_PATH"} +# +## Set LIBPATH for AIX +LIBPATH=${MOZ_DIST_BIN}:${MRE_HOME}${LIBPATH:+":$LIBPATH"} +# +## Set DYLD_LIBRARY_PATH for Mac OS X (Darwin) +DYLD_LIBRARY_PATH=${MOZ_DIST_BIN}:${MRE_HOME}${DYLD_LIBRARY_PATH:+":$DYLD_LIBRARY_PATH"} +# +## Solaris Xserver(Xsun) tuning - use shared memory transport if available +if [ "$XSUNTRANSPORT" = "" ] +then + XSUNTRANSPORT="shmem" + XSUNSMESIZE="512" + export XSUNTRANSPORT XSUNSMESIZE +fi + +# Disable Gnome crash dialog +GNOME_DISABLE_CRASH_DIALOG=1 +export GNOME_DISABLE_CRASH_DIALOG + +if [ "$moz_debug" -eq 1 ] +then + echo "MOZILLA_FIVE_HOME=$MOZILLA_FIVE_HOME" + echo " LD_LIBRARY_PATH=$LD_LIBRARY_PATH" + if [ -n "$LD_LIBRARYN32_PATH" ] + then + echo "LD_LIBRARYN32_PATH=$LD_LIBRARYN32_PATH" + fi + if [ -n "$LD_LIBRARYN64_PATH" ] + then + echo "LD_LIBRARYN64_PATH=$LD_LIBRARYN64_PATH" + fi + if [ -n "$LD_LIBRARY_PATH_64" ]; then + echo "LD_LIBRARY_PATH_64=$LD_LIBRARY_PATH_64" + fi + if [ -n "$DISPLAY" ]; then + echo "DISPLAY=$DISPLAY" + fi + if [ -n "$FONTCONFIG_PATH" ]; then + echo "FONTCONFIG_PATH=$FONTCONFIG_PATH" + fi + if [ -n "$MOZILLA_POSTSCRIPT_PRINTER_LIST" ]; then + echo "MOZILLA_POSTSCRIPT_PRINTER_LIST=$MOZILLA_POSTSCRIPT_PRINTER_LIST" + fi + echo "DYLD_LIBRARY_PATH=$DYLD_LIBRARY_PATH" + echo " LIBRARY_PATH=$LIBRARY_PATH" + echo " SHLIB_PATH=$SHLIB_PATH" + echo " LIBPATH=$LIBPATH" + echo " ADDON_PATH=$ADDON_PATH" + echo " MOZ_PROGRAM=$MOZ_PROGRAM" + echo " MOZ_TOOLKIT=$MOZ_TOOLKIT" + echo " moz_debug=$moz_debug" + echo " moz_debugger=$moz_debugger" + echo "moz_debugger_args=$moz_debugger_args" +fi +# +export MOZILLA_FIVE_HOME LD_LIBRARY_PATH +export SHLIB_PATH LIBPATH LIBRARY_PATH ADDON_PATH DYLD_LIBRARY_PATH + +if [ $moz_debug -eq 1 ] +then + moz_debug_program ${1+"$@"} +else + moz_run_program ${1+"$@"} +fi + +exit $exitcode diff --git a/build/unix/run-third.sh b/build/unix/run-third.sh new file mode 100644 index 000000000..cb2c66137 --- /dev/null +++ b/build/unix/run-third.sh @@ -0,0 +1,25 @@ +#!/bin/sh +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + + +LD_LIBRARY_PATH=. +export LD_LIBRARY_PATH + +PROG=mozilla-bin +PLIBS="-L." + +TOOL=third + +for l in ./*.so components/*.so; do + PLIBS="$PLIBS -incobj $l" +done + +$ECHO atom $PROG -tool $TOOL -env threads -g -all $PLIBS -toolargs="-leaks all -before NS_ShutdownXPCOM" + +cd components && ( + for f in lib*.so; do + mv ../$f.$PROG.$TOOL.threads . + done +) diff --git a/build/unix/stdc++compat/Makefile.in b/build/unix/stdc++compat/Makefile.in new file mode 100644 index 000000000..755e2d61a --- /dev/null +++ b/build/unix/stdc++compat/Makefile.in @@ -0,0 +1,24 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at http://mozilla.org/MPL/2.0/. + +DEPTH = @DEPTH@ +topsrcdir = @top_srcdir@ +srcdir = @srcdir@ +VPATH = @srcdir@ + +include $(DEPTH)/config/autoconf.mk + +FORCE_STATIC_LIB= 1 +STL_FLAGS = +NO_EXPAND_LIBS = 1 +NO_PROFILE_GUIDED_OPTIMIZE = 1 + + $(NULL) + +HOST_CPPSRCS = $(CPPSRCS) + +include $(topsrcdir)/config/rules.mk + +CXXFLAGS += -DMOZ_LIBSTDCXX_VERSION=$(MOZ_LIBSTDCXX_TARGET_VERSION) +HOST_CXXFLAGS += -DMOZ_LIBSTDCXX_VERSION=$(MOZ_LIBSTDCXX_TARGET_VERSION) diff --git a/build/unix/stdc++compat/moz.build b/build/unix/stdc++compat/moz.build new file mode 100644 index 000000000..a33e690b0 --- /dev/null +++ b/build/unix/stdc++compat/moz.build @@ -0,0 +1,16 @@ +# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +MODULE = 'build' + +CPP_SOURCES += [ + 'stdc++compat.cpp', +] + +HOST_LIBRARY_NAME = 'host_stdc++compat' + +LIBRARY_NAME = 'stdc++compat' + diff --git a/build/unix/stdc++compat/stdc++compat.cpp b/build/unix/stdc++compat/stdc++compat.cpp new file mode 100644 index 000000000..cf1baafc0 --- /dev/null +++ b/build/unix/stdc++compat/stdc++compat.cpp @@ -0,0 +1,133 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include <ostream> +#include <istream> +#include <string> + +/* GLIBCXX_3.4.8 is from gcc 4.1.1 (111691) + GLIBCXX_3.4.9 is from gcc 4.2.0 (111690) + GLIBCXX_3.4.10 is from gcc 4.3.0 (126287) + GLIBCXX_3.4.11 is from gcc 4.4.0 (133006) + GLIBCXX_3.4.12 is from gcc 4.4.1 (147138) + GLIBCXX_3.4.13 is from gcc 4.4.2 (151127) + GLIBCXX_3.4.14 is from gcc 4.5.0 (151126) + GLIBCXX_3.4.15 is from gcc 4.6.0 (160071) + GLIBCXX_3.4.16 is form gcc 4.6.1 (172240) */ + +#define GLIBCXX_VERSION(a, b, c) (((a) << 16) | ((b) << 8) | (c)) + +namespace std { +#if MOZ_LIBSTDCXX_VERSION >= GLIBCXX_VERSION(3, 4, 9) + /* Instantiate these templates to avoid GLIBCXX_3.4.9 symbol versions */ + template ostream& ostream::_M_insert(double); + template ostream& ostream::_M_insert(long); + template ostream& ostream::_M_insert(unsigned long); + template ostream& ostream::_M_insert(long long); + template ostream& ostream::_M_insert(unsigned long long); + template ostream& ostream::_M_insert(bool); + template ostream& ostream::_M_insert(const void*); + template ostream& __ostream_insert(ostream&, const char*, streamsize); + template istream& istream::_M_extract(double&); + template istream& istream::_M_extract(float&); + template istream& istream::_M_extract(unsigned int&); + template istream& istream::_M_extract(unsigned long&); + template istream& istream::_M_extract(unsigned short&); + template istream& istream::_M_extract(unsigned long long&); +#endif +#if MOZ_LIBSTDCXX_VERSION >= GLIBCXX_VERSION(3, 4, 14) + /* Instantiate these templates to avoid GLIBCXX_3.4.14 symbol versions + * depending on optimization level */ + template char *string::_S_construct_aux_2(size_type, char, allocator<char> const&); +#ifdef _GLIBCXX_USE_WCHAR_T + template wchar_t *wstring::_S_construct_aux_2(size_type, wchar_t, allocator<wchar_t> const&); +#endif /* _GLIBCXX_USE_WCHAR_T */ +#ifdef __GXX_EXPERIMENTAL_CXX0X__ + template string::basic_string(string&&); + template string& string::operator=(string&&); + template wstring::basic_string(wstring&&); + template wstring& wstring::operator=(wstring&&); + template string& string::assign(string&&); + template wstring& wstring::assign(wstring&&); +#endif /* __GXX_EXPERIMENTAL_CXX0X__ */ +#endif /* (__GNUC__ == 4) && (__GNUC_MINOR__ >= 5) */ +} + +namespace std __attribute__((visibility("default"))) { +#if MOZ_LIBSTDCXX_VERSION >= GLIBCXX_VERSION(3, 4, 14) + /* Hack to avoid GLIBCXX_3.4.14 symbol versions */ + struct _List_node_base + { + void hook(_List_node_base * const __position) throw (); + + void unhook() throw (); + + void transfer(_List_node_base * const __first, + _List_node_base * const __last) throw(); + +/* Hack to avoid GLIBCXX_3.4.15 symbol versions */ +#if MOZ_LIBSTDCXX_VERSION >= GLIBCXX_VERSION(3, 4, 15) + static void swap(_List_node_base& __x, _List_node_base& __y) throw (); + }; + + namespace __detail { + + struct _List_node_base + { +#endif + void _M_hook(_List_node_base * const __position) throw (); + + void _M_unhook() throw (); + + void _M_transfer(_List_node_base * const __first, + _List_node_base * const __last) throw(); + +#if MOZ_LIBSTDCXX_VERSION >= GLIBCXX_VERSION(3, 4, 15) + static void swap(_List_node_base& __x, _List_node_base& __y) throw (); +#endif + }; + + /* The functions actually have the same implementation */ + void + _List_node_base::_M_hook(_List_node_base * const __position) throw () + { + ((std::_List_node_base *)this)->hook((std::_List_node_base * const) __position); + } + + void + _List_node_base::_M_unhook() throw () + { + ((std::_List_node_base *)this)->unhook(); + } + + void + _List_node_base::_M_transfer(_List_node_base * const __first, + _List_node_base * const __last) throw () + { + ((std::_List_node_base *)this)->transfer((std::_List_node_base * const)__first, + (std::_List_node_base * const)__last); + } + +#if MOZ_LIBSTDCXX_VERSION >= GLIBCXX_VERSION(3, 4, 15) + void + _List_node_base::swap(_List_node_base& __x, _List_node_base& __y) throw () + { + std::_List_node_base::swap(*((std::_List_node_base *) &__x), + *((std::_List_node_base *) &__y)); + } +} +#endif + +#endif /*MOZ_LIBSTDCXX_VERSION >= GLIBCXX_VERSION(3, 4, 14)*/ + +#if MOZ_LIBSTDCXX_VERSION >= GLIBCXX_VERSION(3, 4, 11) + /* Hack to avoid GLIBCXX_3.4.11 symbol versions + An inline definition of ctype<char>::_M_widen_init() used to be in + locale_facets.h before GCC 4.4, but moved out of headers in more + recent versions. + It is actually safe to make it do nothing. */ + void ctype<char>::_M_widen_init() const {} +#endif + +} diff --git a/build/unix/test/Makefile.in b/build/unix/test/Makefile.in new file mode 100644 index 000000000..237fab8fb --- /dev/null +++ b/build/unix/test/Makefile.in @@ -0,0 +1,44 @@ +# -*- makefile -*- +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +DEPTH = @DEPTH@ +topsrcdir = @top_srcdir@ +srcdir = @srcdir@ +VPATH = @srcdir@ + +include $(DEPTH)/config/autoconf.mk +include $(topsrcdir)/config/rules.mk + +################################################## +## Gather a list of tests, generate timestamp deps +################################################## +TS=.ts +ifneq (,$(findstring check,$(MAKECMDGOALS))) + allsrc = $(wildcard $(srcdir)/*) + tests2run = $(notdir $(filter %.tpl,$(allsrc))) + check_targets += $(addprefix $(TS)/,$(tests2run)) +endif + +check:: $(TS) $(check_targets) + +############################################# +# Only invoke tests when sources have changed +############################################# +$(TS)/%: $(srcdir)/% + $(PERL) $(srcdir)/runtest $< + @touch $@ + +##################################################### +## Extra dep needed to synchronize parallel execution +##################################################### +$(TS): $(TS)/.done +$(TS)/.done: + $(MKDIR) -p $(dir $@) + touch $@ + +GARBAGE_DIRS += $(TS) + +# EOF diff --git a/build/unix/test/moz.build b/build/unix/test/moz.build new file mode 100644 index 000000000..895d11993 --- /dev/null +++ b/build/unix/test/moz.build @@ -0,0 +1,6 @@ +# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + diff --git a/build/unix/test/runtest b/build/unix/test/runtest new file mode 100644 index 000000000..3a53b0682 --- /dev/null +++ b/build/unix/test/runtest @@ -0,0 +1,95 @@ +#!/usr/bin/env perl +########################################################################### +## Intent: +## Test::Harness is a testing wrapper that will process output +## from Test.pm module tests. Sumarize results, report stats +## and exit with overall status for the testing suites. +## +## Run testing suite: +## % make clean test +## % perl runtest +## +## Run Individual tests +## % perl tUtils0 +########################################################################### + +##----------------------------## +##---] CORE/CPAN INCLUDES [---## +##----------------------------## +use strict; +use warnings; +use Getopt::Long; + +use Test::Harness; + +##-------------------## +##---] EXPORTS [---## +##-------------------## +our $VERSION = qw(1.0); +use FindBin; + +##-------------------## +##---] GLOBALS [---## +##-------------------## +my %argv; + +##----------------## +##---] MAIN [---## +##----------------## +unless(GetOptions(\%argv, + qw(debug|d) + )) +{ + print "Usage: $0\n"; + print " --debug Enable debug mode\n"; + exit 1; +} + +if (2 > $Test::Harness::VERSION) +{ + print "Unit tests will not be run, Test::Harness is too old\n" + if ($argv{debug}); + exit 0; +} + + +my @tests; + +######################################## +## Gather a list of tests if none passed +######################################## +unless (@tests = @ARGV) +{ + local *D; + opendir(D, '.'); + while($_ = readdir(D)) { + next unless /.t\S+$/; + next if (/\.ts$/); + push(@tests, $_); + } + closedir(D); +} + +############################################### +## Glob a list of tests when directories passed +############################################### +my @tmp; +foreach (@tests) +{ + local *D; + if (-d $_ && (my $dir = $_)) + { + opendir(D, $_) || die "opendir(D) failed: $!"; + my @tests = grep(/\.t[^\.\s]+/o, readdir(D)); + closedir(D); + push(@tmp, map{ join('/', $dir, $_); } @tests); + } else { + push(@tmp, $_); + } +} +@tests = @tmp; + +print "$0: @ARGV\n" if ($argv{debug}); +runtests(@tests); + +# EOF diff --git a/build/unix/test/uniq.tpl b/build/unix/test/uniq.tpl new file mode 100644 index 000000000..ae692ab5c --- /dev/null +++ b/build/unix/test/uniq.tpl @@ -0,0 +1,151 @@ +#!/usr/bin/env perl +########################################################################### +## Intent: Unit test to verify uniq.pl +########################################################################### + +##----------------------------## +##---] CORE/CPAN INCLUDES [---## +##----------------------------## +use strict; +use warnings; +use Cwd; +use Getopt::Long; # GetOptions + +use Test; +sub BEGIN { plan tests => 12 } + +##-------------------## +##---] EXPORTS [---## +##-------------------## +our $VERSION = qw(1.0); + +##------------------## +##---] INCLUDES [---## +##------------------## +use FindBin; + +##-------------------## +##---] GLOBALS [---## +##-------------------## +my %argv; + + +########################################################################### +## Intent: Run the arch command for output +## +## Returns: +## 0 on success +## $? command shell exit status +########################################################################### +sub uniq_pl +{ + my $cmd = "perl $FindBin::RealBin/../uniq.pl @_"; + print "Running: $cmd\n" if ($argv{debug}); + my @tmp = `$cmd 2>&1`; + my @output = map{ split(/\s+/o); } @tmp; + wantarray ? @output : "@output"; +} # uniq_pl + +########################################################################### +## Intent: +## +## Returns: +## 0 on success +########################################################################### +sub check_uniq +{ + print STDERR "Running test: check_uniq\n" if ($argv{debug}); + + # TODO: improve test, uniq.pl regexpr handling not quite right + + my @todo = + ( + [ '', qw(a a/b a/b/c) ] => [ qw(a a/b a/b/c) ], + [ '', qw(a/b a a/b/c) ] => [ qw(a/b a a/b/c) ], + [ '', qw(a/b/c a/b a) ] => [ qw(a/b/c a/b a) ], + + [ '', qw(a a/b a/b/c a/b a) ] => [ qw(a a/b a/b/c) ], # dup removal + + [ '-s', qw(a a/b a/b/c) ] => [ qw(a a/b a/b/c) ], + [ '-s', qw(a/b a a/b/c) ] => [ qw(a a/b a/b/c) ], + [ '-s', qw(a/b/c a/b a) ] => [ qw(a a/b a/b/c) ], + + [ '-r', qw(a a/b a/b/c) ] => [ qw(a) ], + [ '-r', qw(a/b a a/b/c) ] => [ qw(a/b a) ], + [ '-r', qw(a/b/c a/b a) ] => [ qw(a/b/c a/b a) ], + + [ '-r', qw(. .. a/b ../a aa/bb) ] => [ qw(. .. a/b aa/bb) ], + [ '-r', qw(.. a/b ../a . aa/bb) ] => [ qw(.. a/b . aa/bb) ], + ); + + my $ct=1; + while (@todo) + { + my ($a, $b) = splice(@todo, 0, 2); + my @args = @{ $a }; + my @exp = @{ $b }; + + my @out = uniq_pl(@args); +# compareExp(\@out, \@exp, 'Failed on line ' . __LINE__ . ", dataset $ct"); + if (0 && 7 == $ct) + { + print STDERR "\n"; + print STDERR map{ "args> $_\n" }@args; + print STDERR "\n"; + print STDERR map{ "exp> $_\n" }@exp; + print STDERR "\n"; + print STDERR map{ "out> $_\n" }@out; + } + + ok("@out", "@exp", 'Failed on line ' . __LINE__ . ", dataset $ct"); + $ct++; + } + +} # check_uniq + +########################################################################### +## Intent: Smoke tests for the unittests module +########################################################################### +sub smoke +{ + print STDERR "Running test: smoke()\n" if ($argv{debug}); +} # smoke() + +########################################################################### +## Intent: Intitialize global test objects and consts +########################################################################### +sub init +{ + print "Running: init()\n" if ($argv{debug}); +# testplan(24, 0); +} # init() + +##----------------## +##---] MAIN [---## +##----------------## +unless(GetOptions(\%argv, + qw( + debug|d + manual + test=s@ + verbose + ))) +{ + print "USAGE: $0\n"; + print " --debug Enable script debug mode\n"; + print " --fail Force a testing failure condition\n"; + print " --manual Also run disabled tests\n"; + print " --smoke Run smoke tests then exit\n"; + print " --test Run a list of tests by function name\n"; + print " --verbose Enable script verbose mode\n"; + exit 1; +} + +init(); +testbyname(@{ $argv{test} }) if ($argv{test}); +smoke(); + +check_uniq(); +ok(1, 0, 'Forced failure by command line arg --fail') if ($argv{fail}); + +# EOF diff --git a/build/unix/uniq.pl b/build/unix/uniq.pl new file mode 100644 index 000000000..301240e03 --- /dev/null +++ b/build/unix/uniq.pl @@ -0,0 +1,91 @@ +#!/usr/bin/env perl + +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +##----------------------------## +##---] CORE/CPAN INCLUDES [---## +##----------------------------## +use strict; +use warnings; +use Getopt::Long; + +##-------------------## +##---] EXPORTS [---## +##-------------------## +our $VERSION = qw(1.1); + +##-------------------## +##---] GLOBALS [---## +##-------------------## +my %argv; +my $modver = $Getopt::Long::VERSION || 0; +my $isOldGetopt = ($modver eq '2.25') ? 1 : 0; + +########################################################################### +## Intent: Script init function +########################################################################### +sub init +{ + if ($isOldGetopt) + { + # mozilla.build/mingw perl in need of an upgrade + # emulate Getopt::Long switch|short:init + foreach (qw(debug regex sort)) + { + if (defined($argv{$_})) + { + $argv{$_} ||= 1; + } + } + } +} # init + +##----------------## +##---] MAIN [---## +##----------------## +my @args = ($isOldGetopt) + ? qw(debug|d regex|r sort|s) + : qw(debug|d:1 regex|r:1 sort|s:1) + ; + +unless(GetOptions(\%argv, @args)) +{ + print "Usage: $0\n"; + print " --sort Sort list elements early\n"; + print " --regex Exclude subdirs by pattern\n"; +} + +init(); +my $debug = $argv{debug} || 0; + +my %seen; +my @out; +my @in = ($argv{sort}) ? sort @ARGV : @ARGV; + +foreach my $d (@in) +{ + next if ($seen{$d}++); + + print " arg is $d\n" if ($debug); + + if ($argv{regex}) + { + my $found = 0; + foreach my $dir (@out) + { + my $dirM = quotemeta($dir); + $found++, last if ($d eq $dir || $d =~ m!^${dirM}\/!); + } + print "Adding $d\n" if ($debug && !$found); + push @out, $d if (!$found); + } else { + print "Adding: $d\n" if ($debug); + push(@out, $d); + } +} + +print "@out\n" + +# EOF diff --git a/build/update-settings.ini b/build/update-settings.ini new file mode 100644 index 000000000..9bbd40aca --- /dev/null +++ b/build/update-settings.ini @@ -0,0 +1,11 @@ +#if 0 +; This Source Code Form is subject to the terms of the Mozilla Public +; License, v. 2.0. If a copy of the MPL was not distributed with this +; file, You can obtain one at http://mozilla.org/MPL/2.0/. +#endif +; If you modify this file updates may fail. +; Do not modify this file. + +#filter substitution +[Settings] +ACCEPTED_MAR_CHANNEL_IDS=@ACCEPTED_MAR_CHANNEL_IDS@ diff --git a/build/upload.py b/build/upload.py new file mode 100644 index 000000000..9dae5d9be --- /dev/null +++ b/build/upload.py @@ -0,0 +1,193 @@ +#!/usr/bin/python +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# +# When run directly, this script expects the following environment variables +# to be set: +# UPLOAD_HOST : host to upload files to +# UPLOAD_USER : username on that host +# UPLOAD_PATH : path on that host to put the files in +# +# And will use the following optional environment variables if set: +# UPLOAD_SSH_KEY : path to a ssh private key to use +# UPLOAD_PORT : port to use for ssh +# POST_UPLOAD_CMD: a commandline to run on the remote host after uploading. +# UPLOAD_PATH and the full paths of all files uploaded will +# be appended to the commandline. +# +# All files to be uploaded should be passed as commandline arguments to this +# script. The script takes one other parameter, --base-path, which you can use +# to indicate that files should be uploaded including their paths relative +# to the base path. + +import sys, os +from optparse import OptionParser +from subprocess import PIPE, Popen, check_call + +def RequireEnvironmentVariable(v): + """Return the value of the environment variable named v, or print + an error and exit if it's unset (or empty).""" + if not v in os.environ or os.environ[v] == "": + print "Error: required environment variable %s not set" % v + sys.exit(1) + return os.environ[v] + +def OptionalEnvironmentVariable(v): + """Return the value of the environment variable named v, or None + if it's unset (or empty).""" + if v in os.environ and os.environ[v] != "": + return os.environ[v] + return None + +def FixupMsysPath(path): + """MSYS helpfully translates absolute pathnames in environment variables + and commandline arguments into Windows native paths. This sucks if you're + trying to pass an absolute path on a remote server. This function attempts + to un-mangle such paths.""" + if 'OSTYPE' in os.environ and os.environ['OSTYPE'] == 'msys': + # sort of awful, find out where our shell is (should be in msys/bin) + # and strip the first part of that path out of the other path + if 'SHELL' in os.environ: + sh = os.environ['SHELL'] + msys = sh[:sh.find('/bin')] + if path.startswith(msys): + path = path[len(msys):] + return path + +def WindowsPathToMsysPath(path): + """Translate a Windows pathname to an MSYS pathname. + Necessary because we call out to ssh/scp, which are MSYS binaries + and expect MSYS paths.""" + if sys.platform != 'win32': + return path + (drive, path) = os.path.splitdrive(os.path.abspath(path)) + return "/" + drive[0] + path.replace('\\','/') + +def AppendOptionalArgsToSSHCommandline(cmdline, port, ssh_key): + """Given optional port and ssh key values, append valid OpenSSH + commandline arguments to the list cmdline if the values are not None.""" + if port is not None: + cmdline.append("-P%d" % port) + if ssh_key is not None: + # Don't interpret ~ paths - ssh can handle that on its own + if not ssh_key.startswith('~'): + ssh_key = WindowsPathToMsysPath(ssh_key) + cmdline.extend(["-o", "IdentityFile=%s" % ssh_key]) + +def DoSSHCommand(command, user, host, port=None, ssh_key=None): + """Execute command on user@host using ssh. Optionally use + port and ssh_key, if provided.""" + cmdline = ["ssh"] + AppendOptionalArgsToSSHCommandline(cmdline, port, ssh_key) + cmdline.extend(["%s@%s" % (user, host), command]) + cmd = Popen(cmdline, stdout=PIPE) + retcode = cmd.wait() + if retcode != 0: + raise Exception("Command %s returned non-zero exit code: %i" % \ + (cmdline, retcode)) + return cmd.stdout.read().strip() + +def DoSCPFile(file, remote_path, user, host, port=None, ssh_key=None): + """Upload file to user@host:remote_path using scp. Optionally use + port and ssh_key, if provided.""" + cmdline = ["scp"] + AppendOptionalArgsToSSHCommandline(cmdline, port, ssh_key) + cmdline.extend([WindowsPathToMsysPath(file), + "%s@%s:%s" % (user, host, remote_path)]) + check_call(cmdline) + +def GetRemotePath(path, local_file, base_path): + """Given a remote path to upload to, a full path to a local file, and an + optional full path that is a base path of the local file, construct the + full remote path to place the file in. If base_path is not None, include + the relative path from base_path to file.""" + if base_path is None or not local_file.startswith(base_path): + return path + dir = os.path.dirname(local_file) + # strip base_path + extra slash and make it unixy + dir = dir[len(base_path)+1:].replace('\\','/') + return path + dir + +def UploadFiles(user, host, path, files, verbose=False, port=None, ssh_key=None, base_path=None, upload_to_temp_dir=False, post_upload_command=None): + """Upload each file in the list files to user@host:path. Optionally pass + port and ssh_key to the ssh commands. If base_path is not None, upload + files including their path relative to base_path. If upload_to_temp_dir is + True files will be uploaded to a temporary directory on the remote server. + Generally, you should have a post upload command specified in these cases + that can move them around to their correct location(s). + If post_upload_command is not None, execute that command on the remote host + after uploading all files, passing it the upload path, and the full paths to + all files uploaded. + If verbose is True, print status updates while working.""" + if upload_to_temp_dir: + path = DoSSHCommand("mktemp -d", user, host, port=port, ssh_key=ssh_key) + if not path.endswith("/"): + path += "/" + if base_path is not None: + base_path = os.path.abspath(base_path) + remote_files = [] + try: + for file in files: + file = os.path.abspath(file) + if not os.path.isfile(file): + raise IOError("File not found: %s" % file) + # first ensure that path exists remotely + remote_path = GetRemotePath(path, file, base_path) + DoSSHCommand("mkdir -p " + remote_path, user, host, port=port, ssh_key=ssh_key) + if verbose: + print "Uploading " + file + DoSCPFile(file, remote_path, user, host, port=port, ssh_key=ssh_key) + remote_files.append(remote_path + '/' + os.path.basename(file)) + if post_upload_command is not None: + if verbose: + print "Running post-upload command: " + post_upload_command + file_list = '"' + '" "'.join(remote_files) + '"' + DoSSHCommand('%s "%s" %s' % (post_upload_command, path, file_list), user, host, port=port, ssh_key=ssh_key) + finally: + if upload_to_temp_dir: + DoSSHCommand("rm -rf %s" % path, user, host, port=port, + ssh_key=ssh_key) + if verbose: + print "Upload complete" + +if __name__ == '__main__': + host = RequireEnvironmentVariable('UPLOAD_HOST') + user = RequireEnvironmentVariable('UPLOAD_USER') + path = OptionalEnvironmentVariable('UPLOAD_PATH') + upload_to_temp_dir = OptionalEnvironmentVariable('UPLOAD_TO_TEMP') + port = OptionalEnvironmentVariable('UPLOAD_PORT') + if port is not None: + port = int(port) + key = OptionalEnvironmentVariable('UPLOAD_SSH_KEY') + post_upload_command = OptionalEnvironmentVariable('POST_UPLOAD_CMD') + if (not path and not upload_to_temp_dir) or (path and upload_to_temp_dir): + print "One (and only one of UPLOAD_PATH or UPLOAD_TO_TEMP must be " + \ + "defined." + sys.exit(1) + if sys.platform == 'win32': + if path is not None: + path = FixupMsysPath(path) + if post_upload_command is not None: + post_upload_command = FixupMsysPath(post_upload_command) + + parser = OptionParser(usage="usage: %prog [options] <files>") + parser.add_option("-b", "--base-path", + action="store", dest="base_path", + help="Preserve file paths relative to this path when uploading. If unset, all files will be uploaded directly to UPLOAD_PATH.") + (options, args) = parser.parse_args() + if len(args) < 1: + print "You must specify at least one file to upload" + sys.exit(1) + try: + UploadFiles(user, host, path, args, base_path=options.base_path, + port=port, ssh_key=key, upload_to_temp_dir=upload_to_temp_dir, + post_upload_command=post_upload_command, + verbose=True) + except IOError, (strerror): + print strerror + sys.exit(1) + except Exception, (err): + print err + sys.exit(2) diff --git a/build/valgrind/cross-architecture.sup b/build/valgrind/cross-architecture.sup new file mode 100644 index 000000000..a02221db9 --- /dev/null +++ b/build/valgrind/cross-architecture.sup @@ -0,0 +1,307 @@ +# Full list is tracked through meta bug 793882 + +#################### +# Intended leaks # +#################### + +{ + PR_SetEnv requires its argument to be leaked, but does not appear on stacks. (See bug 793534 and see bug 793549.) + Memcheck:Leak + ... + fun:_ZL9SaveToEnvPKc + ... +} +{ + PR_SetEnv requires its argument to be leaked, but does not appear on stacks. (See bug 793534 and see bug 793549.) + Memcheck:Leak + ... + fun:_ZL13SaveWordToEnvPKcRK19nsACString_internal + ... +} +{ + See bug 793535 + Memcheck:Leak + fun:calloc + fun:_dlerror_run + fun:dlsym + ... +} +{ + See bug 793611 + Memcheck:Leak + fun:memalign + fun:tls_get_addr_tail + fun:__tls_get_addr + ... +} +{ + See bug 793611 + Memcheck:Leak + fun:memalign + fun:tls_get_addr_tail + fun:___tls_get_addr + ... +} + +################# +# Other leaks # +################# + +{ + Bug 793532 + Memcheck:Leak + fun:malloc + fun:_ZN8JSObject25allocateSlowArrayElementsEP9JSContext + ... +} +{ + Bug 793533 (all 64-bit systems) + Memcheck:Leak + fun:malloc + fun:moz_xmalloc + fun:_ZN3xpc18CreateGlobalObjectEP9JSContextP7JSClassP12nsIPrincipalbPP8JSObjectPP13JSCompartment + ... +} +{ + Bug 793533 (all 64-bit systems) + Memcheck:Leak + fun:malloc + fun:moz_xmalloc + fun:_ZN3xpc18CreateGlobalObjectEP9JSContextP7JSClassP12nsIPrincipalm + ... +} +{ + Bug 793536 (all 64-bit systems) + Memcheck:Leak + fun:malloc + fun:moz_xmalloc + fun:_ZNSt11_Deque_baseIN11MessageLoop11PendingTaskESaIS1_EE17_M_initialize_mapEm + ... +} +{ + Bug 793536 (all 32-bit systems) + Memcheck:Leak + fun:malloc + fun:moz_xmalloc + fun:_ZNSt11_Deque_baseIN11MessageLoop11PendingTaskESaIS1_EE17_M_initialize_mapEj + ... +} +{ + Bug 793548 + Memcheck:Leak + fun:malloc + ... + fun:_Z12ToNewCStringRK19nsACString_internal + fun:_ZN13CrashReporter14SetupExtraDataEP7nsIFileRK19nsACString_internal + ... +} +{ + Bug 793608 + Memcheck:Leak + ... + fun:gaih_inet + fun:getaddrinfo + fun:PR_GetAddrInfoByName + fun:_ZN14nsHostResolver10ThreadFuncEPv + fun:_pt_root + fun:start_thread + fun:clone +} +{ + Bug 793608 variant + Memcheck:Leak + fun:malloc + fun:__libc_res_nsend + fun:__libc_res_nquery + fun:__libc_res_nquerydomain + fun:__libc_res_nsearch + obj:* + ... +} +{ + Bug 793608 variant 2 + Memcheck:Leak + fun:malloc + fun:make_request + fun:__check_pf + fun:getaddrinfo + fun:PR_GetAddrInfoByName + fun:_ZN14nsHostResolver10ThreadFuncEPv + fun:_pt_root + fun:start_thread + fun:clone +} +{ + Bug 793615 + Memcheck:Leak + fun:malloc + fun:sqlite3MemMalloc + ... +} +{ + Bug 794350 + Memcheck:Leak + fun:malloc + fun:moz_xmalloc + fun:_ZN11MessageLoopC1ENS_4TypeE + fun:_ZN4base6Thread10ThreadMainEv + fun:_ZL10ThreadFuncPv + fun:start_thread + fun:clone +} +{ + Bug 794354 + Memcheck:Leak + ... + fun:_ZN7mozilla12safebrowsing10Classifier12ApplyUpdatesEP8nsTArrayIPNS0_11TableUpdateE24nsTArrayDefaultAllocatorE + fun:_ZN30nsUrlClassifierDBServiceWorker11ApplyUpdateEv + fun:_ZN30nsUrlClassifierDBServiceWorker12FinishUpdateEv + ... +} +{ + Bug 794358 + Memcheck:Leak + fun:malloc + ... + fun:PK11_InitPin + fun:_ZN11nsPK11Token12InitPasswordEPKt + ... +} +{ + Bug 794369 + Memcheck:Leak + fun:malloc + fun:moz_xmalloc + fun:_ZN20mozJSComponentLoader10LoadModuleERN7mozilla12FileLocationE + ... +} +{ + Bug 794370 + Memcheck:Leak + fun:malloc + fun:moz_xmalloc + fun:_ZN22nsComponentManagerImpl15RegisterFactoryERK4nsIDPKcS4_P10nsIFactory + ... +} +{ + Bug 794372 + Memcheck:Leak + fun:malloc + fun:moz_xmalloc + fun:_ZN22nsComponentManagerImpl16RegisterCIDEntryEPKN7mozilla6Module8CIDEntryEPNS_11KnownModuleE + ... +} +{ + Bug 794372 + Memcheck:Leak + fun:malloc + fun:moz_xmalloc + fun:_ZN22nsComponentManagerImpl22RegisterCIDEntryLockedEPKN7mozilla6Module8CIDEntryEPNS_11KnownModuleE + ... +} +{ + Bug 794374 + Memcheck:Leak + fun:malloc + fun:moz_xmalloc + fun:_ZN22nsComponentManagerImpl17ManifestComponentERNS_25ManifestProcessingContextEiPKPc + ... +} +{ + Bug 803386 + Memcheck:Free + fun:_ZdlPv + fun:_ZN16SkMallocPixelRefD0Ev + fun:_ZNK8SkRefCnt16internal_disposeEv + fun:_ZN8SkBitmap10freePixelsEv + fun:_ZN8SkBitmapD1Ev + fun:_ZN7mozilla3gfx5ScaleEPhiiiS1_iiiNS0_13SurfaceFormatE + ... +} +{ + Bug 812421 (all 32-bit systems) + Memcheck:Leak + fun:malloc + fun:_ZN3JSC19ExecutableAllocator10createPoolEj + fun:_ZN3JSC19ExecutableAllocator11poolForSizeEj + ... +} +{ + Bug 812421 (all 64-bit systems) + Memcheck:Leak + fun:malloc + fun:_ZN3JSC19ExecutableAllocator10createPoolEm + fun:_ZN3JSC19ExecutableAllocator11poolForSizeEm + ... +} +{ + Bug 812422 + Memcheck:Leak + fun:malloc + fun:moz_xmalloc + fun:_Z22NS_NewComputedDOMStylePN7mozilla3dom7ElementERK18nsAString_internalP12nsIPresShellN18nsComputedDOMStyle9StyleTypeE + fun:_ZN14nsGlobalWindow22GetComputedStyleHelperEP13nsIDOMElementRK18nsAString_internalbPP25nsIDOMCSSStyleDeclaration + ... +} +{ + Bug 812423 + Memcheck:Leak + fun:malloc + fun:_ZN2js15ArgumentsObject6createI18CopyStackFrameArgsEEPS0_P9JSContextN2JS6HandleIP8JSScriptEENS7_IP10JSFunctionEEjRT_ + fun:_ZN2js15ArgumentsObject14createExpectedEP9JSContextPNS_10StackFrameE + ... +} +{ + Bug 812423 + Memcheck:Leak + fun:malloc + fun:_ZN2js15ArgumentsObject6createI13CopyFrameArgsEEPS0_P9JSContextN2JS6HandleIP8JSScriptEENS7_IP10JSFunctionEEjRT_ + fun:_ZN2js15ArgumentsObject14createExpectedEP9JSContextNS_16AbstractFramePtrE + ... +} +{ + Bug 823782 + Memcheck:Leak + fun:malloc + ... + fun:_ZN2js6ctypes7LibraryL7DeclareEP9JSContextjPN2JS5ValueE + ... +} +{ + Bug 823782 + Memcheck:Leak + fun:malloc + fun:_ZN2js6ctypes5CData6CreateEP9JSContextN2JS6HandleIP8JSObjectEES8_Pvb + ... +} +{ + Bug 823782 + Memcheck:Leak + fun:malloc + ... + fun:_ZN2js6ctypes10StructTypeL6CreateEP9JSContextjPN2JS5ValueE + ... +} +{ + Bug 823782 + Memcheck:Leak + fun:malloc + fun:_ZN2js6ctypes9Int64Base9ConstructEP9JSContextN2JS6HandleIP8JSObjectEEmb + ... +} +{ + Bug 824323 + Memcheck:Leak + fun:malloc + ... + fun:_ZN7mozilla3dom7workers13WorkerPrivate9DoRunLoopEP9JSContext + ... +} +{ + Bug 874658 + Memcheck:Leak + fun:malloc + ... + fun:_Z31mozilla_sampler_register_threadPKc + ... +} diff --git a/build/valgrind/i386-redhat-linux-gnu.sup b/build/valgrind/i386-redhat-linux-gnu.sup new file mode 100644 index 000000000..414afc6c5 --- /dev/null +++ b/build/valgrind/i386-redhat-linux-gnu.sup @@ -0,0 +1,60 @@ +# Full list is tracked through meta bug 793882 + +#################################### +# Leaks in third party libraries # +#################################### + +{ + Bug 793537 + Memcheck:Leak + ... + obj:/usr/lib/libpango-1.0.so.0.2800.1 + ... +} +{ + Bug 793598 + Memcheck:Leak + ... + obj:/lib/libdbus-1.so.3.4.0 + ... +} +{ + Bug 793600 + Memcheck:Leak + fun:realloc + obj:/usr/lib/libfontconfig.so.1.4.4 + ... + fun:FcDefaultSubstitute + fun:_ZN17gfxPangoFontGroup11MakeFontSetEP14_PangoLanguagedP9nsAutoRefI10_FcPatternE + ... +} +{ + Bug 794366 + Memcheck:Leak + ... + obj:/usr/lib/libgtk-x11-2.0.so.0.1800.9 + ... +} +{ + Bug 794368 + Memcheck:Leak + ... + obj:/usr/lib/libXrandr.so.2.2.0 + ... +} +{ + Bug 794373 + Memcheck:Leak + ... + obj:/lib/libgobject-2.0.so.0.2200.5 + ... +} +{ + Bug 850134 + Memcheck:Leak + fun:realloc + fun:_ZN2js6VectorIyLj0ENS_15TempAllocPolicyEE13growStorageByEj + fun:_ZN2js8SCOutput5writeEy + fun:_ZN2js8SCOutput9writePairEjj + ... +} diff --git a/build/valgrind/x86_64-redhat-linux-gnu.sup b/build/valgrind/x86_64-redhat-linux-gnu.sup new file mode 100644 index 000000000..13e72cee1 --- /dev/null +++ b/build/valgrind/x86_64-redhat-linux-gnu.sup @@ -0,0 +1,60 @@ +# Full list is tracked through meta bug 793882 + +#################################### +# Leaks in third party libraries # +#################################### + +{ + Bug 793537 + Memcheck:Leak + ... + obj:/usr/lib64/libpango-1.0.so.0.2800.1 + ... +} +{ + Bug 793598 + Memcheck:Leak + ... + obj:/lib64/libdbus-1.so.3.4.0 + ... +} +{ + Bug 793600 + Memcheck:Leak + fun:realloc + obj:/usr/lib64/libfontconfig.so.1.4.4 + ... + fun:FcDefaultSubstitute + fun:_ZN17gfxPangoFontGroup11MakeFontSetEP14_PangoLanguagedP9nsAutoRefI10_FcPatternE + ... +} +{ + Bug 794366 + Memcheck:Leak + ... + obj:/usr/lib64/libgtk-x11-2.0.so.0.1800.9 + ... +} +{ + Bug 794368 + Memcheck:Leak + ... + obj:/usr/lib64/libXrandr.so.2.2.0 + ... +} +{ + Bug 794373 + Memcheck:Leak + ... + obj:/lib64/libgobject-2.0.so.0.2200.5 + ... +} +{ + Bug 850134 + Memcheck:Leak + fun:realloc + fun:_ZN2js6VectorImLm0ENS_15TempAllocPolicyEE13growStorageByEm + fun:_ZN2js8SCOutput5writeEm + fun:_ZN2js8SCOutput9writePairEjj + ... +} diff --git a/build/virtualenv/packages.txt b/build/virtualenv/packages.txt new file mode 100644 index 000000000..0768a25ae --- /dev/null +++ b/build/virtualenv/packages.txt @@ -0,0 +1,18 @@ +simplejson.pth:python/simplejson-2.1.1 +marionette.pth:testing/marionette/client +blessings.pth:python/blessings +mach.pth:python/mach +mozbuild.pth:python/mozbuild +pymake.pth:build/pymake +optional:setup.py:python/psutil:build_ext:--inplace +optional:psutil.pth:python/psutil +which.pth:python/which +ply.pth:other-licenses/ply/ +codegen.pth:python/codegen/ +mock.pth:python/mock-1.0.0 +mozilla.pth:build +mozilla.pth:config +mozilla.pth:xpcom/typelib/xpt/tools +copy:build/buildconfig.py +packages.txt:testing/mozbase/packages.txt +objdir:build diff --git a/build/virtualenv/populate_virtualenv.py b/build/virtualenv/populate_virtualenv.py new file mode 100644 index 000000000..b03205234 --- /dev/null +++ b/build/virtualenv/populate_virtualenv.py @@ -0,0 +1,385 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +# This file contains code for populating the virtualenv environment for +# Mozilla's build system. It is typically called as part of configure. + +from __future__ import print_function, unicode_literals, with_statement + +import distutils.sysconfig +import os +import shutil +import subprocess +import sys + + +# Minimum version of Python required to build. +MINIMUM_PYTHON_MAJOR = 2 +MINIMUM_PYTHON_MINOR = 7 + + +class VirtualenvManager(object): + """Contains logic for managing virtualenvs for building the tree.""" + + def __init__(self, topsrcdir, topobjdir, virtualenv_path, log_handle, + manifest_path): + """Create a new manager. + + Each manager is associated with a source directory, a path where you + want the virtualenv to be created, and a handle to write output to. + """ + assert os.path.isabs(manifest_path), "manifest_path must be an absolute path: %s" % (manifest_path) + self.topsrcdir = topsrcdir + self.topobjdir = topobjdir + self.virtualenv_root = virtualenv_path + self.log_handle = log_handle + self.manifest_path = manifest_path + + @property + def virtualenv_script_path(self): + """Path to virtualenv's own populator script.""" + return os.path.join(self.topsrcdir, 'python', 'virtualenv', + 'virtualenv.py') + + @property + def python_path(self): + if sys.platform in ('win32', 'cygwin'): + return os.path.join(self.virtualenv_root, 'Scripts', 'python.exe') + + return os.path.join(self.virtualenv_root, 'bin', 'python') + + @property + def activate_path(self): + if sys.platform in ('win32', 'cygwin'): + return os.path.join(self.virtualenv_root, 'Scripts', + 'activate_this.py') + + return os.path.join(self.virtualenv_root, 'bin', 'activate_this.py') + + def up_to_date(self): + """Returns whether the virtualenv is present and up to date.""" + + deps = [self.manifest_path, __file__] + + # check if virtualenv exists + if not os.path.exists(self.virtualenv_root) or \ + not os.path.exists(self.activate_path): + + return False + + # check modification times + activate_mtime = os.path.getmtime(self.activate_path) + dep_mtime = max(os.path.getmtime(p) for p in deps) + if dep_mtime > activate_mtime: + return False + + # recursively check sub packages.txt files + submanifests = [i[1] for i in self.packages() + if i[0] == 'packages.txt'] + for submanifest in submanifests: + submanifest = os.path.join(self.topsrcdir, submanifest) + submanager = VirtualenvManager(self.topsrcdir, + self.topobjdir, + self.virtualenv_root, + self.log_handle, + submanifest) + if not submanager.up_to_date(): + return False + + return True + + def ensure(self): + """Ensure the virtualenv is present and up to date. + + If the virtualenv is up to date, this does nothing. Otherwise, it + creates and populates the virtualenv as necessary. + + This should be the main API used from this class as it is the + highest-level. + """ + if self.up_to_date(): + return self.virtualenv_root + return self.build() + + def create(self): + """Create a new, empty virtualenv. + + Receives the path to virtualenv's virtualenv.py script (which will be + called out to), the path to create the virtualenv in, and a handle to + write output to. + """ + env = dict(os.environ) + env.pop('PYTHONDONTWRITEBYTECODE', None) + + args = [sys.executable, self.virtualenv_script_path, + '--system-site-packages', self.virtualenv_root] + + result = subprocess.call(args, stdout=self.log_handle, + stderr=subprocess.STDOUT, env=env) + + if result: + raise Exception('Error creating virtualenv.') + + return self.virtualenv_root + + def packages(self): + with file(self.manifest_path, 'rU') as fh: + packages = [line.rstrip().split(':') + for line in fh] + return packages + + def populate(self): + """Populate the virtualenv. + + The manifest file consists of colon-delimited fields. The first field + specifies the action. The remaining fields are arguments to that + action. The following actions are supported: + + setup.py -- Invoke setup.py for a package. Expects the arguments: + 1. relative path directory containing setup.py. + 2. argument(s) to setup.py. e.g. "develop". Each program argument + is delimited by a colon. Arguments with colons are not yet + supported. + + filename.pth -- Adds the path given as argument to filename.pth under + the virtualenv site packages directory. + + optional -- This denotes the action as optional. The requested action + is attempted. If it fails, we issue a warning and go on. The + initial "optional" field is stripped then the remaining line is + processed like normal. e.g. + "optional:setup.py:python/foo:built_ext:-i" + + copy -- Copies the given file in the virtualenv site packages + directory. + + packages.txt -- Denotes that the specified path is a child manifest. It + will be read and processed as if its contents were concatenated + into the manifest being read. + + objdir -- Denotes a relative path in the object directory to add to the + search path. e.g. "objdir:build" will add $topobjdir/build to the + search path. + + Note that the Python interpreter running this function should be the + one from the virtualenv. If it is the system Python or if the + environment is not configured properly, packages could be installed + into the wrong place. This is how virtualenv's work. + """ + + packages = self.packages() + + def handle_package(package): + python_lib = distutils.sysconfig.get_python_lib() + if package[0] == 'setup.py': + assert len(package) >= 2 + + self.call_setup(os.path.join(self.topsrcdir, package[1]), + package[2:]) + + return True + + if package[0] == 'copy': + assert len(package) == 2 + + src = os.path.join(self.topsrcdir, package[1]) + dst = os.path.join(python_lib, os.path.basename(package[1])) + + shutil.copy(src, dst) + + return True + + if package[0] == 'packages.txt': + assert len(package) == 2 + + src = os.path.join(self.topsrcdir, package[1]) + assert os.path.isfile(src), "'%s' does not exist" % src + submanager = VirtualenvManager(self.topsrcdir, + self.topobjdir, + self.virtualenv_root, + self.log_handle, + src) + submanager.populate() + + return True + + if package[0].endswith('.pth'): + assert len(package) == 2 + + path = os.path.join(self.topsrcdir, package[1]) + + with open(os.path.join(python_lib, package[0]), 'a') as f: + # This path is relative to the .pth file. Using a + # relative path allows the srcdir/objdir combination + # to be moved around (as long as the paths relative to + # each other remain the same). + f.write("%s\n" % os.path.relpath(path, python_lib)) + + return True + + if package[0] == 'optional': + try: + handle_package(package[1:]) + return True + except: + print('Error processing command. Ignoring', \ + 'because optional. (%s)' % ':'.join(package), + file=self.log_handle) + return False + + if package[0] == 'objdir': + assert len(package) == 2 + path = os.path.join(self.topobjdir, package[1]) + + with open(os.path.join(python_lib, 'objdir.pth'), 'a') as f: + f.write('%s\n' % path) + + return True + + raise Exception('Unknown action: %s' % package[0]) + + # We always target the OS X deployment target that Python itself was + # built with, regardless of what's in the current environment. If we + # don't do # this, we may run into a Python bug. See + # http://bugs.python.org/issue9516 and bug 659881. + # + # Note that this assumes that nothing compiled in the virtualenv is + # shipped as part of a distribution. If we do ship anything, the + # deployment target here may be different from what's targeted by the + # shipping binaries and # virtualenv-produced binaries may fail to + # work. + # + # We also ignore environment variables that may have been altered by + # configure or a mozconfig activated in the current shell. We trust + # Python is smart enough to find a proper compiler and to use the + # proper compiler flags. If it isn't your Python is likely broken. + IGNORE_ENV_VARIABLES = ('CC', 'CXX', 'CFLAGS', 'CXXFLAGS', 'LDFLAGS', + 'PYTHONDONTWRITEBYTECODE') + + try: + old_target = os.environ.get('MACOSX_DEPLOYMENT_TARGET', None) + sysconfig_target = \ + distutils.sysconfig.get_config_var('MACOSX_DEPLOYMENT_TARGET') + + if sysconfig_target is not None: + os.environ['MACOSX_DEPLOYMENT_TARGET'] = sysconfig_target + + old_env_variables = {} + for k in IGNORE_ENV_VARIABLES: + if k not in os.environ: + continue + + old_env_variables[k] = os.environ[k] + del os.environ[k] + + for package in packages: + handle_package(package) + finally: + os.environ.pop('MACOSX_DEPLOYMENT_TARGET', None) + + if old_target is not None: + os.environ['MACOSX_DEPLOYMENT_TARGET'] = old_target + + os.environ.update(old_env_variables) + + def call_setup(self, directory, arguments): + """Calls setup.py in a directory.""" + setup = os.path.join(directory, 'setup.py') + + program = [sys.executable, setup] + program.extend(arguments) + + # We probably could call the contents of this file inside the context + # of this interpreter using execfile() or similar. However, if global + # variables like sys.path are adjusted, this could cause all kinds of + # havoc. While this may work, invoking a new process is safer. + + try: + output = subprocess.check_output(program, cwd=directory, stderr=subprocess.STDOUT) + print(output) + except subprocess.CalledProcessError as e: + if 'Python.h: No such file or directory' in e.output: + print('WARNING: Python.h not found. Install Python development headers.') + else: + print(e.output) + + raise Exception('Error installing package: %s' % directory) + + def build(self): + """Build a virtualenv per tree conventions. + + This returns the path of the created virtualenv. + """ + + self.create() + + # We need to populate the virtualenv using the Python executable in + # the virtualenv for paths to be proper. + + args = [self.python_path, __file__, 'populate', self.topsrcdir, + self.topobjdir, self.virtualenv_root] + + result = subprocess.call(args, stdout=self.log_handle, + stderr=subprocess.STDOUT, cwd=self.topsrcdir) + + if result != 0: + raise Exception('Error populating virtualenv.') + + os.utime(self.activate_path, None) + + return self.virtualenv_root + + def activate(self): + """Activate the virtualenv in this Python context. + + If you run a random Python script and wish to "activate" the + virtualenv, you can simply instantiate an instance of this class + and call .ensure() and .activate() to make the virtualenv active. + """ + + execfile(self.activate_path, dict(__file__=self.activate_path)) + + +def verify_python_version(log_handle): + """Ensure the current version of Python is sufficient.""" + major, minor = sys.version_info[:2] + + if major != MINIMUM_PYTHON_MAJOR or minor < MINIMUM_PYTHON_MINOR: + log_handle.write('Python %d.%d or greater (but not Python 3) is ' + 'required to build. ' % + (MINIMUM_PYTHON_MAJOR, MINIMUM_PYTHON_MINOR)) + log_handle.write('You are running Python %d.%d.\n' % (major, minor)) + sys.exit(1) + + +if __name__ == '__main__': + if len(sys.argv) < 4: + print('Usage: populate_virtualenv.py /path/to/topsrcdir /path/to/topobjdir /path/to/virtualenv') + sys.exit(1) + + verify_python_version(sys.stdout) + + topsrcdir = sys.argv[1] + topobjdir = sys.argv[2] + virtualenv_path = sys.argv[3] + populate = False + + # This should only be called internally. + if sys.argv[1] == 'populate': + populate = True + topsrcdir = sys.argv[2] + topobjdir = sys.argv[3] + virtualenv_path = sys.argv[4] + + # path to default packages.txt + manifest_path = os.path.join(topsrcdir, 'build', 'virtualenv', 'packages.txt') + + manager = VirtualenvManager(topsrcdir, topobjdir, virtualenv_path, + sys.stdout, manifest_path) + + if populate: + manager.populate() + else: + manager.ensure() + diff --git a/build/win32/Makefile.in b/build/win32/Makefile.in new file mode 100644 index 000000000..c6a7dc45e --- /dev/null +++ b/build/win32/Makefile.in @@ -0,0 +1,76 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +DEPTH = @DEPTH@ +topsrcdir = @top_srcdir@ +srcdir = @srcdir@ +VPATH = @srcdir@ + +include $(DEPTH)/config/autoconf.mk + +NO_PROFILE_GUIDED_OPTIMIZE = 1 + +ifdef ENABLE_TESTS + +USE_STATIC_LIBS = 1 + +endif # ENABLE_TESTS + +MOZ_GLUE_LDFLAGS = + +include $(topsrcdir)/config/rules.mk + +ifdef WIN32_REDIST_DIR +ifndef MOZ_DEBUG + +ifeq (1400,$(_MSC_VER)) +REDIST_FILES = \ + Microsoft.VC80.CRT.manifest \ + msvcm80.dll \ + msvcp80.dll \ + msvcr80.dll \ + $(NULL) +endif + +ifeq (1500,$(_MSC_VER)) +REDIST_FILES = \ + Microsoft.VC90.CRT.manifest \ + msvcm90.dll \ + msvcp90.dll \ + msvcr90.dll \ + $(NULL) +endif + +ifeq (1600,$(_MSC_VER)) +REDIST_FILES = \ + msvcp100.dll \ + msvcr100.dll \ + $(NULL) +endif + +ifeq (1700,$(_MSC_VER)) +REDIST_FILES = \ + msvcp110.dll \ + msvcr110.dll \ + $(NULL) +endif + +ifdef REDIST_FILES +libs-preqs = \ + $(call mkdir_deps,$(FINAL_TARGET)) \ + $(NULL) + +libs:: $(libs-preqs) + install --preserve-timestamps $(foreach f,$(REDIST_FILES),"$(WIN32_REDIST_DIR)"/$(f)) $(FINAL_TARGET) +endif + +endif # ! MOZ_DEBUG +endif # WIN32_REDIST_DIR + +# run the binscope tool to make sure the binary and all libraries +# are using all available Windows OS-level security mechanisms +check:: + $(PYTHON) $(srcdir)/autobinscope.py $(DIST)/bin/$(MOZ_APP_NAME)$(BIN_SUFFIX) $(DIST)/crashreporter-symbols/ + $(PYTHON) $(srcdir)/autobinscope.py $(DIST)/bin/plugin-container.exe $(DIST)/crashreporter-symbols/ + diff --git a/build/win32/__init__.py b/build/win32/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/build/win32/__init__.py diff --git a/build/win32/autobinscope.py b/build/win32/autobinscope.py new file mode 100644 index 000000000..4899e731a --- /dev/null +++ b/build/win32/autobinscope.py @@ -0,0 +1,76 @@ +#!/usr/bin/env python + +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +# run Microsoft's Binscope tool (http://www.microsoft.com/download/en/details.aspx?id=11910) +# against a fresh Windows build. output a 'binscope.log' file with full details +# of the run and appropriate strings to integrate with the buildbots + +# from the docs : "The error code returned when running under the command line is equal +# to the number of failures the tool reported plus the number of errors. BinScope will return +# 0 only if there are no errors or failures." + +# the symbol dir should point to the symbol dir hierarchy created +# via running make buildsymbols in a windows build's objdir + +import sys +import subprocess +import os + +BINSCOPE_OUTPUT_LOGFILE = r".\binscope_xml_output.log" + +# usage +if len(sys.argv) < 3: + print """usage : autobinscope.by path_to_binary path_to_symbols [log_file_path]" + log_file_path is optional, log will be written to .\binscope_xml_output.log by default""" + sys.exit(0) + +binary_path = sys.argv[1] +symbol_path = sys.argv[2] + +if len(sys.argv) == 4: + log_file_path = sys.argv[3] +else: + log_file_path = BINSCOPE_OUTPUT_LOGFILE + +# execute binscope against the binary, using the BINSCOPE environment +# variable as the path to binscope.exe +try: + binscope_path = os.environ['BINSCOPE'] +except KeyError: + print "BINSCOPE environment variable is not set, can't check DEP/ASLR etc. status." + sys.exit(0) + +try: + proc = subprocess.Popen([binscope_path, "/target", binary_path, + "/output", log_file_path, "/sympath", symbol_path, + "/c", "ATLVersionCheck", "/c", "ATLVulnCheck", "/c", "FunctionPointersCheck", + "/c", "SharedSectionCheck", "/c", "APTCACheck", "/c", "NXCheck", + "/c", "GSCheck", "/c", "GSFunctionSafeBuffersCheck", "/c", "GSFriendlyInitCheck", + "/c", "CompilerVersionCheck", "/c", "SafeSEHCheck", "/c", "SNCheck", + "/c", "DBCheck"], stdout=subprocess.PIPE) + +except WindowsError, (errno, strerror): + if errno != 2 and errno != 3: + print "Unexpected error ! \nError " + str(errno) + " : " + strerror + "\nExiting !\n" + sys.exit(0) + else: + print "Could not locate binscope at location : %s\n" % binscope_path + print "Binscope wasn't installed or the BINSCOPE env variable wasn't set correctly, skipping this check and exiting..." + sys.exit(0) + +proc.wait() + +output = proc.communicate()[0] + +# is this a PASS or a FAIL ? +if proc.returncode != 0: + print "Error count: %d" % proc.returncode + print "TEST-UNEXPECTED-FAIL | autobinscope.py | %s is missing a needed Windows protection, such as /GS or ASLR" % binary_path + logfile = open(log_file_path, "r") + for line in logfile: + print(line), +else: + print "TEST-PASS | autobinscope.py | %s succeeded" % binary_path diff --git a/build/win32/crashinject.cpp b/build/win32/crashinject.cpp new file mode 100644 index 000000000..200d7d80f --- /dev/null +++ b/build/win32/crashinject.cpp @@ -0,0 +1,95 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + * Given a PID, this program attempts to inject a DLL into the process + * with that PID. The DLL it attempts to inject, "crashinjectdll.dll", + * must exist alongside this exe. The DLL will then crash the process. + */ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <windows.h> + +int main(int argc, char** argv) +{ + if (argc != 2) { + fprintf(stderr, "Usage: crashinject <PID>\n"); + return 1; + } + + int pid = atoi(argv[1]); + if (pid <= 0) { + fprintf(stderr, "Usage: crashinject <PID>\n"); + return 1; + } + + // find our DLL to inject + wchar_t filename[_MAX_PATH]; + if (GetModuleFileNameW(NULL, filename, sizeof(filename) / sizeof(wchar_t)) == 0) + return 1; + + wchar_t* slash = wcsrchr(filename, L'\\'); + if (slash == NULL) + return 1; + + slash++; + wcscpy(slash, L"crashinjectdll.dll"); + + // now find our target process + HANDLE targetProc = OpenProcess(PROCESS_VM_OPERATION | PROCESS_VM_WRITE | PROCESS_CREATE_THREAD | PROCESS_QUERY_INFORMATION, + FALSE, + pid); + if (targetProc == NULL) { + fprintf(stderr, "Error %d opening target process\n", GetLastError()); + return 1; + } + + /* + * This is sort of insane, but we're implementing a technique described here: + * http://www.codeproject.com/KB/threads/winspy.aspx#section_2 + * + * The gist is to use CreateRemoteThread to create a thread in the other + * process, but cheat and make the thread function kernel32!LoadLibrary, + * so that the only remote data we have to pass to the other process + * is the path to the library we want to load. The library we're loading + * will then do its dirty work inside the other process. + */ + HMODULE hKernel32 = GetModuleHandleW(L"Kernel32"); + // allocate some memory to hold the path in the remote process + void* pLibRemote = VirtualAllocEx(targetProc, NULL, sizeof(filename), + MEM_COMMIT, PAGE_READWRITE); + if (pLibRemote == NULL) { + fprintf(stderr, "Error %d in VirtualAllocEx\n", GetLastError()); + CloseHandle(targetProc); + return 1; + } + + if (!WriteProcessMemory(targetProc, pLibRemote, (void*)filename, + sizeof(filename), NULL)) { + fprintf(stderr, "Error %d in WriteProcessMemory\n", GetLastError()); + VirtualFreeEx(targetProc, pLibRemote, sizeof(filename), MEM_RELEASE); + CloseHandle(targetProc); + return 1; + } + // Now create a thread in the target process that will load our DLL + HANDLE hThread = CreateRemoteThread( + targetProc, NULL, 0, + (LPTHREAD_START_ROUTINE)GetProcAddress(hKernel32, + "LoadLibraryW"), + pLibRemote, 0, NULL); + if (hThread == NULL) { + fprintf(stderr, "Error %d in CreateRemoteThread\n", GetLastError()); + VirtualFreeEx(targetProc, pLibRemote, sizeof(filename), MEM_RELEASE); + CloseHandle(targetProc); + return 1; + } + WaitForSingleObject(hThread, INFINITE); + // Cleanup, not that it's going to matter at this point + CloseHandle(hThread); + VirtualFreeEx(targetProc, pLibRemote, sizeof(filename), MEM_RELEASE); + CloseHandle(targetProc); + + return 0; +} diff --git a/build/win32/crashinjectdll/Makefile.in b/build/win32/crashinjectdll/Makefile.in new file mode 100644 index 000000000..179c9049e --- /dev/null +++ b/build/win32/crashinjectdll/Makefile.in @@ -0,0 +1,18 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +DEPTH = @DEPTH@ +topsrcdir = @top_srcdir@ +srcdir = @srcdir@ +VPATH = @srcdir@ + +include $(DEPTH)/config/autoconf.mk + +DEFFILE = $(srcdir)/crashinjectdll.def +FORCE_SHARED_LIB = 1 +USE_STATIC_LIBS = 1 + +MOZ_GLUE_LDFLAGS = + +include $(topsrcdir)/config/rules.mk diff --git a/build/win32/crashinjectdll/crashinjectdll.cpp b/build/win32/crashinjectdll/crashinjectdll.cpp new file mode 100644 index 000000000..0d69a72d1 --- /dev/null +++ b/build/win32/crashinjectdll/crashinjectdll.cpp @@ -0,0 +1,38 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include <stdio.h> +#include <windows.h> + +// make sure we only ever spawn one thread +DWORD tid = -1; + +DWORD WINAPI CrashingThread( + LPVOID lpParameter +) +{ + // not a very friendly DLL + volatile int* x = (int *)0x0; + *x = 1; + return 0; +} + +BOOL WINAPI DllMain( + HANDLE hinstDLL, + DWORD dwReason, + LPVOID lpvReserved +) +{ + if (tid == -1) + // we have to crash on another thread because LoadLibrary() will + // catch memory access errors and return failure to the calling process + CreateThread( + NULL, // default security attributes + 0, // use default stack size + CrashingThread , // thread function name + NULL, // argument to thread function + 0, // use default creation flags + &tid); // returns the thread identifier + return TRUE; +} diff --git a/build/win32/crashinjectdll/crashinjectdll.def b/build/win32/crashinjectdll/crashinjectdll.def new file mode 100644 index 000000000..d1ea5602d --- /dev/null +++ b/build/win32/crashinjectdll/crashinjectdll.def @@ -0,0 +1,7 @@ +;+# This Source Code Form is subject to the terms of the Mozilla Public +;+# License, v. 2.0. If a copy of the MPL was not distributed with this +;+# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +LIBRARY crashinjectdll +EXPORTS + DllMain diff --git a/build/win32/crashinjectdll/moz.build b/build/win32/crashinjectdll/moz.build new file mode 100644 index 000000000..de3bb42f7 --- /dev/null +++ b/build/win32/crashinjectdll/moz.build @@ -0,0 +1,12 @@ +# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +CPP_SOURCES += [ + 'crashinjectdll.cpp', +] + +LIBRARY_NAME = 'crashinjectdll' + diff --git a/build/win32/moz.build b/build/win32/moz.build new file mode 100644 index 000000000..c02c976ea --- /dev/null +++ b/build/win32/moz.build @@ -0,0 +1,16 @@ +# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +if CONFIG['_MSC_VER'] and CONFIG['OS_TEST'] != 'x86_64': + TEST_DIRS += ['vmwarerecordinghelper'] + +TEST_DIRS += ['crashinjectdll'] + +if CONFIG['ENABLE_TESTS']: + PROGRAM = 'crashinject' + CPP_SOURCES += [ + 'crashinject.cpp', + ] diff --git a/build/win32/mozconfig.vs2010 b/build/win32/mozconfig.vs2010 new file mode 100644 index 000000000..ac644796c --- /dev/null +++ b/build/win32/mozconfig.vs2010 @@ -0,0 +1,13 @@ +export INCLUDE=/d/msvs10/vc/include:/d/msvs10/vc/atlmfc/include:/d/sdks/v7.0/include:/d/sdks/v7.0/include/atl:/d/msvs8/VC/PlatformSDK/include:/d/sdks/dx10/include +export LIBPATH=/d/msvs10/vc/lib:/d/msvs10/vc/atlmfc/lib:/c/WINDOWS/Microsoft.NET/Framework/v2.0.50727 +export LIB=/d/msvs10/vc/lib:/d/msvs10/vc/atlmfc/lib:/d/sdks/v7.0/lib:/d/msvs8/VC/PlatformSDK/lib:/d/msvs8/SDK/v2.0/lib:/d/mozilla-build/atlthunk_compat:/d/sdks/dx10/lib/x86 +export PATH="/d/msvs10/VSTSDB/Deploy:/d/msvs10/Common7/IDE/:/d/msvs10/VC/BIN:/d/msvs10/Common7/Tools:/d/msvs10/VC/VCPackages:${PATH}" +export WIN32_REDIST_DIR=/d/msvs10/VC/redist/x86/Microsoft.VC100.CRT + +. $topsrcdir/build/mozconfig.vs2010-common + +mk_export_correct_style LIB +mk_export_correct_style LIBPATH +mk_export_correct_style PATH +mk_export_correct_style INCLUDE +mk_export_correct_style WIN32_REDIST_DIR diff --git a/build/win32/mozconfig.vs2010-win64 b/build/win32/mozconfig.vs2010-win64 new file mode 100644 index 000000000..ba61c2429 --- /dev/null +++ b/build/win32/mozconfig.vs2010-win64 @@ -0,0 +1,33 @@ + +if [ -d "/c/PROGRA~2/MICROS~2.0" ]; then + # /c/Program Files (x86)/Microsoft Visual Studio 10.0 + _VSPATH="/c/PROGRA~2/MICROS~2.0" +else + _VSPATH="/c/tools/msvs10" +fi + +## SDK redist ## +export WIN32_REDIST_DIR=${_VSPATH}/VC/redist/x86/Microsoft.VC100.CRT + +## moz tools location for 64-bit builders ## +export MOZ_TOOLS=C:/mozilla-build/moztools + +## includes: win8 sdk includes, winrt headers for metro, msvc 10 std library, directx sdk for d3d9 ## +export INCLUDE=/c/Program\ Files\ \(x86\)/Windows\ Kits/8.0/include/shared:/c/Program\ Files\ \(x86\)/Windows\ Kits/8.0/include/um:/c/Program\ Files\ \(x86\)/Windows\ Kits/8.0/include/winrt:/c/Program\ Files\ \(x86\)/Windows\ Kits/8.0/include/winrt/wrl:/c/Program\ Files\ \(x86\)/Windows\ Kits/8.0/include/winrt/wrl/wrappers:${_VSPATH}/vc/include:${_VSPATH}/vc/atlmfc/include:/c/tools/sdks/dx10/include + +## libs: win8 sdk x86 (32-bit) libs, msvc 10 (32-bit) std library, msvc 10 atl libs, directx sdk (32-bit) for d3d9 ## +export LIBPATH=/c/Program\ Files\ \(x86\)/Windows\ Kits/8.0/Lib/win8/um/x86:${_VSPATH}/vc/lib:${_VSPATH}/vc/atlmfc/lib:/c/tools/sdks/dx10/lib +export LIB=/c/Program\ Files\ \(x86\)/Windows\ Kits/8.0/Lib/win8/um/x86:${_VSPATH}/vc/lib:${_VSPATH}/vc/atlmfc/lib:/c/tools/sdks/dx10/lib + +## paths: win8 sdk x86 (32-bit) tools, msvc 10 (32-bit) build toolchain, moz tools ## +export PATH="/c/Program Files (x86)/Windows Kits/8.0/bin/x86:${_VSPATH}/Common7/IDE:${_VSPATH}/VC/BIN:${_VSPATH}/Common7/Tools:${_VSPATH}/VC/VCPackages:/c/mozilla-build/moztools:${PATH}" + +. $topsrcdir/build/mozconfig.vs2010-common + +mk_export_correct_style LIB +mk_export_correct_style LIBPATH +mk_export_correct_style PATH +mk_export_correct_style INCLUDE +mk_export_correct_style WIN32_REDIST_DIR + +mk_add_options "export MOZ_TOOLS=$MOZ_TOOLS" diff --git a/build/win32/pgomerge.py b/build/win32/pgomerge.py new file mode 100644 index 000000000..313d66870 --- /dev/null +++ b/build/win32/pgomerge.py @@ -0,0 +1,44 @@ +#!/usr/bin/python +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +# Usage: pgomerge.py <binary basename> <dist/bin> +# Gathers .pgc files from dist/bin and merges them into +# $PWD/$basename.pgd using pgomgr, then deletes them. +# No errors if any of these files don't exist. + +import sys, os, os.path, subprocess +if not sys.platform == "win32": + raise Exception("This script was only meant for Windows.") + +def MergePGOFiles(basename, pgddir, pgcdir): + """Merge pgc files produced from an instrumented binary + into the pgd file for the second pass of profile-guided optimization + with MSVC. |basename| is the name of the DLL or EXE without the + extension. |pgddir| is the path that contains <basename>.pgd + (should be the objdir it was built in). |pgcdir| is the path + containing basename!N.pgc files, which is probably dist/bin. + Calls pgomgr to merge each pgc file into the pgd, then deletes + the pgc files.""" + if not os.path.isdir(pgddir) or not os.path.isdir(pgcdir): + return + pgdfile = os.path.abspath(os.path.join(pgddir, basename + ".pgd")) + if not os.path.isfile(pgdfile): + return + for file in os.listdir(pgcdir): + if file.startswith(basename+"!") and file.endswith(".pgc"): + try: + pgcfile = os.path.normpath(os.path.join(pgcdir, file)) + subprocess.call(['pgomgr', '-merge', + pgcfile, + pgdfile]) + os.remove(pgcfile) + except OSError: + pass + +if __name__ == '__main__': + if len(sys.argv) != 3: + print >>sys.stderr, "Usage: pgomerge.py <binary basename> <dist/bin>" + sys.exit(1) + MergePGOFiles(sys.argv[1], os.getcwd(), sys.argv[2]) diff --git a/build/win32/procmem.py b/build/win32/procmem.py new file mode 100644 index 000000000..f9383802e --- /dev/null +++ b/build/win32/procmem.py @@ -0,0 +1,48 @@ +# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+import os, sys, ctypes, ctypes.wintypes
+
+class VM_COUNTERS(ctypes.Structure):
+ _fields_ = [("PeakVirtualSize", ctypes.wintypes.ULONG),
+ ("VirtualSize", ctypes.wintypes.ULONG),
+ ("PageFaultCount", ctypes.wintypes.ULONG),
+ ("PeakWorkingSetSize", ctypes.wintypes.ULONG),
+ ("WorkingSetSize", ctypes.wintypes.ULONG),
+ ("QuotaPeakPagedPoolUsage", ctypes.wintypes.ULONG),
+ ("QuotaPagedPoolUsage", ctypes.wintypes.ULONG),
+ ("QuotaPeakNonPagedPoolUsage", ctypes.wintypes.ULONG),
+ ("QuotaNonPagedPoolUsage", ctypes.wintypes.ULONG),
+ ("PagefileUsage", ctypes.wintypes.ULONG),
+ ("PeakPagefileUsage", ctypes.wintypes.ULONG)
+ ]
+
+def get_vmsize(handle):
+ """
+ Return (peak_virtual_size, virtual_size) for the process |handle|.
+ """
+ ProcessVmCounters = 3
+ vmc = VM_COUNTERS()
+ if ctypes.windll.ntdll.NtQueryInformationProcess(int(handle),
+ ProcessVmCounters,
+ ctypes.byref(vmc),
+ ctypes.sizeof(vmc),
+ None) == 0:
+ return (vmc.PeakVirtualSize, vmc.VirtualSize)
+
+ return (-1, -1)
+
+if __name__ == '__main__':
+ PROCESS_QUERY_INFORMATION = 0x0400
+ for pid in sys.argv[1:]:
+ handle = ctypes.windll.kernel32.OpenProcess(PROCESS_QUERY_INFORMATION,
+ 0, # no inherit
+ int(pid))
+ if handle:
+ print "Process %s:" % pid
+ vsize, peak_vsize = get_vmsize(handle)
+ print "peak vsize: %d" % peak_vsize
+ ctypes.windll.kernel32.CloseHandle(handle)
+ else:
+ print "Couldn't open process %s" % pid
diff --git a/build/win32/vmwarerecordinghelper/Makefile.in b/build/win32/vmwarerecordinghelper/Makefile.in new file mode 100644 index 000000000..2a601343e --- /dev/null +++ b/build/win32/vmwarerecordinghelper/Makefile.in @@ -0,0 +1,19 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +DEPTH = @DEPTH@ +topsrcdir = @top_srcdir@ +srcdir = @srcdir@ +VPATH = @srcdir@ + +include $(DEPTH)/config/autoconf.mk + +DEFFILE = $(srcdir)/$(LIBRARY_NAME).def + +FORCE_SHARED_LIB = 1 +USE_STATIC_LIBS = 1 + +MOZ_GLUE_LDFLAGS = + +include $(topsrcdir)/config/rules.mk diff --git a/build/win32/vmwarerecordinghelper/moz.build b/build/win32/vmwarerecordinghelper/moz.build new file mode 100644 index 000000000..64c273474 --- /dev/null +++ b/build/win32/vmwarerecordinghelper/moz.build @@ -0,0 +1,12 @@ +# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +CPP_SOURCES += [ + 'vmwarerecordinghelper.cpp', +] + +LIBRARY_NAME = 'vmwarerecordinghelper' + diff --git a/build/win32/vmwarerecordinghelper/vmwarerecordinghelper.cpp b/build/win32/vmwarerecordinghelper/vmwarerecordinghelper.cpp new file mode 100644 index 000000000..b56bab2ab --- /dev/null +++ b/build/win32/vmwarerecordinghelper/vmwarerecordinghelper.cpp @@ -0,0 +1,34 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/** + * The following code comes from "Starting and Stopping Recording of Virtual + * Machine Activity from Within the Guest": + * + * http://kb.vmware.com/selfservice/documentLink.do?externalID=1001401 + */ + +void __cdecl +StartRecording() +{ + __asm { + mov eax, 564d5868h + mov ebx, 1 + mov cx, 47 + mov dx, 5658h + in eax, dx + } +} + +void __cdecl +StopRecording() +{ + __asm { + mov eax, 564d5868h + mov ebx, 2 + mov cx, 47 + mov dx, 5658h + in eax, dx + } +} diff --git a/build/win32/vmwarerecordinghelper/vmwarerecordinghelper.def b/build/win32/vmwarerecordinghelper/vmwarerecordinghelper.def new file mode 100644 index 000000000..c0d97f8a7 --- /dev/null +++ b/build/win32/vmwarerecordinghelper/vmwarerecordinghelper.def @@ -0,0 +1,8 @@ +;+# This Source Code Form is subject to the terms of the Mozilla Public +;+# License, v. 2.0. If a copy of the MPL was not distributed with this +;+# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +LIBRARY vmwarerecordinghelper +EXPORTS + StartRecording + StopRecording diff --git a/build/win64/mozconfig.vs2010 b/build/win64/mozconfig.vs2010 new file mode 100644 index 000000000..c9d925169 --- /dev/null +++ b/build/win64/mozconfig.vs2010 @@ -0,0 +1,37 @@ + +if [ -d "/c/PROGRA~2/MICROS~2.0" ]; then + # /c/Program Files (x86)/Microsoft Visual Studio 10.0 + _VSPATH="/c/PROGRA~2/MICROS~2.0" +else + _VSPATH="/c/tools/msvs10" +fi + +## SDK redist ## +export WIN32_REDIST_DIR=${_VSPATH}/VC/redist/x64/Microsoft.VC100.CRT + +## includes: win8 sdk includes, winrt headers for metro, msvc 10 std library, directx sdk for d3d9 ## +export INCLUDE=/c/Program\ Files\ \(x86\)/Windows\ Kits/8.0/include/shared:/c/Program\ Files\ \(x86\)/Windows\ Kits/8.0/include/um:/c/Program\ Files\ \(x86\)/Windows\ Kits/8.0/include/winrt:/c/Program\ Files\ \(x86\)/Windows\ Kits/8.0/include/winrt/wrl:/c/Program\ Files\ \(x86\)/Windows\ Kits/8.0/include/winrt/wrl/wrappers:${_VSPATH}/vc/include:${_VSPATH}/vc/atlmfc/include:/c/tools/sdks/dx10/include + +## libs: win8 sdk x64 (64-bit) libs, msvc 10 (64-bit) std library, msvc 10 atl libs, directx sdk (64-bit) for d3d9 ## +export LIBPATH=/c/Program\ Files\ \(x86\)/Windows\ Kits/8.0/Lib/win8/um/x64:${_VSPATH}/vc/lib/amd64:${_VSPATH}/vc/atlmfc/lib/amd64:/c/tools/sdks/dx10/lib/x64 +export LIB=/c/Program\ Files\ \(x86\)/Windows\ Kits/8.0/Lib/win8/um/x64:${_VSPATH}/vc/lib/amd64:${_VSPATH}/vc/atlmfc/lib/amd64:/c/tools/sdks/dx10/lib/x64 + +## paths: win8 sdk x64 (64-bit) tools, msvc 10 (64-bit) build toolchain, moz tools ## +export PATH="/c/Program Files (x86)/Windows Kits/8.0/bin/x64:${_VSPATH}/Common7/IDE:${_VSPATH}/VC/BIN/amd64:${_VSPATH}/VC/BIN/x86_amd64:${_VSPATH}/VC/BIN:${_VSPATH}/Common7/Tools:${_VSPATH}/VC/VCPackages:${PATH}" + +# Use 32bit linker for PGO crash bug. +# https://connect.microsoft.com/VisualStudio/feedback/details/686117/ +if [ -f /c/PROGRA~2/MICROS~2.0/VC/BIN/x86_amd64/link.exe ]; then + # /c/Program Files (x86)/Microsoft Visual Studio 10.0 + export LD=c:/PROGRA~2/MICROS~2.0/VC/BIN/x86_amd64/link.exe +else + export LD=c:/tools/msvs10/VC/BIN/x86_amd64/link.exe +fi + +. $topsrcdir/build/mozconfig.vs2010-common + +mk_export_correct_style LIB +mk_export_correct_style LIBPATH +mk_export_correct_style PATH +mk_export_correct_style INCLUDE +mk_export_correct_style WIN32_REDIST_DIR diff --git a/build/xpccheck.py b/build/xpccheck.py new file mode 100644 index 000000000..718343982 --- /dev/null +++ b/build/xpccheck.py @@ -0,0 +1,78 @@ +# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+'''A generic script to verify all test files are in the
+corresponding .ini file.
+
+Usage: xpccheck.py <directory> [<directory> ...]
+'''
+
+import sys
+import os
+from glob import glob
+import manifestparser
+
+def getIniTests(testdir):
+ mp = manifestparser.ManifestParser(strict=False)
+ mp.read(os.path.join(testdir, 'xpcshell.ini'))
+ return mp.tests
+
+def verifyDirectory(initests, directory):
+ files = glob(os.path.join(os.path.abspath(directory), "test_*"))
+ for f in files:
+ if (not os.path.isfile(f)):
+ continue
+
+ name = os.path.basename(f)
+ if name.endswith('.in'):
+ name = name[:-3]
+
+ if not name.endswith('.js'):
+ continue
+
+ found = False
+ for test in initests:
+ if os.path.join(os.path.abspath(directory), name) == test['path']:
+ found = True
+ break
+
+ if not found:
+ print >>sys.stderr, "TEST-UNEXPECTED-FAIL | xpccheck | test %s is missing from test manifest %s!" % (name, os.path.join(directory, 'xpcshell.ini'))
+ sys.exit(1)
+
+def verifyIniFile(initests, directory):
+ files = glob(os.path.join(os.path.abspath(directory), "test_*"))
+ for test in initests:
+ name = test['path'].split('/')[-1]
+
+ found = False
+ for f in files:
+
+ fname = f.split('/')[-1]
+ if fname.endswith('.in'):
+ fname = '.in'.join(fname.split('.in')[:-1])
+
+ if os.path.join(os.path.abspath(directory), fname) == test['path']:
+ found = True
+ break
+
+ if not found:
+ print >>sys.stderr, "TEST-UNEXPECTED-FAIL | xpccheck | found %s in xpcshell.ini and not in directory '%s'" % (name, directory)
+ sys.exit(1)
+
+if __name__ == '__main__':
+ if len(sys.argv) < 3:
+ print >>sys.stderr, "Usage: xpccheck.py <topsrcdir> <directory> [<directory> ...]"
+ sys.exit(1)
+
+ topsrcdir = sys.argv[1]
+ for d in sys.argv[2:]:
+ # xpcshell-unpack is a copy of xpcshell sibling directory and in the Makefile
+ # we copy all files (including xpcshell.ini from the sibling directory.
+ if d.endswith('toolkit/mozapps/extensions/test/xpcshell-unpack'):
+ continue
+
+ initests = getIniTests(d)
+ verifyDirectory(initests, d)
+ verifyIniFile(initests, d)
|