From eeb89f8b6b4c909f9107cc01f724016437c38061 Mon Sep 17 00:00:00 2001 From: "benjamin@smedbergs.us" Date: Tue, 26 Jun 2007 09:35:01 -0700 Subject: [PATCH] Bug 383167 trying again, reviews by luser and biesi, sr=jst --- Makefile.in | 2 +- browser/app/Makefile.in | 29 +- browser/app/application.ini | 14 +- browser/app/nsBrowserApp.cpp | 62 +- browser/installer/unix/packages-static | 1 + browser/installer/windows/packages-static | 1 + client.mk | 3 +- config/Makefile.in | 29 +- config/aboutime.pl | 24 - config/bdate.c | 63 - config/bdate.pl | 44 - config/build-number.pl | 54 - config/config.mk | 6 - config/configobj.py | 2279 +++++++++++++++++ config/mozBDate.pm | 172 -- config/printconfigsetting.py | 21 + config/version_win.pl | 14 +- dom/src/base/nsGlobalWindow.cpp | 15 +- gfx/src/ps/nsPostScriptObj.cpp | 8 +- layout/build/Makefile.in | 20 - layout/build/nsContentHTTPStartup.cpp | 113 - layout/build/nsContentHTTPStartup.h | 78 - layout/build/nsLayoutModule.cpp | 9 - modules/libreg/src/vr_stubs.c | 7 - netwerk/protocol/http/src/nsHttpHandler.cpp | 18 +- netwerk/protocol/http/src/nsHttpHandler.h | 2 +- toolkit/xre/Makefile.in | 10 +- toolkit/xre/make-platformini.py | 31 + toolkit/xre/nsAppRunner.cpp | 79 +- xpcom/glue/standalone/Makefile.in | 1 - .../standalone/nsGREDirServiceProvider.cpp | 320 --- .../glue/standalone/nsGREDirServiceProvider.h | 58 - xpcom/glue/standalone/nsXPCOMGlue.cpp | 66 - xpcom/glue/standalone/nsXPCOMGlue.h | 40 - xpcom/system/Makefile.in | 1 + .../xre => xpcom/system}/nsIXULAppInfo.idl | 0 xpinstall/src/nsJSInstall.cpp | 3 - xpinstall/src/nsSoftwareUpdate.cpp | 1 - xulrunner/app/nsRegisterGRE.h | 6 + xulrunner/app/nsXULRunnerApp.cpp | 72 +- xulrunner/examples/simple/application.ini | 2 +- 41 files changed, 2555 insertions(+), 1223 deletions(-) delete mode 100755 config/aboutime.pl delete mode 100644 config/bdate.c delete mode 100755 config/bdate.pl delete mode 100644 config/build-number.pl create mode 100644 config/configobj.py delete mode 100755 config/mozBDate.pm create mode 100644 config/printconfigsetting.py delete mode 100644 layout/build/nsContentHTTPStartup.cpp delete mode 100644 layout/build/nsContentHTTPStartup.h create mode 100644 toolkit/xre/make-platformini.py delete mode 100644 xpcom/glue/standalone/nsGREDirServiceProvider.cpp delete mode 100644 xpcom/glue/standalone/nsGREDirServiceProvider.h rename {toolkit/xre => xpcom/system}/nsIXULAppInfo.idl (100%) diff --git a/Makefile.in b/Makefile.in index 2601ac9c1b0..22bb6a0aaa6 100644 --- a/Makefile.in +++ b/Makefile.in @@ -203,4 +203,4 @@ deliver: splitsymbols rebase signnss endif # WINNT -BUILDID = $(shell cat $(DEPTH)/config/build_number) +BUILDID = $(shell $(PYTHON) $(srcdir)/config/printconfigsetting.py $(DIST)/bin/application.ini App BuildID) diff --git a/browser/app/Makefile.in b/browser/app/Makefile.in index cb0c9d4b860..b606164e8f4 100644 --- a/browser/app/Makefile.in +++ b/browser/app/Makefile.in @@ -65,27 +65,16 @@ DEFINES += -DAPP_VERSION="$(APP_VERSION)" APP_UA_NAME = $(shell echo $(MOZ_APP_DISPLAYNAME) | sed -e's/[^A-Za-z]//g') DEFINES += -DAPP_UA_NAME="$(APP_UA_NAME)" -ifdef LIBXUL_SDK -# Build application.ini for a XULRunner app - DIST_FILES = application.ini -# GRE_BUILD_ID is only available in nsBuildID.h in a form that we can't use -# directly. So munge it. Beware makefile and shell escaping -AWK_EXPR = '/\#define GRE_BUILD_ID/ { gsub(/"/, "", $$3); print $$3 }' -AWK_CMD = awk $(AWK_EXPR) < $(LIBXUL_DIST)/include/nsBuildID.h +GRE_MILESTONE = $(shell $(PYTHON) $(topsrcdir)/config/printconfigsetting.py $(LIBXUL_DIST)/bin/platform.ini Build Milestone) +GRE_BUILDID = $(shell $(PYTHON) $(topsrcdir)/config/printconfigsetting.py $(LIBXUL_DIST)/bin/platform.ini Build BuildID) -GRE_BUILD_ID = $(shell $(AWK_CMD)) +DEFINES += -DGRE_MILESTONE=$(GRE_MILESTONE) -DGRE_BUILDID=$(GRE_BUILDID) -DEFINES += -DGRE_BUILD_ID=$(GRE_BUILD_ID) - -include $(topsrcdir)/config/rules.mk - -else +ifndef LIBXUL_SDK # Build a binary bootstrapping with XRE_main -MOZILLA_INTERNAL_API = 1 - ifeq ($(USE_SHORT_LIBNAME), 1) PROGRAM = $(MOZ_APP_NAME)$(BIN_SUFFIX) else @@ -94,6 +83,7 @@ endif REQUIRES = \ xpcom \ + string \ xulapp \ $(NULL) @@ -120,11 +110,18 @@ ifneq (,$(filter mac cocoa,$(MOZ_WIDGET_TOOLKIT))) TK_LIBS := -framework Cocoa $(TK_LIBS) endif +ifdef MOZ_ENABLE_LIBXUL +APP_XPCOM_LIBS = $(XPCOM_GLUE_LDOPTS) +else +MOZILLA_INTERNAL_API = 1 +APP_XPCOM_LIBS = $(XPCOM_LIBS) +endif + LIBS += \ $(STATIC_COMPONENTS_LINKER_PATH) \ $(EXTRA_DSO_LIBS) \ $(MOZ_JS_LIBS) \ - $(XPCOM_LIBS) \ + $(APP_XPCOM_LIBS) \ $(NSPR_LIBS) \ $(TK_LIBS) \ $(NULL) diff --git a/browser/app/application.ini b/browser/app/application.ini index 190a428d48b..fdfecfd7a96 100644 --- a/browser/app/application.ini +++ b/browser/app/application.ini @@ -40,18 +40,24 @@ Vendor=Mozilla Name=Firefox Version=@APP_VERSION@ -BuildID=@BUILD_ID@ +BuildID=@GRE_BUILDID@ Copyright=Copyright (c) 1998 - 2007 mozilla.org ID={ec8030f7-c20a-464f-9b0e-13a3a9e97384} [Gecko] -MinVersion=@GRE_BUILD_ID@ -MaxVersion=@GRE_BUILD_ID@ +MinVersion=@GRE_MILESTONE@ +MaxVersion=@GRE_MILESTONE@ [XRE] EnableProfileMigrator=1 EnableExtensionManager=1 [Crash Reporter] -Enabled=0 +#if MOZILLA_OFFICIAL +#if XP_WIN +Enabled=1 +#elif XP_MACOSX +Enabled=1 +#endif +#endif ServerURL=https://crash-reports.mozilla.com/submit diff --git a/browser/app/nsBrowserApp.cpp b/browser/app/nsBrowserApp.cpp index 8d22d9d78c5..58a600f2e69 100644 --- a/browser/app/nsBrowserApp.cpp +++ b/browser/app/nsBrowserApp.cpp @@ -41,32 +41,52 @@ #include #include #endif -#include "nsBuildID.h" -static const nsXREAppData kAppData = { - sizeof(nsXREAppData), - nsnull, - "Mozilla", - "Firefox", - NS_STRINGIFY(APP_VERSION), - NS_STRINGIFY(BUILD_ID), - "{ec8030f7-c20a-464f-9b0e-13a3a9e97384}", - "Copyright (c) 1998 - 2007 mozilla.org", - NS_XRE_ENABLE_PROFILE_MIGRATOR | - NS_XRE_ENABLE_EXTENSION_MANAGER -#if defined(MOZILLA_OFFICIAL) && (defined(XP_WIN) || defined(XP_MACOSX)) - | NS_XRE_ENABLE_CRASH_REPORTER +#include +#include + +#include "nsCOMPtr.h" +#include "nsILocalFile.h" +#include "nsStringGlue.h" + +static void Output(const char *fmt, ... ) +{ + va_list ap; + va_start(ap, fmt); + +#if defined(XP_WIN) && !MOZ_WINCONSOLE + char msg[2048]; + + vsnprintf(msg, sizeof(msg), fmt, ap); + + MessageBox(NULL, msg, "XULRunner", MB_OK | MB_ICONERROR); +#else + vfprintf(stderr, fmt, ap); #endif -, - nsnull, // xreDirectory - nsnull, // minVersion - nsnull, // maxVersion - "https://crash-reports.mozilla.com/submit" -}; + + va_end(ap); +} int main(int argc, char* argv[]) { - return XRE_main(argc, argv, &kAppData); + nsCOMPtr appini; + nsresult rv = XRE_GetBinaryPath(argv[0], getter_AddRefs(appini)); + if (NS_FAILED(rv)) { + Output("Couldn't calculate the application directory."); + return 255; + } + appini->SetNativeLeafName(NS_LITERAL_CSTRING("application.ini")); + + nsXREAppData *appData; + rv = XRE_CreateAppData(appini, &appData); + if (NS_FAILED(rv)) { + Output("Couldn't read application.ini"); + return 255; + } + + int result = XRE_main(argc, argv, appData); + XRE_FreeAppData(appData); + return result; } #if defined( XP_WIN ) && defined( WIN32 ) && !defined(__GNUC__) diff --git a/browser/installer/unix/packages-static b/browser/installer/unix/packages-static index 427b47085d2..da8e717c995 100644 --- a/browser/installer/unix/packages-static +++ b/browser/installer/unix/packages-static @@ -45,6 +45,7 @@ bin/components/libjar50.so ; [Base Browser Files] bin/@MOZ_APP_NAME@-bin bin/@MOZ_APP_NAME@ +bin/application.ini bin/mozilla-xremote-client bin/run-mozilla.sh bin/plugins/libnullplugin.so diff --git a/browser/installer/windows/packages-static b/browser/installer/windows/packages-static index 1b164502247..b9604e75a41 100644 --- a/browser/installer/windows/packages-static +++ b/browser/installer/windows/packages-static @@ -48,6 +48,7 @@ bin\msvcr80.dll [browser] ; [Base Browser Files] bin\@MOZ_APP_NAME@.exe +bin\application.ini bin\plugins\npnul32.dll bin\res\cmessage.txt bin\res\effective_tld_names.dat diff --git a/client.mk b/client.mk index 1fdcc2aa9ce..60d4112d3d3 100644 --- a/client.mk +++ b/client.mk @@ -457,6 +457,7 @@ ifndef MAKE MAKE := gmake endif PERL ?= perl +PYTHON ?= python CONFIG_GUESS_SCRIPT := $(wildcard $(TOPSRCDIR)/build/autoconf/config.guess) ifdef CONFIG_GUESS_SCRIPT @@ -940,7 +941,7 @@ else ifdef MOZ_UNIFY_BDATE ifndef MOZ_BUILD_DATE ifdef MOZ_BUILD_PROJECTS -MOZ_BUILD_DATE = $(shell $(PERL) -I$(TOPSRCDIR)/config $(TOPSRCDIR)/config/bdate.pl) +MOZ_BUILD_DATE = $(shell $(PYTHON) $(TOPSRCDIR)/toolkit/xre/make-platformini.py --print-buildid) export MOZ_BUILD_DATE endif endif diff --git a/config/Makefile.in b/config/Makefile.in index a58860e4a77..0b96a2ea1df 100644 --- a/config/Makefile.in +++ b/config/Makefile.in @@ -59,9 +59,6 @@ PLSRCS = nfspwd.pl revdepth.pl TARGETS = $(HOST_PROGRAM) $(PLSRCS:.pl=) $(SIMPLE_PROGRAMS) -# Generate the build number on the fly. -TARGETS += build_number nsBuildID.h - ifndef CROSS_COMPILE ifdef USE_ELF_DYNSTR_GC TARGETS += elf-dynstr-gc @@ -96,7 +93,6 @@ NSPR_CFLAGS += -I$(srcdir)/../nsprpub/pr/include/md endif HEADERS = \ - nsBuildID.h \ $(DEPTH)/mozilla-config.h \ $(srcdir)/nsStaticComponents.h \ $(NULL) @@ -123,35 +119,12 @@ export:: $(INSTALL) system_wrappers $(DIST)/include endif -# we don't use an explicit dependency here because then we would -# regenerate nsBuildID.h during the make install phase and that would -# be bad. install:: - @if test ! -f nsBuildID.h; then\ - echo "You must have done at least a make export before trying to do a make install."; \ - echo "(nsBuildID.h is missing.)"; \ - exit 1; \ - fi; $(SYSINSTALL) $(IFLAGS1) $(DEPTH)/mozilla-config.h $(DESTDIR)$(includedir) -GARBAGE += build_number nsBuildID \ +GARBAGE += \ $(FINAL_LINK_COMPS) $(FINAL_LINK_LIBS) $(FINAL_LINK_COMP_NAMES) -ifneq (,$(BUILD_OFFICIAL)$(MOZILLA_OFFICIAL)) -_BN_OFFICIAL=1 -else -_BN_OFFICIAL= -endif - -build_number: FORCE - $(PERL) -I$(srcdir) $(srcdir)/bdate.pl $@ $(_BN_OFFICIAL) - -nsBuildID.h: nsBuildID.h.in build_number $(srcdir)/milestone.txt Makefile - $(RM) $@ - MOZ_MILESTONE_RELEASE=$(MOZ_MILESTONE_RELEASE); \ - export MOZ_MILESTONE_RELEASE; \ - $(PERL) -I$(srcdir) $(srcdir)/aboutime.pl -m $(srcdir)/milestone.txt $@ build_number $(srcdir)/nsBuildID.h.in - ifndef CROSS_COMPILE ifdef USE_ELF_DYNSTR_GC elf-dynstr-gc: elf-dynstr-gc.c Makefile Makefile.in diff --git a/config/aboutime.pl b/config/aboutime.pl deleted file mode 100755 index cdf59a55c76..00000000000 --- a/config/aboutime.pl +++ /dev/null @@ -1,24 +0,0 @@ - -use strict; -use Getopt::Std; -require mozBDate; -require "Moz/Milestone.pm"; - -my $mfile; -getopts('m:'); -if (defined($::opt_m)) { - $mfile = $::opt_m; -} - -my $outfile = $ARGV[0]; -my $build_num_file = $ARGV[1]; -my $infile = ""; - -$infile = $ARGV[2] if ("$ARGV[2]" ne ""); - -if (defined($mfile)) { - my $milestone = &Moz::Milestone::getOfficialMilestone($mfile); - &mozBDate::SetMilestone($milestone); -} -&mozBDate::SubstituteBuildNumber($outfile, $build_num_file, $infile); - diff --git a/config/bdate.c b/config/bdate.c deleted file mode 100644 index b134ada8889..00000000000 --- a/config/bdate.c +++ /dev/null @@ -1,63 +0,0 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* ***** 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.org code. - * - * The Initial Developer of the Original Code is - * Netscape Communications Corporation. - * Portions created by the Initial Developer are Copyright (C) 1998 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * - * Alternatively, the contents of this file may be used under the terms of - * either of 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 ***** */ - -/* -** -** bdate.c: Possibly cross-platform date-based build number -** generator. Output is YYJJJ, where YY == 2-digit -** year, and JJJ is the Julian date (day of the year). -** -** Author: briano@netscape.com -** -*/ - -#include -#include - -#ifdef SUNOS4 -#include "sunos4.h" -#endif - -void main(void) -{ - time_t t = time(NULL); - struct tm *tms; - - tms = localtime(&t); - printf("500%02d%03d%02d\n", tms->tm_year, 1+tms->tm_yday, tms->tm_hour); - exit(0); -} diff --git a/config/bdate.pl b/config/bdate.pl deleted file mode 100755 index 45fac3aaf82..00000000000 --- a/config/bdate.pl +++ /dev/null @@ -1,44 +0,0 @@ -# -# ***** 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.org code. -# -# The Initial Developer of the Original Code is -# Netscape Communications Corporation. -# Portions created by the Initial Developer are Copyright (C) 1998 -# the Initial Developer. All Rights Reserved. -# -# Contributor(s): -# -# Alternatively, the contents of this file may be used under the terms of -# either of 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 ***** - -use mozBDate; - -# Both "generate" args are optional -$file = $ARGV[0] if ("$ARGV[0]" ne ""); -$official = 1 if ("$ARGV[1]" ne ""); -&mozBDate::UpdateBuildNumber($file, $official); - diff --git a/config/build-number.pl b/config/build-number.pl deleted file mode 100644 index b0fcb661941..00000000000 --- a/config/build-number.pl +++ /dev/null @@ -1,54 +0,0 @@ -# -# ***** 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 Communicator client code. -# -# The Initial Developer of the Original Code is -# Netscape Communications Corporation. -# Portions created by the Initial Developer are Copyright (C) 1998 -# the Initial Developer. All Rights Reserved. -# -# Contributor(s): -# -# Alternatively, the contents of this file may be used under the terms of -# either of 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 ***** - -my $progname = $0; -my $contents; - -# this script needs to be run in config -my $numberfile = "build_number"; - -# This is the preferences file that gets read and written. - -open(NUMBER, "<$numberfile") || die "no build_number file\n"; - -while ( ) { - $build_number = $_ -} -close (NUMBER); - -chop($build_number); - diff --git a/config/config.mk b/config/config.mk index a7cccba8bd4..4ceb49022ff 100644 --- a/config/config.mk +++ b/config/config.mk @@ -871,12 +871,6 @@ ifdef LOCALE_SRCDIR MAKE_JARS_FLAGS += -c $(LOCALE_SRCDIR) endif -# -# Add BUILD_ID to set of DEFINES -# -BUILD_ID := $(shell cat $(DEPTH)/config/build_number) -DEFINES += -DBUILD_ID=$(BUILD_ID) - ifeq (,$(filter WINCE WINNT OS2,$(OS_ARCH))) RUN_TEST_PROGRAM = $(DIST)/bin/run-mozilla.sh endif diff --git a/config/configobj.py b/config/configobj.py new file mode 100644 index 00000000000..f48638af21e --- /dev/null +++ b/config/configobj.py @@ -0,0 +1,2279 @@ +# configobj.py +# A config file reader/writer that supports nested sections in config files. +# Copyright (C) 2005-2006 Michael Foord, Nicola Larosa +# E-mail: fuzzyman AT voidspace DOT org DOT uk +# nico AT tekNico DOT net + +# ConfigObj 4 +# http://www.voidspace.org.uk/python/configobj.html + +# Released subject to the BSD License +# Please see http://www.voidspace.org.uk/python/license.shtml + +# Scripts maintained at http://www.voidspace.org.uk/python/index.shtml +# For information about bugfixes, updates and support, please join the +# ConfigObj mailing list: +# http://lists.sourceforge.net/lists/listinfo/configobj-develop +# Comments, suggestions and bug reports welcome. + +from __future__ import generators + +import sys +INTP_VER = sys.version_info[:2] +if INTP_VER < (2, 2): + raise RuntimeError("Python v.2.2 or later needed") + +import os, re +compiler = None +try: + import compiler +except ImportError: + # for IronPython + pass +from types import StringTypes +from warnings import warn +try: + from codecs import BOM_UTF8, BOM_UTF16, BOM_UTF16_BE, BOM_UTF16_LE +except ImportError: + # Python 2.2 does not have these + # UTF-8 + BOM_UTF8 = '\xef\xbb\xbf' + # UTF-16, little endian + BOM_UTF16_LE = '\xff\xfe' + # UTF-16, big endian + BOM_UTF16_BE = '\xfe\xff' + if sys.byteorder == 'little': + # UTF-16, native endianness + BOM_UTF16 = BOM_UTF16_LE + else: + # UTF-16, native endianness + BOM_UTF16 = BOM_UTF16_BE + +# A dictionary mapping BOM to +# the encoding to decode with, and what to set the +# encoding attribute to. +BOMS = { + BOM_UTF8: ('utf_8', None), + BOM_UTF16_BE: ('utf16_be', 'utf_16'), + BOM_UTF16_LE: ('utf16_le', 'utf_16'), + BOM_UTF16: ('utf_16', 'utf_16'), + } +# All legal variants of the BOM codecs. +# TODO: the list of aliases is not meant to be exhaustive, is there a +# better way ? +BOM_LIST = { + 'utf_16': 'utf_16', + 'u16': 'utf_16', + 'utf16': 'utf_16', + 'utf-16': 'utf_16', + 'utf16_be': 'utf16_be', + 'utf_16_be': 'utf16_be', + 'utf-16be': 'utf16_be', + 'utf16_le': 'utf16_le', + 'utf_16_le': 'utf16_le', + 'utf-16le': 'utf16_le', + 'utf_8': 'utf_8', + 'u8': 'utf_8', + 'utf': 'utf_8', + 'utf8': 'utf_8', + 'utf-8': 'utf_8', + } + +# Map of encodings to the BOM to write. +BOM_SET = { + 'utf_8': BOM_UTF8, + 'utf_16': BOM_UTF16, + 'utf16_be': BOM_UTF16_BE, + 'utf16_le': BOM_UTF16_LE, + None: BOM_UTF8 + } + +try: + from validate import VdtMissingValue +except ImportError: + VdtMissingValue = None + +try: + enumerate +except NameError: + def enumerate(obj): + """enumerate for Python 2.2.""" + i = -1 + for item in obj: + i += 1 + yield i, item + +try: + True, False +except NameError: + True, False = 1, 0 + + +__version__ = '4.4.0' + +__revision__ = '$Id: configobj.py,v 3.3 2007/06/26 16:35:02 benjamin%smedbergs.us Exp $' + +__docformat__ = "restructuredtext en" + +__all__ = ( + '__version__', + 'DEFAULT_INDENT_TYPE', + 'DEFAULT_INTERPOLATION', + 'ConfigObjError', + 'NestingError', + 'ParseError', + 'DuplicateError', + 'ConfigspecError', + 'ConfigObj', + 'SimpleVal', + 'InterpolationError', + 'InterpolationLoopError', + 'MissingInterpolationOption', + 'RepeatSectionError', + 'UnreprError', + 'UnknownType', + '__docformat__', + 'flatten_errors', +) + +DEFAULT_INTERPOLATION = 'configparser' +DEFAULT_INDENT_TYPE = ' ' +MAX_INTERPOL_DEPTH = 10 + +OPTION_DEFAULTS = { + 'interpolation': True, + 'raise_errors': False, + 'list_values': True, + 'create_empty': False, + 'file_error': False, + 'configspec': None, + 'stringify': True, + # option may be set to one of ('', ' ', '\t') + 'indent_type': None, + 'encoding': None, + 'default_encoding': None, + 'unrepr': False, + 'write_empty_values': False, +} + + +def getObj(s): + s = "a=" + s + if compiler is None: + raise ImportError('compiler module not available') + p = compiler.parse(s) + return p.getChildren()[1].getChildren()[0].getChildren()[1] + +class UnknownType(Exception): + pass + +class Builder: + + def build(self, o): + m = getattr(self, 'build_' + o.__class__.__name__, None) + if m is None: + raise UnknownType(o.__class__.__name__) + return m(o) + + def build_List(self, o): + return map(self.build, o.getChildren()) + + def build_Const(self, o): + return o.value + + def build_Dict(self, o): + d = {} + i = iter(map(self.build, o.getChildren())) + for el in i: + d[el] = i.next() + return d + + def build_Tuple(self, o): + return tuple(self.build_List(o)) + + def build_Name(self, o): + if o.name == 'None': + return None + if o.name == 'True': + return True + if o.name == 'False': + return False + + # An undefinted Name + raise UnknownType('Undefined Name') + + def build_Add(self, o): + real, imag = map(self.build_Const, o.getChildren()) + try: + real = float(real) + except TypeError: + raise UnknownType('Add') + if not isinstance(imag, complex) or imag.real != 0.0: + raise UnknownType('Add') + return real+imag + + def build_Getattr(self, o): + parent = self.build(o.expr) + return getattr(parent, o.attrname) + + def build_UnarySub(self, o): + return -self.build_Const(o.getChildren()[0]) + + def build_UnaryAdd(self, o): + return self.build_Const(o.getChildren()[0]) + +def unrepr(s): + if not s: + return s + return Builder().build(getObj(s)) + +def _splitlines(instring): + """Split a string on lines, without losing line endings or truncating.""" + + +class ConfigObjError(SyntaxError): + """ + This is the base class for all errors that ConfigObj raises. + It is a subclass of SyntaxError. + """ + def __init__(self, message='', line_number=None, line=''): + self.line = line + self.line_number = line_number + self.message = message + SyntaxError.__init__(self, message) + +class NestingError(ConfigObjError): + """ + This error indicates a level of nesting that doesn't match. + """ + +class ParseError(ConfigObjError): + """ + This error indicates that a line is badly written. + It is neither a valid ``key = value`` line, + nor a valid section marker line. + """ + +class DuplicateError(ConfigObjError): + """ + The keyword or section specified already exists. + """ + +class ConfigspecError(ConfigObjError): + """ + An error occured whilst parsing a configspec. + """ + +class InterpolationError(ConfigObjError): + """Base class for the two interpolation errors.""" + +class InterpolationLoopError(InterpolationError): + """Maximum interpolation depth exceeded in string interpolation.""" + + def __init__(self, option): + InterpolationError.__init__( + self, + 'interpolation loop detected in value "%s".' % option) + +class RepeatSectionError(ConfigObjError): + """ + This error indicates additional sections in a section with a + ``__many__`` (repeated) section. + """ + +class MissingInterpolationOption(InterpolationError): + """A value specified for interpolation was missing.""" + + def __init__(self, option): + InterpolationError.__init__( + self, + 'missing option "%s" in interpolation.' % option) + +class UnreprError(ConfigObjError): + """An error parsing in unrepr mode.""" + + +class InterpolationEngine(object): + """ + A helper class to help perform string interpolation. + + This class is an abstract base class; its descendants perform + the actual work. + """ + + # compiled regexp to use in self.interpolate() + _KEYCRE = re.compile(r"%\(([^)]*)\)s") + + def __init__(self, section): + # the Section instance that "owns" this engine + self.section = section + + def interpolate(self, key, value): + def recursive_interpolate(key, value, section, backtrail): + """The function that does the actual work. + + ``value``: the string we're trying to interpolate. + ``section``: the section in which that string was found + ``backtrail``: a dict to keep track of where we've been, + to detect and prevent infinite recursion loops + + This is similar to a depth-first-search algorithm. + """ + # Have we been here already? + if backtrail.has_key((key, section.name)): + # Yes - infinite loop detected + raise InterpolationLoopError(key) + # Place a marker on our backtrail so we won't come back here again + backtrail[(key, section.name)] = 1 + + # Now start the actual work + match = self._KEYCRE.search(value) + while match: + # The actual parsing of the match is implementation-dependent, + # so delegate to our helper function + k, v, s = self._parse_match(match) + if k is None: + # That's the signal that no further interpolation is needed + replacement = v + else: + # Further interpolation may be needed to obtain final value + replacement = recursive_interpolate(k, v, s, backtrail) + # Replace the matched string with its final value + start, end = match.span() + value = ''.join((value[:start], replacement, value[end:])) + new_search_start = start + len(replacement) + # Pick up the next interpolation key, if any, for next time + # through the while loop + match = self._KEYCRE.search(value, new_search_start) + + # Now safe to come back here again; remove marker from backtrail + del backtrail[(key, section.name)] + + return value + + # Back in interpolate(), all we have to do is kick off the recursive + # function with appropriate starting values + value = recursive_interpolate(key, value, self.section, {}) + return value + + def _fetch(self, key): + """Helper function to fetch values from owning section. + + Returns a 2-tuple: the value, and the section where it was found. + """ + # switch off interpolation before we try and fetch anything ! + save_interp = self.section.main.interpolation + self.section.main.interpolation = False + + # Start at section that "owns" this InterpolationEngine + current_section = self.section + while True: + # try the current section first + val = current_section.get(key) + if val is not None: + break + # try "DEFAULT" next + val = current_section.get('DEFAULT', {}).get(key) + if val is not None: + break + # move up to parent and try again + # top-level's parent is itself + if current_section.parent is current_section: + # reached top level, time to give up + break + current_section = current_section.parent + + # restore interpolation to previous value before returning + self.section.main.interpolation = save_interp + if val is None: + raise MissingInterpolationOption(key) + return val, current_section + + def _parse_match(self, match): + """Implementation-dependent helper function. + + Will be passed a match object corresponding to the interpolation + key we just found (e.g., "%(foo)s" or "$foo"). Should look up that + key in the appropriate config file section (using the ``_fetch()`` + helper function) and return a 3-tuple: (key, value, section) + + ``key`` is the name of the key we're looking for + ``value`` is the value found for that key + ``section`` is a reference to the section where it was found + + ``key`` and ``section`` should be None if no further + interpolation should be performed on the resulting value + (e.g., if we interpolated "$$" and returned "$"). + """ + raise NotImplementedError + + +class ConfigParserInterpolation(InterpolationEngine): + """Behaves like ConfigParser.""" + _KEYCRE = re.compile(r"%\(([^)]*)\)s") + + def _parse_match(self, match): + key = match.group(1) + value, section = self._fetch(key) + return key, value, section + + +class TemplateInterpolation(InterpolationEngine): + """Behaves like string.Template.""" + _delimiter = '$' + _KEYCRE = re.compile(r""" + \$(?: + (?P\$) | # Two $ signs + (?P[_a-z][_a-z0-9]*) | # $name format + {(?P[^}]*)} # ${name} format + ) + """, re.IGNORECASE | re.VERBOSE) + + def _parse_match(self, match): + # Valid name (in or out of braces): fetch value from section + key = match.group('named') or match.group('braced') + if key is not None: + value, section = self._fetch(key) + return key, value, section + # Escaped delimiter (e.g., $$): return single delimiter + if match.group('escaped') is not None: + # Return None for key and section to indicate it's time to stop + return None, self._delimiter, None + # Anything else: ignore completely, just return it unchanged + return None, match.group(), None + +interpolation_engines = { + 'configparser': ConfigParserInterpolation, + 'template': TemplateInterpolation, +} + +class Section(dict): + """ + A dictionary-like object that represents a section in a config file. + + It does string interpolation if the 'interpolation' attribute + of the 'main' object is set to True. + + Interpolation is tried first from this object, then from the 'DEFAULT' + section of this object, next from the parent and its 'DEFAULT' section, + and so on until the main object is reached. + + A Section will behave like an ordered dictionary - following the + order of the ``scalars`` and ``sections`` attributes. + You can use this to change the order of members. + + Iteration follows the order: scalars, then sections. + """ + + def __init__(self, parent, depth, main, indict=None, name=None): + """ + * parent is the section above + * depth is the depth level of this section + * main is the main ConfigObj + * indict is a dictionary to initialise the section with + """ + if indict is None: + indict = {} + dict.__init__(self) + # used for nesting level *and* interpolation + self.parent = parent + # used for the interpolation attribute + self.main = main + # level of nesting depth of this Section + self.depth = depth + # the sequence of scalar values in this Section + self.scalars = [] + # the sequence of sections in this Section + self.sections = [] + # purely for information + self.name = name + # for comments :-) + self.comments = {} + self.inline_comments = {} + # for the configspec + self.configspec = {} + self._order = [] + self._configspec_comments = {} + self._configspec_inline_comments = {} + self._cs_section_comments = {} + self._cs_section_inline_comments = {} + # for defaults + self.defaults = [] + # + # we do this explicitly so that __setitem__ is used properly + # (rather than just passing to ``dict.__init__``) + for entry in indict: + self[entry] = indict[entry] + + def _interpolate(self, key, value): + try: + # do we already have an interpolation engine? + engine = self._interpolation_engine + except AttributeError: + # not yet: first time running _interpolate(), so pick the engine + name = self.main.interpolation + if name == True: # note that "if name:" would be incorrect here + # backwards-compatibility: interpolation=True means use default + name = DEFAULT_INTERPOLATION + name = name.lower() # so that "Template", "template", etc. all work + class_ = interpolation_engines.get(name, None) + if class_ is None: + # invalid value for self.main.interpolation + self.main.interpolation = False + return value + else: + # save reference to engine so we don't have to do this again + engine = self._interpolation_engine = class_(self) + # let the engine do the actual work + return engine.interpolate(key, value) + + def __getitem__(self, key): + """Fetch the item and do string interpolation.""" + val = dict.__getitem__(self, key) + if self.main.interpolation and isinstance(val, StringTypes): + return self._interpolate(key, val) + return val + + def __setitem__(self, key, value, unrepr=False): + """ + Correctly set a value. + + Making dictionary values Section instances. + (We have to special case 'Section' instances - which are also dicts) + + Keys must be strings. + Values need only be strings (or lists of strings) if + ``main.stringify`` is set. + + `unrepr`` must be set when setting a value to a dictionary, without + creating a new sub-section. + """ + if not isinstance(key, StringTypes): + raise ValueError, 'The key "%s" is not a string.' % key + # add the comment + if not self.comments.has_key(key): + self.comments[key] = [] + self.inline_comments[key] = '' + # remove the entry from defaults + if key in self.defaults: + self.defaults.remove(key) + # + if isinstance(value, Section): + if not self.has_key(key): + self.sections.append(key) + dict.__setitem__(self, key, value) + elif isinstance(value, dict) and not unrepr: + # First create the new depth level, + # then create the section + if not self.has_key(key): + self.sections.append(key) + new_depth = self.depth + 1 + dict.__setitem__( + self, + key, + Section( + self, + new_depth, + self.main, + indict=value, + name=key)) + else: + if not self.has_key(key): + self.scalars.append(key) + if not self.main.stringify: + if isinstance(value, StringTypes): + pass + elif isinstance(value, (list, tuple)): + for entry in value: + if not isinstance(entry, StringTypes): + raise TypeError, ( + 'Value is not a string "%s".' % entry) + else: + raise TypeError, 'Value is not a string "%s".' % value + dict.__setitem__(self, key, value) + + def __delitem__(self, key): + """Remove items from the sequence when deleting.""" + dict. __delitem__(self, key) + if key in self.scalars: + self.scalars.remove(key) + else: + self.sections.remove(key) + del self.comments[key] + del self.inline_comments[key] + + def get(self, key, default=None): + """A version of ``get`` that doesn't bypass string interpolation.""" + try: + return self[key] + except KeyError: + return default + + def update(self, indict): + """ + A version of update that uses our ``__setitem__``. + """ + for entry in indict: + self[entry] = indict[entry] + + def pop(self, key, *args): + """ """ + val = dict.pop(self, key, *args) + if key in self.scalars: + del self.comments[key] + del self.inline_comments[key] + self.scalars.remove(key) + elif key in self.sections: + del self.comments[key] + del self.inline_comments[key] + self.sections.remove(key) + if self.main.interpolation and isinstance(val, StringTypes): + return self._interpolate(key, val) + return val + + def popitem(self): + """Pops the first (key,val)""" + sequence = (self.scalars + self.sections) + if not sequence: + raise KeyError, ": 'popitem(): dictionary is empty'" + key = sequence[0] + val = self[key] + del self[key] + return key, val + + def clear(self): + """ + A version of clear that also affects scalars/sections + Also clears comments and configspec. + + Leaves other attributes alone : + depth/main/parent are not affected + """ + dict.clear(self) + self.scalars = [] + self.sections = [] + self.comments = {} + self.inline_comments = {} + self.configspec = {} + + def setdefault(self, key, default=None): + """A version of setdefault that sets sequence if appropriate.""" + try: + return self[key] + except KeyError: + self[key] = default + return self[key] + + def items(self): + """ """ + return zip((self.scalars + self.sections), self.values()) + + def keys(self): + """ """ + return (self.scalars + self.sections) + + def values(self): + """ """ + return [self[key] for key in (self.scalars + self.sections)] + + def iteritems(self): + """ """ + return iter(self.items()) + + def iterkeys(self): + """ """ + return iter((self.scalars + self.sections)) + + __iter__ = iterkeys + + def itervalues(self): + """ """ + return iter(self.values()) + + def __repr__(self): + return '{%s}' % ', '.join([('%s: %s' % (repr(key), repr(self[key]))) + for key in (self.scalars + self.sections)]) + + __str__ = __repr__ + + # Extra methods - not in a normal dictionary + + def dict(self): + """ + Return a deepcopy of self as a dictionary. + + All members that are ``Section`` instances are recursively turned to + ordinary dictionaries - by calling their ``dict`` method. + + >>> n = a.dict() + >>> n == a + 1 + >>> n is a + 0 + """ + newdict = {} + for entry in self: + this_entry = self[entry] + if isinstance(this_entry, Section): + this_entry = this_entry.dict() + elif isinstance(this_entry, list): + # create a copy rather than a reference + this_entry = list(this_entry) + elif isinstance(this_entry, tuple): + # create a copy rather than a reference + this_entry = tuple(this_entry) + newdict[entry] = this_entry + return newdict + + def merge(self, indict): + """ + A recursive update - useful for merging config files. + + >>> a = '''[section1] + ... option1 = True + ... [[subsection]] + ... more_options = False + ... # end of file'''.splitlines() + >>> b = '''# File is user.ini + ... [section1] + ... option1 = False + ... # end of file'''.splitlines() + >>> c1 = ConfigObj(b) + >>> c2 = ConfigObj(a) + >>> c2.merge(c1) + >>> c2 + {'section1': {'option1': 'False', 'subsection': {'more_options': 'False'}}} + """ + for key, val in indict.items(): + if (key in self and isinstance(self[key], dict) and + isinstance(val, dict)): + self[key].merge(val) + else: + self[key] = val + + def rename(self, oldkey, newkey): + """ + Change a keyname to another, without changing position in sequence. + + Implemented so that transformations can be made on keys, + as well as on values. (used by encode and decode) + + Also renames comments. + """ + if oldkey in self.scalars: + the_list = self.scalars + elif oldkey in self.sections: + the_list = self.sections + else: + raise KeyError, 'Key "%s" not found.' % oldkey + pos = the_list.index(oldkey) + # + val = self[oldkey] + dict.__delitem__(self, oldkey) + dict.__setitem__(self, newkey, val) + the_list.remove(oldkey) + the_list.insert(pos, newkey) + comm = self.comments[oldkey] + inline_comment = self.inline_comments[oldkey] + del self.comments[oldkey] + del self.inline_comments[oldkey] + self.comments[newkey] = comm + self.inline_comments[newkey] = inline_comment + + def walk(self, function, raise_errors=True, + call_on_sections=False, **keywargs): + """ + Walk every member and call a function on the keyword and value. + + Return a dictionary of the return values + + If the function raises an exception, raise the errror + unless ``raise_errors=False``, in which case set the return value to + ``False``. + + Any unrecognised keyword arguments you pass to walk, will be pased on + to the function you pass in. + + Note: if ``call_on_sections`` is ``True`` then - on encountering a + subsection, *first* the function is called for the *whole* subsection, + and then recurses into it's members. This means your function must be + able to handle strings, dictionaries and lists. This allows you + to change the key of subsections as well as for ordinary members. The + return value when called on the whole subsection has to be discarded. + + See the encode and decode methods for examples, including functions. + + .. caution:: + + You can use ``walk`` to transform the names of members of a section + but you mustn't add or delete members. + + >>> config = '''[XXXXsection] + ... XXXXkey = XXXXvalue'''.splitlines() + >>> cfg = ConfigObj(config) + >>> cfg + {'XXXXsection': {'XXXXkey': 'XXXXvalue'}} + >>> def transform(section, key): + ... val = section[key] + ... newkey = key.replace('XXXX', 'CLIENT1') + ... section.rename(key, newkey) + ... if isinstance(val, (tuple, list, dict)): + ... pass + ... else: + ... val = val.replace('XXXX', 'CLIENT1') + ... section[newkey] = val + >>> cfg.walk(transform, call_on_sections=True) + {'CLIENT1section': {'CLIENT1key': None}} + >>> cfg + {'CLIENT1section': {'CLIENT1key': 'CLIENT1value'}} + """ + out = {} + # scalars first + for i in range(len(self.scalars)): + entry = self.scalars[i] + try: + val = function(self, entry, **keywargs) + # bound again in case name has changed + entry = self.scalars[i] + out[entry] = val + except Exception: + if raise_errors: + raise + else: + entry = self.scalars[i] + out[entry] = False + # then sections + for i in range(len(self.sections)): + entry = self.sections[i] + if call_on_sections: + try: + function(self, entry, **keywargs) + except Exception: + if raise_errors: + raise + else: + entry = self.sections[i] + out[entry] = False + # bound again in case name has changed + entry = self.sections[i] + # previous result is discarded + out[entry] = self[entry].walk( + function, + raise_errors=raise_errors, + call_on_sections=call_on_sections, + **keywargs) + return out + + def decode(self, encoding): + """ + Decode all strings and values to unicode, using the specified encoding. + + Works with subsections and list values. + + Uses the ``walk`` method. + + Testing ``encode`` and ``decode``. + >>> m = ConfigObj(a) + >>> m.decode('ascii') + >>> def testuni(val): + ... for entry in val: + ... if not isinstance(entry, unicode): + ... print >> sys.stderr, type(entry) + ... raise AssertionError, 'decode failed.' + ... if isinstance(val[entry], dict): + ... testuni(val[entry]) + ... elif not isinstance(val[entry], unicode): + ... raise AssertionError, 'decode failed.' + >>> testuni(m) + >>> m.encode('ascii') + >>> a == m + 1 + """ + warn('use of ``decode`` is deprecated.', DeprecationWarning) + def decode(section, key, encoding=encoding, warn=True): + """ """ + val = section[key] + if isinstance(val, (list, tuple)): + newval = [] + for entry in val: + newval.append(entry.decode(encoding)) + elif isinstance(val, dict): + newval = val + else: + newval = val.decode(encoding) + newkey = key.decode(encoding) + section.rename(key, newkey) + section[newkey] = newval + # using ``call_on_sections`` allows us to modify section names + self.walk(decode, call_on_sections=True) + + def encode(self, encoding): + """ + Encode all strings and values from unicode, + using the specified encoding. + + Works with subsections and list values. + Uses the ``walk`` method. + """ + warn('use of ``encode`` is deprecated.', DeprecationWarning) + def encode(section, key, encoding=encoding): + """ """ + val = section[key] + if isinstance(val, (list, tuple)): + newval = [] + for entry in val: + newval.append(entry.encode(encoding)) + elif isinstance(val, dict): + newval = val + else: + newval = val.encode(encoding) + newkey = key.encode(encoding) + section.rename(key, newkey) + section[newkey] = newval + self.walk(encode, call_on_sections=True) + + def istrue(self, key): + """A deprecated version of ``as_bool``.""" + warn('use of ``istrue`` is deprecated. Use ``as_bool`` method ' + 'instead.', DeprecationWarning) + return self.as_bool(key) + + def as_bool(self, key): + """ + Accepts a key as input. The corresponding value must be a string or + the objects (``True`` or 1) or (``False`` or 0). We allow 0 and 1 to + retain compatibility with Python 2.2. + + If the string is one of ``True``, ``On``, ``Yes``, or ``1`` it returns + ``True``. + + If the string is one of ``False``, ``Off``, ``No``, or ``0`` it returns + ``False``. + + ``as_bool`` is not case sensitive. + + Any other input will raise a ``ValueError``. + + >>> a = ConfigObj() + >>> a['a'] = 'fish' + >>> a.as_bool('a') + Traceback (most recent call last): + ValueError: Value "fish" is neither True nor False + >>> a['b'] = 'True' + >>> a.as_bool('b') + 1 + >>> a['b'] = 'off' + >>> a.as_bool('b') + 0 + """ + val = self[key] + if val == True: + return True + elif val == False: + return False + else: + try: + if not isinstance(val, StringTypes): + raise KeyError + else: + return self.main._bools[val.lower()] + except KeyError: + raise ValueError('Value "%s" is neither True nor False' % val) + + def as_int(self, key): + """ + A convenience method which coerces the specified value to an integer. + + If the value is an invalid literal for ``int``, a ``ValueError`` will + be raised. + + >>> a = ConfigObj() + >>> a['a'] = 'fish' + >>> a.as_int('a') + Traceback (most recent call last): + ValueError: invalid literal for int(): fish + >>> a['b'] = '1' + >>> a.as_int('b') + 1 + >>> a['b'] = '3.2' + >>> a.as_int('b') + Traceback (most recent call last): + ValueError: invalid literal for int(): 3.2 + """ + return int(self[key]) + + def as_float(self, key): + """ + A convenience method which coerces the specified value to a float. + + If the value is an invalid literal for ``float``, a ``ValueError`` will + be raised. + + >>> a = ConfigObj() + >>> a['a'] = 'fish' + >>> a.as_float('a') + Traceback (most recent call last): + ValueError: invalid literal for float(): fish + >>> a['b'] = '1' + >>> a.as_float('b') + 1.0 + >>> a['b'] = '3.2' + >>> a.as_float('b') + 3.2000000000000002 + """ + return float(self[key]) + + +class ConfigObj(Section): + """An object to read, create, and write config files.""" + + _keyword = re.compile(r'''^ # line start + (\s*) # indentation + ( # keyword + (?:".*?")| # double quotes + (?:'.*?')| # single quotes + (?:[^'"=].*?) # no quotes + ) + \s*=\s* # divider + (.*) # value (including list values and comments) + $ # line end + ''', + re.VERBOSE) + + _sectionmarker = re.compile(r'''^ + (\s*) # 1: indentation + ((?:\[\s*)+) # 2: section marker open + ( # 3: section name open + (?:"\s*\S.*?\s*")| # at least one non-space with double quotes + (?:'\s*\S.*?\s*')| # at least one non-space with single quotes + (?:[^'"\s].*?) # at least one non-space unquoted + ) # section name close + ((?:\s*\])+) # 4: section marker close + \s*(\#.*)? # 5: optional comment + $''', + re.VERBOSE) + + # this regexp pulls list values out as a single string + # or single values and comments + # FIXME: this regex adds a '' to the end of comma terminated lists + # workaround in ``_handle_value`` + _valueexp = re.compile(r'''^ + (?: + (?: + ( + (?: + (?: + (?:".*?")| # double quotes + (?:'.*?')| # single quotes + (?:[^'",\#][^,\#]*?) # unquoted + ) + \s*,\s* # comma + )* # match all list items ending in a comma (if any) + ) + ( + (?:".*?")| # double quotes + (?:'.*?')| # single quotes + (?:[^'",\#\s][^,]*?)| # unquoted + (?:(? 1: + msg = ("Parsing failed with several errors.\nFirst error %s" % + info) + error = ConfigObjError(msg) + else: + error = self._errors[0] + # set the errors attribute; it's a list of tuples: + # (error_type, message, line_number) + error.errors = self._errors + # set the config attribute + error.config = self + raise error + # delete private attributes + del self._errors + # + if defaults['configspec'] is None: + self.configspec = None + else: + self._handle_configspec(defaults['configspec']) + + def __repr__(self): + return 'ConfigObj({%s})' % ', '.join( + [('%s: %s' % (repr(key), repr(self[key]))) for key in + (self.scalars + self.sections)]) + + def _handle_bom(self, infile): + """ + Handle any BOM, and decode if necessary. + + If an encoding is specified, that *must* be used - but the BOM should + still be removed (and the BOM attribute set). + + (If the encoding is wrongly specified, then a BOM for an alternative + encoding won't be discovered or removed.) + + If an encoding is not specified, UTF8 or UTF16 BOM will be detected and + removed. The BOM attribute will be set. UTF16 will be decoded to + unicode. + + NOTE: This method must not be called with an empty ``infile``. + + Specifying the *wrong* encoding is likely to cause a + ``UnicodeDecodeError``. + + ``infile`` must always be returned as a list of lines, but may be + passed in as a single string. + """ + if ((self.encoding is not None) and + (self.encoding.lower() not in BOM_LIST)): + # No need to check for a BOM + # the encoding specified doesn't have one + # just decode + return self._decode(infile, self.encoding) + # + if isinstance(infile, (list, tuple)): + line = infile[0] + else: + line = infile + if self.encoding is not None: + # encoding explicitly supplied + # And it could have an associated BOM + # TODO: if encoding is just UTF16 - we ought to check for both + # TODO: big endian and little endian versions. + enc = BOM_LIST[self.encoding.lower()] + if enc == 'utf_16': + # For UTF16 we try big endian and little endian + for BOM, (encoding, final_encoding) in BOMS.items(): + if not final_encoding: + # skip UTF8 + continue + if infile.startswith(BOM): + ### BOM discovered + ##self.BOM = True + # Don't need to remove BOM + return self._decode(infile, encoding) + # + # If we get this far, will *probably* raise a DecodeError + # As it doesn't appear to start with a BOM + return self._decode(infile, self.encoding) + # + # Must be UTF8 + BOM = BOM_SET[enc] + if not line.startswith(BOM): + return self._decode(infile, self.encoding) + # + newline = line[len(BOM):] + # + # BOM removed + if isinstance(infile, (list, tuple)): + infile[0] = newline + else: + infile = newline + self.BOM = True + return self._decode(infile, self.encoding) + # + # No encoding specified - so we need to check for UTF8/UTF16 + for BOM, (encoding, final_encoding) in BOMS.items(): + if not line.startswith(BOM): + continue + else: + # BOM discovered + self.encoding = final_encoding + if not final_encoding: + self.BOM = True + # UTF8 + # remove BOM + newline = line[len(BOM):] + if isinstance(infile, (list, tuple)): + infile[0] = newline + else: + infile = newline + # UTF8 - don't decode + if isinstance(infile, StringTypes): + return infile.splitlines(True) + else: + return infile + # UTF16 - have to decode + return self._decode(infile, encoding) + # + # No BOM discovered and no encoding specified, just return + if isinstance(infile, StringTypes): + # infile read from a file will be a single string + return infile.splitlines(True) + else: + return infile + + def _a_to_u(self, aString): + """Decode ASCII strings to unicode if a self.encoding is specified.""" + if self.encoding: + return aString.decode('ascii') + else: + return aString + + def _decode(self, infile, encoding): + """ + Decode infile to unicode. Using the specified encoding. + + if is a string, it also needs converting to a list. + """ + if isinstance(infile, StringTypes): + # can't be unicode + # NOTE: Could raise a ``UnicodeDecodeError`` + return infile.decode(encoding).splitlines(True) + for i, line in enumerate(infile): + if not isinstance(line, unicode): + # NOTE: The isinstance test here handles mixed lists of unicode/string + # NOTE: But the decode will break on any non-string values + # NOTE: Or could raise a ``UnicodeDecodeError`` + infile[i] = line.decode(encoding) + return infile + + def _decode_element(self, line): + """Decode element to unicode if necessary.""" + if not self.encoding: + return line + if isinstance(line, str) and self.default_encoding: + return line.decode(self.default_encoding) + return line + + def _str(self, value): + """ + Used by ``stringify`` within validate, to turn non-string values + into strings. + """ + if not isinstance(value, StringTypes): + return str(value) + else: + return value + + def _parse(self, infile): + """Actually parse the config file.""" + temp_list_values = self.list_values + if self.unrepr: + self.list_values = False + comment_list = [] + done_start = False + this_section = self + maxline = len(infile) - 1 + cur_index = -1 + reset_comment = False + while cur_index < maxline: + if reset_comment: + comment_list = [] + cur_index += 1 + line = infile[cur_index] + sline = line.strip() + # do we have anything on the line ? + if not sline or sline.startswith('#') or sline.startswith(';'): + reset_comment = False + comment_list.append(line) + continue + if not done_start: + # preserve initial comment + self.initial_comment = comment_list + comment_list = [] + done_start = True + reset_comment = True + # first we check if it's a section marker + mat = self._sectionmarker.match(line) + if mat is not None: + # is a section line + (indent, sect_open, sect_name, sect_close, comment) = ( + mat.groups()) + if indent and (self.indent_type is None): + self.indent_type = indent + cur_depth = sect_open.count('[') + if cur_depth != sect_close.count(']'): + self._handle_error( + "Cannot compute the section depth at line %s.", + NestingError, infile, cur_index) + continue + # + if cur_depth < this_section.depth: + # the new section is dropping back to a previous level + try: + parent = self._match_depth( + this_section, + cur_depth).parent + except SyntaxError: + self._handle_error( + "Cannot compute nesting level at line %s.", + NestingError, infile, cur_index) + continue + elif cur_depth == this_section.depth: + # the new section is a sibling of the current section + parent = this_section.parent + elif cur_depth == this_section.depth + 1: + # the new section is a child the current section + parent = this_section + else: + self._handle_error( + "Section too nested at line %s.", + NestingError, infile, cur_index) + # + sect_name = self._unquote(sect_name) + if parent.has_key(sect_name): + self._handle_error( + 'Duplicate section name at line %s.', + DuplicateError, infile, cur_index) + continue + # create the new section + this_section = Section( + parent, + cur_depth, + self, + name=sect_name) + parent[sect_name] = this_section + parent.inline_comments[sect_name] = comment + parent.comments[sect_name] = comment_list + continue + # + # it's not a section marker, + # so it should be a valid ``key = value`` line + mat = self._keyword.match(line) + if mat is None: + # it neither matched as a keyword + # or a section marker + self._handle_error( + 'Invalid line at line "%s".', + ParseError, infile, cur_index) + else: + # is a keyword value + # value will include any inline comment + (indent, key, value) = mat.groups() + if indent and (self.indent_type is None): + self.indent_type = indent + # check for a multiline value + if value[:3] in ['"""', "'''"]: + try: + (value, comment, cur_index) = self._multiline( + value, infile, cur_index, maxline) + except SyntaxError: + self._handle_error( + 'Parse error in value at line %s.', + ParseError, infile, cur_index) + continue + else: + if self.unrepr: + comment = '' + try: + value = unrepr(value) + except Exception, e: + if type(e) == UnknownType: + msg = 'Unknown name or type in value at line %s.' + else: + msg = 'Parse error in value at line %s.' + self._handle_error(msg, UnreprError, infile, + cur_index) + continue + else: + if self.unrepr: + comment = '' + try: + value = unrepr(value) + except Exception, e: + if isinstance(e, UnknownType): + msg = 'Unknown name or type in value at line %s.' + else: + msg = 'Parse error in value at line %s.' + self._handle_error(msg, UnreprError, infile, + cur_index) + continue + else: + # extract comment and lists + try: + (value, comment) = self._handle_value(value) + except SyntaxError: + self._handle_error( + 'Parse error in value at line %s.', + ParseError, infile, cur_index) + continue + # + key = self._unquote(key) + if this_section.has_key(key): + self._handle_error( + 'Duplicate keyword name at line %s.', + DuplicateError, infile, cur_index) + continue + # add the key. + # we set unrepr because if we have got this far we will never + # be creating a new section + this_section.__setitem__(key, value, unrepr=True) + this_section.inline_comments[key] = comment + this_section.comments[key] = comment_list + continue + # + if self.indent_type is None: + # no indentation used, set the type accordingly + self.indent_type = '' + # + if self._terminated: + comment_list.append('') + # preserve the final comment + if not self and not self.initial_comment: + self.initial_comment = comment_list + elif not reset_comment: + self.final_comment = comment_list + self.list_values = temp_list_values + + def _match_depth(self, sect, depth): + """ + Given a section and a depth level, walk back through the sections + parents to see if the depth level matches a previous section. + + Return a reference to the right section, + or raise a SyntaxError. + """ + while depth < sect.depth: + if sect is sect.parent: + # we've reached the top level already + raise SyntaxError + sect = sect.parent + if sect.depth == depth: + return sect + # shouldn't get here + raise SyntaxError + + def _handle_error(self, text, ErrorClass, infile, cur_index): + """ + Handle an error according to the error settings. + + Either raise the error or store it. + The error will have occured at ``cur_index`` + """ + line = infile[cur_index] + cur_index += 1 + message = text % cur_index + error = ErrorClass(message, cur_index, line) + if self.raise_errors: + # raise the error - parsing stops here + raise error + # store the error + # reraise when parsing has finished + self._errors.append(error) + + def _unquote(self, value): + """Return an unquoted version of a value""" + if (value[0] == value[-1]) and (value[0] in ('"', "'")): + value = value[1:-1] + return value + + def _quote(self, value, multiline=True): + """ + Return a safely quoted version of a value. + + Raise a ConfigObjError if the value cannot be safely quoted. + If multiline is ``True`` (default) then use triple quotes + if necessary. + + Don't quote values that don't need it. + Recursively quote members of a list and return a comma joined list. + Multiline is ``False`` for lists. + Obey list syntax for empty and single member lists. + + If ``list_values=False`` then the value is only quoted if it contains + a ``\n`` (is multiline). + + If ``write_empty_values`` is set, and the value is an empty string, it + won't be quoted. + """ + if multiline and self.write_empty_values and value == '': + # Only if multiline is set, so that it is used for values not + # keys, and not values that are part of a list + return '' + if multiline and isinstance(value, (list, tuple)): + if not value: + return ',' + elif len(value) == 1: + return self._quote(value[0], multiline=False) + ',' + return ', '.join([self._quote(val, multiline=False) + for val in value]) + if not isinstance(value, StringTypes): + if self.stringify: + value = str(value) + else: + raise TypeError, 'Value "%s" is not a string.' % value + squot = "'%s'" + dquot = '"%s"' + noquot = "%s" + wspace_plus = ' \r\t\n\v\t\'"' + tsquot = '"""%s"""' + tdquot = "'''%s'''" + if not value: + return '""' + if (not self.list_values and '\n' not in value) or not (multiline and + ((("'" in value) and ('"' in value)) or ('\n' in value))): + if not self.list_values: + # we don't quote if ``list_values=False`` + quot = noquot + # for normal values either single or double quotes will do + elif '\n' in value: + # will only happen if multiline is off - e.g. '\n' in key + raise ConfigObjError, ('Value "%s" cannot be safely quoted.' % + value) + elif ((value[0] not in wspace_plus) and + (value[-1] not in wspace_plus) and + (',' not in value)): + quot = noquot + else: + if ("'" in value) and ('"' in value): + raise ConfigObjError, ( + 'Value "%s" cannot be safely quoted.' % value) + elif '"' in value: + quot = squot + else: + quot = dquot + else: + # if value has '\n' or "'" *and* '"', it will need triple quotes + if (value.find('"""') != -1) and (value.find("'''") != -1): + raise ConfigObjError, ( + 'Value "%s" cannot be safely quoted.' % value) + if value.find('"""') == -1: + quot = tdquot + else: + quot = tsquot + return quot % value + + def _handle_value(self, value): + """ + Given a value string, unquote, remove comment, + handle lists. (including empty and single member lists) + """ + # do we look for lists in values ? + if not self.list_values: + mat = self._nolistvalue.match(value) + if mat is None: + raise SyntaxError + # NOTE: we don't unquote here + return mat.groups() + # + mat = self._valueexp.match(value) + if mat is None: + # the value is badly constructed, probably badly quoted, + # or an invalid list + raise SyntaxError + (list_values, single, empty_list, comment) = mat.groups() + if (list_values == '') and (single is None): + # change this if you want to accept empty values + raise SyntaxError + # NOTE: note there is no error handling from here if the regex + # is wrong: then incorrect values will slip through + if empty_list is not None: + # the single comma - meaning an empty list + return ([], comment) + if single is not None: + # handle empty values + if list_values and not single: + # FIXME: the '' is a workaround because our regex now matches + # '' at the end of a list if it has a trailing comma + single = None + else: + single = single or '""' + single = self._unquote(single) + if list_values == '': + # not a list value + return (single, comment) + the_list = self._listvalueexp.findall(list_values) + the_list = [self._unquote(val) for val in the_list] + if single is not None: + the_list += [single] + return (the_list, comment) + + def _multiline(self, value, infile, cur_index, maxline): + """Extract the value, where we are in a multiline situation.""" + quot = value[:3] + newvalue = value[3:] + single_line = self._triple_quote[quot][0] + multi_line = self._triple_quote[quot][1] + mat = single_line.match(value) + if mat is not None: + retval = list(mat.groups()) + retval.append(cur_index) + return retval + elif newvalue.find(quot) != -1: + # somehow the triple quote is missing + raise SyntaxError + # + while cur_index < maxline: + cur_index += 1 + newvalue += '\n' + line = infile[cur_index] + if line.find(quot) == -1: + newvalue += line + else: + # end of multiline, process it + break + else: + # we've got to the end of the config, oops... + raise SyntaxError + mat = multi_line.match(line) + if mat is None: + # a badly formed line + raise SyntaxError + (value, comment) = mat.groups() + return (newvalue + value, comment, cur_index) + + def _handle_configspec(self, configspec): + """Parse the configspec.""" + # FIXME: Should we check that the configspec was created with the + # correct settings ? (i.e. ``list_values=False``) + if not isinstance(configspec, ConfigObj): + try: + configspec = ConfigObj( + configspec, + raise_errors=True, + file_error=True, + list_values=False) + except ConfigObjError, e: + # FIXME: Should these errors have a reference + # to the already parsed ConfigObj ? + raise ConfigspecError('Parsing configspec failed: %s' % e) + except IOError, e: + raise IOError('Reading configspec failed: %s' % e) + self._set_configspec_value(configspec, self) + + def _set_configspec_value(self, configspec, section): + """Used to recursively set configspec values.""" + if '__many__' in configspec.sections: + section.configspec['__many__'] = configspec['__many__'] + if len(configspec.sections) > 1: + # FIXME: can we supply any useful information here ? + raise RepeatSectionError + if hasattr(configspec, 'initial_comment'): + section._configspec_initial_comment = configspec.initial_comment + section._configspec_final_comment = configspec.final_comment + section._configspec_encoding = configspec.encoding + section._configspec_BOM = configspec.BOM + section._configspec_newlines = configspec.newlines + section._configspec_indent_type = configspec.indent_type + for entry in configspec.scalars: + section._configspec_comments[entry] = configspec.comments[entry] + section._configspec_inline_comments[entry] = ( + configspec.inline_comments[entry]) + section.configspec[entry] = configspec[entry] + section._order.append(entry) + for entry in configspec.sections: + if entry == '__many__': + continue + section._cs_section_comments[entry] = configspec.comments[entry] + section._cs_section_inline_comments[entry] = ( + configspec.inline_comments[entry]) + if not section.has_key(entry): + section[entry] = {} + self._set_configspec_value(configspec[entry], section[entry]) + + def _handle_repeat(self, section, configspec): + """Dynamically assign configspec for repeated section.""" + try: + section_keys = configspec.sections + scalar_keys = configspec.scalars + except AttributeError: + section_keys = [entry for entry in configspec + if isinstance(configspec[entry], dict)] + scalar_keys = [entry for entry in configspec + if not isinstance(configspec[entry], dict)] + if '__many__' in section_keys and len(section_keys) > 1: + # FIXME: can we supply any useful information here ? + raise RepeatSectionError + scalars = {} + sections = {} + for entry in scalar_keys: + val = configspec[entry] + scalars[entry] = val + for entry in section_keys: + val = configspec[entry] + if entry == '__many__': + scalars[entry] = val + continue + sections[entry] = val + # + section.configspec = scalars + for entry in sections: + if not section.has_key(entry): + section[entry] = {} + self._handle_repeat(section[entry], sections[entry]) + + def _write_line(self, indent_string, entry, this_entry, comment): + """Write an individual line, for the write method""" + # NOTE: the calls to self._quote here handles non-StringType values. + if not self.unrepr: + val = self._decode_element(self._quote(this_entry)) + else: + val = repr(this_entry) + return '%s%s%s%s%s' % ( + indent_string, + self._decode_element(self._quote(entry, multiline=False)), + self._a_to_u(' = '), + val, + self._decode_element(comment)) + + def _write_marker(self, indent_string, depth, entry, comment): + """Write a section marker line""" + return '%s%s%s%s%s' % ( + indent_string, + self._a_to_u('[' * depth), + self._quote(self._decode_element(entry), multiline=False), + self._a_to_u(']' * depth), + self._decode_element(comment)) + + def _handle_comment(self, comment): + """Deal with a comment.""" + if not comment: + return '' + start = self.indent_type + if not comment.startswith('#'): + start += self._a_to_u(' # ') + return (start + comment) + + # Public methods + + def write(self, outfile=None, section=None): + """ + Write the current ConfigObj as a file + + tekNico: FIXME: use StringIO instead of real files + + >>> filename = a.filename + >>> a.filename = 'test.ini' + >>> a.write() + >>> a.filename = filename + >>> a == ConfigObj('test.ini', raise_errors=True) + 1 + """ + if self.indent_type is None: + # this can be true if initialised from a dictionary + self.indent_type = DEFAULT_INDENT_TYPE + # + out = [] + cs = self._a_to_u('#') + csp = self._a_to_u('# ') + if section is None: + int_val = self.interpolation + self.interpolation = False + section = self + for line in self.initial_comment: + line = self._decode_element(line) + stripped_line = line.strip() + if stripped_line and not stripped_line.startswith(cs): + line = csp + line + out.append(line) + # + indent_string = self.indent_type * section.depth + for entry in (section.scalars + section.sections): + if entry in section.defaults: + # don't write out default values + continue + for comment_line in section.comments[entry]: + comment_line = self._decode_element(comment_line.lstrip()) + if comment_line and not comment_line.startswith(cs): + comment_line = csp + comment_line + out.append(indent_string + comment_line) + this_entry = section[entry] + comment = self._handle_comment(section.inline_comments[entry]) + # + if isinstance(this_entry, dict): + # a section + out.append(self._write_marker( + indent_string, + this_entry.depth, + entry, + comment)) + out.extend(self.write(section=this_entry)) + else: + out.append(self._write_line( + indent_string, + entry, + this_entry, + comment)) + # + if section is self: + for line in self.final_comment: + line = self._decode_element(line) + stripped_line = line.strip() + if stripped_line and not stripped_line.startswith(cs): + line = csp + line + out.append(line) + self.interpolation = int_val + # + if section is not self: + return out + # + if (self.filename is None) and (outfile is None): + # output a list of lines + # might need to encode + # NOTE: This will *screw* UTF16, each line will start with the BOM + if self.encoding: + out = [l.encode(self.encoding) for l in out] + if (self.BOM and ((self.encoding is None) or + (BOM_LIST.get(self.encoding.lower()) == 'utf_8'))): + # Add the UTF8 BOM + if not out: + out.append('') + out[0] = BOM_UTF8 + out[0] + return out + # + # Turn the list to a string, joined with correct newlines + output = (self._a_to_u(self.newlines or os.linesep) + ).join(out) + if self.encoding: + output = output.encode(self.encoding) + if (self.BOM and ((self.encoding is None) or + (BOM_LIST.get(self.encoding.lower()) == 'utf_8'))): + # Add the UTF8 BOM + output = BOM_UTF8 + output + if outfile is not None: + outfile.write(output) + else: + h = open(self.filename, 'wb') + h.write(output) + h.close() + + def validate(self, validator, preserve_errors=False, copy=False, + section=None): + """ + Test the ConfigObj against a configspec. + + It uses the ``validator`` object from *validate.py*. + + To run ``validate`` on the current ConfigObj, call: :: + + test = config.validate(validator) + + (Normally having previously passed in the configspec when the ConfigObj + was created - you can dynamically assign a dictionary of checks to the + ``configspec`` attribute of a section though). + + It returns ``True`` if everything passes, or a dictionary of + pass/fails (True/False). If every member of a subsection passes, it + will just have the value ``True``. (It also returns ``False`` if all + members fail). + + In addition, it converts the values from strings to their native + types if their checks pass (and ``stringify`` is set). + + If ``preserve_errors`` is ``True`` (``False`` is default) then instead + of a marking a fail with a ``False``, it will preserve the actual + exception object. This can contain info about the reason for failure. + For example the ``VdtValueTooSmallError`` indeicates that the value + supplied was too small. If a value (or section) is missing it will + still be marked as ``False``. + + You must have the validate module to use ``preserve_errors=True``. + + You can then use the ``flatten_errors`` function to turn your nested + results dictionary into a flattened list of failures - useful for + displaying meaningful error messages. + """ + if section is None: + if self.configspec is None: + raise ValueError, 'No configspec supplied.' + if preserve_errors: + if VdtMissingValue is None: + raise ImportError('Missing validate module.') + section = self + # + spec_section = section.configspec + if copy and hasattr(section, '_configspec_initial_comment'): + section.initial_comment = section._configspec_initial_comment + section.final_comment = section._configspec_final_comment + section.encoding = section._configspec_encoding + section.BOM = section._configspec_BOM + section.newlines = section._configspec_newlines + section.indent_type = section._configspec_indent_type + if '__many__' in section.configspec: + many = spec_section['__many__'] + # dynamically assign the configspecs + # for the sections below + for entry in section.sections: + self._handle_repeat(section[entry], many) + # + out = {} + ret_true = True + ret_false = True + order = [k for k in section._order if k in spec_section] + order += [k for k in spec_section if k not in order] + for entry in order: + if entry == '__many__': + continue + if (not entry in section.scalars) or (entry in section.defaults): + # missing entries + # or entries from defaults + missing = True + val = None + if copy and not entry in section.scalars: + # copy comments + section.comments[entry] = ( + section._configspec_comments.get(entry, [])) + section.inline_comments[entry] = ( + section._configspec_inline_comments.get(entry, '')) + # + else: + missing = False + val = section[entry] + try: + check = validator.check(spec_section[entry], + val, + missing=missing + ) + except validator.baseErrorClass, e: + if not preserve_errors or isinstance(e, VdtMissingValue): + out[entry] = False + else: + # preserve the error + out[entry] = e + ret_false = False + ret_true = False + else: + ret_false = False + out[entry] = True + if self.stringify or missing: + # if we are doing type conversion + # or the value is a supplied default + if not self.stringify: + if isinstance(check, (list, tuple)): + # preserve lists + check = [self._str(item) for item in check] + elif missing and check is None: + # convert the None from a default to a '' + check = '' + else: + check = self._str(check) + if (check != val) or missing: + section[entry] = check + if not copy and missing and entry not in section.defaults: + section.defaults.append(entry) + # + # Missing sections will have been created as empty ones when the + # configspec was read. + for entry in section.sections: + # FIXME: this means DEFAULT is not copied in copy mode + if section is self and entry == 'DEFAULT': + continue + if copy: + section.comments[entry] = section._cs_section_comments[entry] + section.inline_comments[entry] = ( + section._cs_section_inline_comments[entry]) + check = self.validate(validator, preserve_errors=preserve_errors, + copy=copy, section=section[entry]) + out[entry] = check + if check == False: + ret_true = False + elif check == True: + ret_false = False + else: + ret_true = False + ret_false = False + # + if ret_true: + return True + elif ret_false: + return False + else: + return out + +class SimpleVal(object): + """ + A simple validator. + Can be used to check that all members expected are present. + + To use it, provide a configspec with all your members in (the value given + will be ignored). Pass an instance of ``SimpleVal`` to the ``validate`` + method of your ``ConfigObj``. ``validate`` will return ``True`` if all + members are present, or a dictionary with True/False meaning + present/missing. (Whole missing sections will be replaced with ``False``) + """ + + def __init__(self): + self.baseErrorClass = ConfigObjError + + def check(self, check, member, missing=False): + """A dummy check method, always returns the value unchanged.""" + if missing: + raise self.baseErrorClass + return member + +# Check / processing functions for options +def flatten_errors(cfg, res, levels=None, results=None): + """ + An example function that will turn a nested dictionary of results + (as returned by ``ConfigObj.validate``) into a flat list. + + ``cfg`` is the ConfigObj instance being checked, ``res`` is the results + dictionary returned by ``validate``. + + (This is a recursive function, so you shouldn't use the ``levels`` or + ``results`` arguments - they are used by the function. + + Returns a list of keys that failed. Each member of the list is a tuple : + :: + + ([list of sections...], key, result) + + If ``validate`` was called with ``preserve_errors=False`` (the default) + then ``result`` will always be ``False``. + + *list of sections* is a flattened list of sections that the key was found + in. + + If the section was missing then key will be ``None``. + + If the value (or section) was missing then ``result`` will be ``False``. + + If ``validate`` was called with ``preserve_errors=True`` and a value + was present, but failed the check, then ``result`` will be the exception + object returned. You can use this as a string that describes the failure. + + For example *The value "3" is of the wrong type*. + + >>> import validate + >>> vtor = validate.Validator() + >>> my_ini = ''' + ... option1 = True + ... [section1] + ... option1 = True + ... [section2] + ... another_option = Probably + ... [section3] + ... another_option = True + ... [[section3b]] + ... value = 3 + ... value2 = a + ... value3 = 11 + ... ''' + >>> my_cfg = ''' + ... option1 = boolean() + ... option2 = boolean() + ... option3 = boolean(default=Bad_value) + ... [section1] + ... option1 = boolean() + ... option2 = boolean() + ... option3 = boolean(default=Bad_value) + ... [section2] + ... another_option = boolean() + ... [section3] + ... another_option = boolean() + ... [[section3b]] + ... value = integer + ... value2 = integer + ... value3 = integer(0, 10) + ... [[[section3b-sub]]] + ... value = string + ... [section4] + ... another_option = boolean() + ... ''' + >>> cs = my_cfg.split('\\n') + >>> ini = my_ini.split('\\n') + >>> cfg = ConfigObj(ini, configspec=cs) + >>> res = cfg.validate(vtor, preserve_errors=True) + >>> errors = [] + >>> for entry in flatten_errors(cfg, res): + ... section_list, key, error = entry + ... section_list.insert(0, '[root]') + ... if key is not None: + ... section_list.append(key) + ... else: + ... section_list.append('[missing]') + ... section_string = ', '.join(section_list) + ... errors.append((section_string, ' = ', error)) + >>> errors.sort() + >>> for entry in errors: + ... print entry[0], entry[1], (entry[2] or 0) + [root], option2 = 0 + [root], option3 = the value "Bad_value" is of the wrong type. + [root], section1, option2 = 0 + [root], section1, option3 = the value "Bad_value" is of the wrong type. + [root], section2, another_option = the value "Probably" is of the wrong type. + [root], section3, section3b, section3b-sub, [missing] = 0 + [root], section3, section3b, value2 = the value "a" is of the wrong type. + [root], section3, section3b, value3 = the value "11" is too big. + [root], section4, [missing] = 0 + """ + if levels is None: + # first time called + levels = [] + results = [] + if res is True: + return results + if res is False: + results.append((levels[:], None, False)) + if levels: + levels.pop() + return results + for (key, val) in res.items(): + if val == True: + continue + if isinstance(cfg.get(key), dict): + # Go down one level + levels.append(key) + flatten_errors(cfg[key], val, levels, results) + continue + results.append((levels[:], key, val)) + # + # Go up one level + if levels: + levels.pop() + # + return results + +"""*A programming language is a medium of expression.* - Paul Graham""" diff --git a/config/mozBDate.pm b/config/mozBDate.pm deleted file mode 100755 index 2418f43b8e5..00000000000 --- a/config/mozBDate.pm +++ /dev/null @@ -1,172 +0,0 @@ -# -# ***** 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.org code. -# -# The Initial Developer of the Original Code is -# Netscape Communications Corporation. -# Portions created by the Initial Developer are Copyright (C) 1998-2000 -# the Initial Developer. All Rights Reserved. -# -# Contributor(s): -# -# Alternatively, the contents of this file may be used under the terms of -# either of 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 ***** - -package mozBDate; - -use strict; -use IO::File; - -BEGIN { - use Exporter (); - use vars qw ($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS $milestone); - - $VERSION = 1.00; - @ISA = qw(Exporter); - @EXPORT = qw(&UpdateBuildNumber &SubstituteBuildNumber &SetMilestone); - %EXPORT_TAGS = ( ); - @EXPORT_OK = qw(); -} - -local $mozBDate::milestone = "0.0"; - -sub write_number($) { - my ($file, $num) = @_; - unlink($file); - open(OUT, ">$file") || die "$file: $!\n"; - print OUT "$num\n"; - close(OUT); -} - -sub UpdateBuildNumber($$) { - - my ($outfile, $official) = @_; - my $given_date = $ENV{"MOZ_BUILD_DATE"}; - my $build_number; - - if ($given_date eq "") { - # XP way of doing the build date. - # 1998091509 = 1998, September, 15th, 9am local time zone - my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = - localtime(time); - - # localtime returns year minus 1900 - $year = $year + 1900; - $build_number = sprintf("%04d%02d%02d%02d", $year, 1+$mon, - $mday, $hour); - } - else { - $build_number = $given_date; - } - - if ("$outfile" eq "") { - print "$build_number\n"; - return; - } - - if (!$official) { - $build_number = "0000000000"; - } - - my $old_num = ""; - - # Don't overwrite $outfile if its contents won't change - if ( -e $outfile ) { - open(OLD, "<$outfile") || die "$outfile: $!\n"; - $old_num = ; - chomp($old_num); - close(OLD); - } - - if ($old_num ne $build_number) { - &write_number($outfile, $build_number); - } - return; -} - -sub SubstituteBuildNumber($$$) { - - my ($outfile, $build_num, $infile) = @_; - my $INFILE = new IO::File; - my $OUTFILE = new IO::File; - - open $INFILE, "<$build_num"; - my $build = <$INFILE>; - close $INFILE; - chomp $build; - chop $build if (substr($build, -1, 1) eq "\r"); - - if ($infile ne "") { - open($INFILE, "< $infile") || die "$infile: $!\n"; - } else { - open($INFILE, "< $outfile") || die "$outfile: $!\n"; - } - open $OUTFILE, ">${outfile}.old" || die; - - while (<$INFILE>) { - my $id = $_; - my $temp; - if ($id =~ "Build ID:") { - $temp = "Build ID: " . $build; - $id =~ s/Build ID:\s\d+/$temp/; - print $OUTFILE $id; - } - elsif ($id =~ "NS_BUILD_ID") { - $temp = "NS_BUILD_ID " . $build; - $id =~ s/NS_BUILD_ID\s\d+/$temp/; - print $OUTFILE $id; - } - elsif ($id =~ "GRE_BUILD_ID") { - if (defined($ENV{'MOZ_MILESTONE_RELEASE'}) && - $ENV{'MOZ_MILESTONE_RELEASE'} ne "") { - $temp = "GRE_BUILD_ID \"$milestone\""; - } else { - $temp = "GRE_BUILD_ID \"${milestone}_${build}\""; - } - $id =~ s/GRE_BUILD_ID\s\"\d+\"/$temp/; - print $OUTFILE $id; - } - else { - print $OUTFILE $_; - } - } - - close $INFILE; - close $OUTFILE; - - unlink $outfile; - rename "${outfile}.old", "$outfile"; -} - -sub SetMilestone($) { - my ($mstone) = (@_); - $milestone = $mstone if ($mstone ne ""); -} - -END {}; - -1; - diff --git a/config/printconfigsetting.py b/config/printconfigsetting.py new file mode 100644 index 00000000000..81f4e39044e --- /dev/null +++ b/config/printconfigsetting.py @@ -0,0 +1,21 @@ +import configobj, sys + +try: + (file, section, key) = sys.argv[1:] +except ValueError: + print "Usage: printconfigsetting.py
" + sys.exit(1) + +c = configobj.ConfigObj(file) + +try: + s = c[section] +except KeyError: + print >>sys.stderr, "Section [%s] not found." % section + sys.exit(1) + +try: + print s[key] +except KeyError: + print >>sys.stderr, "Key %s not found." % key + sys.exit(1) diff --git a/config/version_win.pl b/config/version_win.pl index 3c0f00c70a3..73c7b0f5569 100755 --- a/config/version_win.pl +++ b/config/version_win.pl @@ -138,7 +138,6 @@ if ($bits eq "16") { $fileos="VOS__WINDOWS16"; } my $bufferstr=" "; my $MILESTONE_FILE = "$topsrcdir/config/milestone.txt"; -my $BUILDID_FILE = "$depth/config/build_number"; #Read module.ver file #Version file overrides for WIN32: @@ -243,17 +242,7 @@ if ($official eq "1") { } - my ($buildid, $buildid_hi, $buildid_lo); - open(NUMBER, "<$BUILDID_FILE") || die "No build number file\n"; - while ( ) { $buildid = $_ } - close (NUMBER); - $buildid =~ s/^\s*(.*)\s*$/$1/; - $buildid_hi = substr($buildid, 0, 5); - $buildid_lo = substr($buildid, 5); - - $mfversion = $mpversion = "$milestone: $buildid"; - my @pvarray = split(',', $productversion); - $fileversion = "$pvarray[0],$pvarray[1],$buildid_hi,$buildid_lo"; + $mfversion = $mpversion = "$milestone"; } my $copyright = "License: MPL 1.1/GPL 2.0/LGPL 2.1"; @@ -390,7 +379,6 @@ print RCFILE qq{ // 1 VERSIONINFO - FILEVERSION $fileversion PRODUCTVERSION $productversion FILEFLAGSMASK 0x3fL FILEFLAGS $fileflags diff --git a/dom/src/base/nsGlobalWindow.cpp b/dom/src/base/nsGlobalWindow.cpp index 218211558ec..25d6dd31a4a 100644 --- a/dom/src/base/nsGlobalWindow.cpp +++ b/dom/src/base/nsGlobalWindow.cpp @@ -157,6 +157,7 @@ #include "nsCDefaultURIFixup.h" #include "nsEventDispatcher.h" #include "nsIObserverService.h" +#include "nsIXULAppInfo.h" #include "nsNetUtil.h" #include "plbase64.h" @@ -193,8 +194,6 @@ static PRLogModuleInfo* gDOMLeakPRLog; #endif -#include "nsBuildID.h" - nsIFactory *nsGlobalWindow::sComputedDOMStyleFactory = nsnull; static nsIEntropyCollector *gEntropyCollector = nsnull; @@ -8247,8 +8246,18 @@ nsNavigator::GetOnLine(PRBool* aOnline) NS_IMETHODIMP nsNavigator::GetBuildID(nsAString& aBuildID) { - aBuildID = NS_LITERAL_STRING(NS_STRINGIFY(NS_BUILD_ID)); + nsCOMPtr appInfo = + do_GetService("@mozilla.org/xre/app-info;1"); + if (!appInfo) + return NS_ERROR_NOT_IMPLEMENTED; + nsCAutoString buildID; + nsresult rv = appInfo->GetAppBuildID(buildID); + if (NS_FAILED(rv)) + return rv; + + aBuildID.Truncate(); + AppendASCIItoUTF16(buildID, aBuildID); return NS_OK; } diff --git a/gfx/src/ps/nsPostScriptObj.cpp b/gfx/src/ps/nsPostScriptObj.cpp index 9cbfc4b2224..1cba16bab88 100644 --- a/gfx/src/ps/nsPostScriptObj.cpp +++ b/gfx/src/ps/nsPostScriptObj.cpp @@ -73,10 +73,6 @@ #include "nsCRT.h" #include "nsFontMetricsPS.h" -#ifndef NS_BUILD_ID -#include "nsBuildID.h" -#endif /* !NS_BUILD_ID */ - #include "nsPrintfCString.h" #include "prenv.h" @@ -413,8 +409,8 @@ nsPostScriptObj::write_prolog(FILE *aHandle, PRBool aFTPEnable) fpCString(fWidth).get(), fpCString(fHeight).get()); - fprintf(f, "%%%%Creator: Mozilla PostScript module (%s/%lu)\n", - "rv:" MOZILLA_VERSION, (unsigned long)NS_BUILD_ID); + fprintf(f, "%%%%Creator: Mozilla PostScript module (%s)\n", + "rv:" MOZILLA_VERSION); fprintf(f, "%%%%DocumentData: Clean8Bit\n"); fprintf(f, "%%%%DocumentPaperSizes: %s\n", mPrintSetup->paper_name); fprintf(f, "%%%%Orientation: %s\n", diff --git a/layout/build/Makefile.in b/layout/build/Makefile.in index 0d035bc7590..77cb5b614f1 100644 --- a/layout/build/Makefile.in +++ b/layout/build/Makefile.in @@ -50,9 +50,6 @@ ifeq ($(OS_ARCH),OSF1) SHELL := ksh endif -BUILD_DATE = gbdate.h -BUILD_DATE_TS = gbdate.tstamp - MODULE = layout LIBRARY_NAME = gklayout EXPORT_LIBRARY = 1 @@ -112,7 +109,6 @@ endif CPPSRCS = \ nsLayoutModule.cpp \ - nsContentHTTPStartup.cpp \ nsContentDLF.cpp \ nsLayoutStatics.cpp \ $(NULL) @@ -298,24 +294,8 @@ endif DEFINES += -D_IMPL_NS_LAYOUT -GARBAGE += $(BUILD_DATE) $(BUILD_DATE_TS) - ifeq ($(OS_ARCH),IRIX) ifeq ($(GNU_CXX),1) LDFLAGS += -Wl,-LD_LAYOUT:lgot_buffer=50 endif endif - - -export:: $(BUILD_DATE) - -$(BUILD_DATE_TS): FORCE - @for f in $(SHARED_LIBRARY_LIBS); do \ - if [ $$f -nt $@ ]; then \ - touch $@; \ - fi; \ - done - -$(BUILD_DATE):: gbdate.pl $(BUILD_DATE_TS) - $(RM) $@ - $(PERL) $(srcdir)/gbdate.pl > $@ diff --git a/layout/build/nsContentHTTPStartup.cpp b/layout/build/nsContentHTTPStartup.cpp deleted file mode 100644 index 9455532dcb0..00000000000 --- a/layout/build/nsContentHTTPStartup.cpp +++ /dev/null @@ -1,113 +0,0 @@ -/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ -/* ***** 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 Communicator client code. - * - * The Initial Developer of the Original Code is - * Netscape Communications Corporation. - * Portions created by the Initial Developer are Copyright (C) 1998 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * Alec Flett - * - * Alternatively, the contents of this file may be used under the terms of - * either of 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 ***** */ - -#include "nsIServiceManager.h" -#include "nsICategoryManager.h" - -#include "nsCOMPtr.h" -#include "nsString.h" -#include "nsXPIDLString.h" -#include "nsCRT.h" - -#include "nsContentHTTPStartup.h" -#include "nsIHttpProtocolHandler.h" -#include "gbdate.h" - -#define PRODUCT_NAME "Gecko" - -NS_IMPL_ISUPPORTS1(nsContentHTTPStartup,nsIObserver) - -nsresult -nsContentHTTPStartup::Observe( nsISupports *aSubject, - const char *aTopic, - const PRUnichar *aData) -{ - if (nsCRT::strcmp(aTopic, NS_HTTP_STARTUP_TOPIC) != 0) - return NS_OK; - - nsresult rv = nsnull; - - nsCOMPtr http(do_QueryInterface(aSubject)); - if (NS_FAILED(rv)) return rv; - - rv = http->SetProduct(NS_LITERAL_CSTRING(PRODUCT_NAME)); - if (NS_FAILED(rv)) return rv; - - rv = http->SetProductSub(NS_LITERAL_CSTRING(PRODUCT_VERSION)); - if (NS_FAILED(rv)) return rv; - - return NS_OK; -} - -nsresult -nsContentHTTPStartup::RegisterHTTPStartup(nsIComponentManager* aCompMgr, - nsIFile* aPath, - const char* aRegistryLocation, - const char* aComponentType, - const nsModuleComponentInfo* aInfo) -{ - nsresult rv; - nsCOMPtr - catMan(do_GetService(NS_CATEGORYMANAGER_CONTRACTID, &rv)); - - if (NS_FAILED(rv)) return rv; - - nsXPIDLCString previousEntry; - rv = catMan->AddCategoryEntry(NS_HTTP_STARTUP_CATEGORY, - "Content UserAgent Setter", - NS_CONTENTHTTPSTARTUP_CONTRACTID, - PR_TRUE, PR_TRUE, - getter_Copies(previousEntry)); - return rv; -} - -nsresult -nsContentHTTPStartup::UnregisterHTTPStartup(nsIComponentManager* aCompMgr, - nsIFile* aPath, - const char* aRegistryLocation, - const nsModuleComponentInfo* aInfo) -{ - nsresult rv; - nsCOMPtr - catMan(do_GetService(NS_CATEGORYMANAGER_CONTRACTID, &rv)); - - if (NS_FAILED(rv)) return rv; - - - return NS_OK; -} diff --git a/layout/build/nsContentHTTPStartup.h b/layout/build/nsContentHTTPStartup.h deleted file mode 100644 index aab45d07fce..00000000000 --- a/layout/build/nsContentHTTPStartup.h +++ /dev/null @@ -1,78 +0,0 @@ -/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ -/* ***** 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 Communicator client code. - * - * The Initial Developer of the Original Code is - * Netscape Communications Corporation. - * Portions created by the Initial Developer are Copyright (C) 1998 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * Alec Flett - * - * Alternatively, the contents of this file may be used under the terms of - * either of 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 ***** */ - -#include "nsIObserver.h" - -#define NS_CONTENTHTTPSTARTUP_CONTRACTID \ - "@mozilla.org/content/http-startup;1" - -/* c2f6ef7e-afd5-4be4-a1f5-c824efa4231b */ -#define NS_CONTENTHTTPSTARTUP_CID \ -{ 0xc2f6ef7e, 0xafd5, 0x4be4, \ - {0xa1, 0xf5, 0xc8, 0x24, 0xef, 0xa4, 0x23, 0x1b} } - -struct nsModuleComponentInfo; -class nsIFile; - -class nsContentHTTPStartup : public nsIObserver -{ -public: - nsContentHTTPStartup() { } - virtual ~nsContentHTTPStartup() {} - - NS_DECL_ISUPPORTS - NS_DECL_NSIOBSERVER - -public: - static NS_IMETHODIMP - RegisterHTTPStartup(nsIComponentManager* aCompMgr, - nsIFile* aPath, - const char* aRegistryLocation, - const char* aComponentType, - const nsModuleComponentInfo* aInfo); - - static NS_IMETHODIMP - UnregisterHTTPStartup(nsIComponentManager* aCompMgr, - nsIFile* aPath, - const char* aRegistryLocation, - const nsModuleComponentInfo* aInfo); - -private: - nsresult setUserAgent(); - -}; diff --git a/layout/build/nsLayoutModule.cpp b/layout/build/nsLayoutModule.cpp index 2d6302bc6e3..04ea85c1034 100644 --- a/layout/build/nsLayoutModule.cpp +++ b/layout/build/nsLayoutModule.cpp @@ -38,7 +38,6 @@ #include "nsLayoutStatics.h" #include "nsContentCID.h" -#include "nsContentHTTPStartup.h" #include "nsContentDLF.h" #include "nsContentPolicyUtils.h" #include "nsDataDocumentContentPolicy.h" @@ -517,7 +516,6 @@ MAKE_CTOR(CreateXULPopupListener, nsIXULPopupListener, NS_NewXUL MAKE_CTOR(CreateXTFService, nsIXTFService, NS_NewXTFService) MAKE_CTOR(CreateXMLContentBuilder, nsIXMLContentBuilder, NS_NewXMLContentBuilder) #endif -NS_GENERIC_FACTORY_CONSTRUCTOR(nsContentHTTPStartup) MAKE_CTOR(CreateContentDLF, nsIDocumentLoaderFactory, NS_NewContentDocumentLoaderFactory) NS_GENERIC_FACTORY_CONSTRUCTOR(nsCSSOMFactory) NS_GENERIC_FACTORY_CONSTRUCTOR(nsInspectorCSSUtils) @@ -1218,13 +1216,6 @@ static const nsModuleComponentInfo gComponents[] = { CreateXMLContentBuilder }, #endif - { "Content HTTP Startup Listener", - NS_CONTENTHTTPSTARTUP_CID, - NS_CONTENTHTTPSTARTUP_CONTRACTID, - nsContentHTTPStartupConstructor, - nsContentHTTPStartup::RegisterHTTPStartup, - nsContentHTTPStartup::UnregisterHTTPStartup }, - { "Document Loader Factory", NS_CONTENT_DOCUMENT_LOADER_FACTORY_CID, "@mozilla.org/content/document-loader-factory;1", diff --git a/modules/libreg/src/vr_stubs.c b/modules/libreg/src/vr_stubs.c index f7f5a6f98f2..f407259a3f6 100644 --- a/modules/libreg/src/vr_stubs.c +++ b/modules/libreg/src/vr_stubs.c @@ -606,17 +606,10 @@ int strncasecmp(const char *str1, const char *str2, int length) #include "NSReg.h" #include "VerReg.h" -#include "nsBuildID.h" char *TheRegistry = "registry"; char *Flist; -/* WARNING: build hackery */ -#if defined(STANDALONE_REGISTRY) && !defined(XP_MAC) && !defined(XP_MACOSX) -long BUILDNUM = NS_BUILD_ID; -#endif - - REGERR vr_ParseVersion(char *verstr, VERSION *result); #if defined(XP_UNIX) && !defined(XP_MACOSX) diff --git a/netwerk/protocol/http/src/nsHttpHandler.cpp b/netwerk/protocol/http/src/nsHttpHandler.cpp index bc6e1097cc9..4defbb4bbd4 100644 --- a/netwerk/protocol/http/src/nsHttpHandler.cpp +++ b/netwerk/protocol/http/src/nsHttpHandler.cpp @@ -75,6 +75,8 @@ #include "nsNetUtil.h" #include "nsIOService.h" +#include "nsIXULAppInfo.h" + #if defined(XP_UNIX) || defined(XP_BEOS) #include #endif @@ -164,6 +166,7 @@ nsHttpHandler::nsHttpHandler() , mPhishyUserPassLength(1) , mLastUniqueID(NowInSeconds()) , mSessionStartTime(0) + , mProduct("Gecko") , mUserAgentIsDirty(PR_TRUE) , mUseCache(PR_TRUE) , mSendSecureXSiteReferrer(PR_TRUE) @@ -258,6 +261,11 @@ nsHttpHandler::Init() rv = InitConnectionMgr(); if (NS_FAILED(rv)) return rv; + nsCOMPtr appInfo = + do_GetService("@mozilla.org/xre/app-info;1"); + if (appInfo) + appInfo->GetPlatformBuildID(mProductSub); + // Startup the http category // Bring alive the objects in the http-protocol-startup category NS_CreateServicesFromCategory(NS_HTTP_STARTUP_CATEGORY, @@ -816,16 +824,6 @@ nsHttpHandler::PrefsChanged(nsIPrefBranch *prefs, const char *pref) } // Gather product values. - if (PREF_CHANGED(UA_PREF("product"))) { - prefs->GetCharPref(UA_PREF_PREFIX "product", - getter_Copies(mProduct)); - mUserAgentIsDirty = PR_TRUE; - } - if (PREF_CHANGED(UA_PREF("productSub"))) { - prefs->GetCharPref(UA_PREF("productSub"), - getter_Copies(mProductSub)); - mUserAgentIsDirty = PR_TRUE; - } if (PREF_CHANGED(UA_PREF("productComment"))) { prefs->GetCharPref(UA_PREF("productComment"), getter_Copies(mProductComment)); diff --git a/netwerk/protocol/http/src/nsHttpHandler.h b/netwerk/protocol/http/src/nsHttpHandler.h index c08313d7ae9..2b60204d675 100644 --- a/netwerk/protocol/http/src/nsHttpHandler.h +++ b/netwerk/protocol/http/src/nsHttpHandler.h @@ -284,7 +284,7 @@ private: nsXPIDLCString mVendor; nsXPIDLCString mVendorSub; nsXPIDLCString mVendorComment; - nsXPIDLCString mProduct; + nsCString mProduct; nsXPIDLCString mProductSub; nsXPIDLCString mProductComment; nsCString mExtraUA; diff --git a/toolkit/xre/Makefile.in b/toolkit/xre/Makefile.in index 90eeec22c62..e3dd8d9ebd7 100644 --- a/toolkit/xre/Makefile.in +++ b/toolkit/xre/Makefile.in @@ -93,7 +93,6 @@ FORCE_STATIC_LIB = 1 XPIDLSRCS = \ nsINativeAppSupport.idl \ - nsIXULAppInfo.idl \ nsIXULRuntime.idl \ $(NULL) @@ -253,3 +252,12 @@ endif export:: $(addprefix $(topsrcdir)/xpfe/bootstrap/, $(SHAREDCPPSRCS)) $(STACKWALK_CPPSRCS) $(INSTALL) $^ . + +platform.ini: FORCE + $(PYTHON) $(srcdir)/make-platformini.py $(topsrcdir)/config/milestone.txt > $@ + +libs:: platform.ini + $(INSTALL) $^ $(DIST)/bin + +install:: + $(INSTALL) $(IFLAGS1) $^ $(DESTDIR)$(mozappdir) diff --git a/toolkit/xre/make-platformini.py b/toolkit/xre/make-platformini.py new file mode 100644 index 00000000000..4e2a904d53d --- /dev/null +++ b/toolkit/xre/make-platformini.py @@ -0,0 +1,31 @@ +#!/usr/bin/python + +from optparse import OptionParser +from datetime import datetime +import sys +import os + +o = OptionParser() +o.add_option("--print-buildid", action="store_true", dest="print_buildid") + +(options, args) = o.parse_args() +buildid = os.environ.get('MOZ_BUILD_DATE', datetime.now().strftime('%Y%m%d%H')) + +if options.print_buildid: + print buildid + sys.exit(0) + +(milestoneFile,) = args +for line in open(milestoneFile, 'r'): + if line[0] == '#': + continue + + line = line.strip() + if line == '': + continue + + milestone = line + +print """[Build] +BuildID=%s +Milestone=%s""" % (buildid, milestone) diff --git a/toolkit/xre/nsAppRunner.cpp b/toolkit/xre/nsAppRunner.cpp index 501060cf6c2..af28cc23aef 100644 --- a/toolkit/xre/nsAppRunner.cpp +++ b/toolkit/xre/nsAppRunner.cpp @@ -46,7 +46,6 @@ #include "nsAppRunner.h" #include "nsUpdateDriver.h" -#include "nsBuildID.h" #ifdef XP_MACOSX #include "MacLaunchHelper.h" @@ -265,6 +264,9 @@ extern "C" { int gArgc; char **gArgv; +static char gToolkitVersion[20]; +static char gToolkitBuildID[40]; + static int gRestartArgc; static char **gRestartArgv; @@ -573,7 +575,7 @@ nsXULAppInfo::GetVersion(nsACString& aResult) NS_IMETHODIMP nsXULAppInfo::GetPlatformVersion(nsACString& aResult) { - aResult.AssignLiteral(TOOLKIT_EM_VERSION); + aResult.AssignLiteral(gToolkitVersion); return NS_OK; } @@ -589,7 +591,7 @@ nsXULAppInfo::GetAppBuildID(nsACString& aResult) NS_IMETHODIMP nsXULAppInfo::GetPlatformBuildID(nsACString& aResult) { - aResult.Assign(NS_STRINGIFY(BUILD_ID)); + aResult.Assign(gToolkitBuildID); return NS_OK; } @@ -1936,7 +1938,7 @@ static void BuildVersion(nsCString &aBuf) aBuf.Append('_'); aBuf.Append(gAppData->buildID); aBuf.Append('/'); - aBuf.AppendLiteral(GRE_BUILD_ID); + aBuf.Append(gToolkitBuildID); } static void @@ -2261,27 +2263,6 @@ XRE_main(int argc, char* argv[], const nsXREAppData* aAppData) return 1; } - if (appData.size > offsetof(nsXREAppData, minVersion)) { - if (!appData.minVersion) { - Output(PR_TRUE, "Error: Gecko:MinVersion not specified in application.ini\n"); - return 1; - } - - if (!appData.maxVersion) { - // If no maxVersion is specified, we assume the app is only compatible - // with the initial preview release. Do not increment this number ever! - SetAllocatedString(appData.maxVersion, "1.*"); - } - - if (NS_CompareVersions(appData.minVersion, TOOLKIT_EM_VERSION) > 0 || - NS_CompareVersions(appData.maxVersion, TOOLKIT_EM_VERSION) < 0) { - Output(PR_TRUE, "Error: Platform version " TOOLKIT_EM_VERSION " is not compatible with\n" - "minVersion >= %s\nmaxVersion <= %s\n", - appData.minVersion, appData.maxVersion); - return 1; - } - } - ScopedLogging log; if (!appData.xreDirectory) { @@ -2300,6 +2281,54 @@ XRE_main(int argc, char* argv[], const nsXREAppData* aAppData) return 2; } + nsCOMPtr iniFile; + rv = appData.xreDirectory->Clone(getter_AddRefs(iniFile)); + if (NS_FAILED(rv)) + return 2; + + iniFile->AppendNative(NS_LITERAL_CSTRING("platform.ini")); + + nsCOMPtr localIniFile = do_QueryInterface(iniFile); + if (!localIniFile) + return 2; + + nsINIParser parser; + rv = parser.Init(localIniFile); + if (NS_SUCCEEDED(rv)) { + rv = parser.GetString("Build", "Milestone", + gToolkitVersion, sizeof(gToolkitVersion)); + NS_ASSERTION(NS_SUCCEEDED(rv), "Failed to get toolkit version"); + + rv = parser.GetString("Build", "BuildID", + gToolkitBuildID, sizeof(gToolkitBuildID)); + NS_ASSERTION(NS_SUCCEEDED(rv), "Failed to get toolkit buildid"); + } + else { + NS_ERROR("Couldn't parse platform.ini!"); + } + + if (appData.size > offsetof(nsXREAppData, minVersion)) { + if (!appData.minVersion) { + Output(PR_TRUE, "Error: Gecko:MinVersion not specified in application.ini\n"); + return 1; + } + + if (!appData.maxVersion) { + // If no maxVersion is specified, we assume the app is only compatible + // with the initial preview release. Do not increment this number ever! + SetAllocatedString(appData.maxVersion, "1.*"); + } + + if (NS_CompareVersions(appData.minVersion, gToolkitVersion) > 0 || + NS_CompareVersions(appData.maxVersion, gToolkitVersion) < 0) { + Output(PR_TRUE, "Error: Platform version '%s' is not compatible with\n" + "minVersion >= %s\nmaxVersion <= %s\n", + gToolkitVersion, + appData.minVersion, appData.maxVersion); + return 1; + } + } + #ifdef MOZ_AIRBAG const char* airbagEnv = PR_GetEnv("MOZ_CRASHREPORTER"); if (airbagEnv && *airbagEnv) { diff --git a/xpcom/glue/standalone/Makefile.in b/xpcom/glue/standalone/Makefile.in index 960f7cac05b..d9f90d16dc3 100644 --- a/xpcom/glue/standalone/Makefile.in +++ b/xpcom/glue/standalone/Makefile.in @@ -84,7 +84,6 @@ CSRCS = \ CPPSRCS = \ $(XPCOM_GLUE_SRC_LCPPSRCS) \ nsXPCOMGlue.cpp \ - nsGREDirServiceProvider.cpp \ $(LINKSRC) \ $(NULL) diff --git a/xpcom/glue/standalone/nsGREDirServiceProvider.cpp b/xpcom/glue/standalone/nsGREDirServiceProvider.cpp deleted file mode 100644 index 0b28cc224b9..00000000000 --- a/xpcom/glue/standalone/nsGREDirServiceProvider.cpp +++ /dev/null @@ -1,320 +0,0 @@ -/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* ***** 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 Communicator. - * - * The Initial Developer of the Original Code is - * Netscape Communications Corp. - * Portions created by the Initial Developer are Copyright (C) 2003 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * Sean Su - * Benjamin Smedberg - * - * 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 ***** */ - -#include "nsBuildID.h" - -#include "nsEmbedString.h" -#include "nsXPCOMPrivate.h" -#include "nsXPCOMGlue.h" -#include "nsILocalFile.h" -#include "nsIDirectoryService.h" -#include "nsDirectoryServiceDefs.h" -#include "nsCOMPtr.h" -#include "nsMemory.h" - -#include "nspr.h" -#include "plstr.h" - -#ifdef XP_WIN32 -#include -#include -#include -#elif defined(XP_OS2) -#define INCL_DOS -#include -#include -#include -#include "prenv.h" -#elif defined(XP_MACOSX) -#include -#include -#elif defined(XP_UNIX) -#include -#include -#include -#include "prenv.h" -#elif defined(XP_BEOS) -#include -#include -#endif - -#include - -#include "nsGREDirServiceProvider.h" - -static PRBool GRE_GetCurrentProcessDirectory(char* buffer); - -//***************************************************************************** -// nsGREDirServiceProvider::nsISupports -//***************************************************************************** - -NS_IMPL_ISUPPORTS1(nsGREDirServiceProvider, nsIDirectoryServiceProvider) - -//***************************************************************************** -// nsGREDirServiceProvider::nsIDirectoryServiceProvider -//***************************************************************************** - -NS_IMETHODIMP -nsGREDirServiceProvider::GetFile(const char *prop, PRBool *persistent, nsIFile **_retval) -{ - *_retval = nsnull; - *persistent = PR_TRUE; - - //--------------------------------------------------------------- - // Note that by returning a valid localFile's for NS_GRE_DIR, - // your app is indicating to XPCOM that it found a GRE version - // with which it's compatible with and intends to be "run against" - // that GRE. - // - // Please see http://www.mozilla.org/projects/embedding/GRE.html - // for more info on GRE. - //--------------------------------------------------------------- - if(strcmp(prop, NS_GRE_DIR) == 0) - { - nsILocalFile* lfile = nsnull; - nsresult rv = GRE_GetGREDirectory(&lfile); - *_retval = lfile; - return rv; - } - - return NS_ERROR_FAILURE; -} - -//***************************************************************************** -// Implementations from nsXPCOMGlue.h and helper functions. -//***************************************************************************** - -PRBool -GRE_GetCurrentProcessDirectory(char* buffer) -{ - *buffer = '\0'; - -#ifdef XP_WIN - DWORD bufLength = ::GetModuleFileName(0, buffer, MAXPATHLEN); - if (bufLength == 0 || bufLength == MAXPATHLEN) - return PR_FALSE; - // chop of the executable name by finding the rightmost backslash - unsigned char* lastSlash = _mbsrchr((unsigned char*) buffer, '\\'); - if (lastSlash) { - *(lastSlash) = '\0'; - return PR_TRUE; - } - -#elif defined(XP_MACOSX) - // Works even if we're not bundled. - CFBundleRef appBundle = CFBundleGetMainBundle(); - if (appBundle != nsnull) - { - CFURLRef bundleURL = CFBundleCopyExecutableURL(appBundle); - if (bundleURL != nsnull) - { - CFURLRef parentURL = CFURLCreateCopyDeletingLastPathComponent(kCFAllocatorDefault, bundleURL); - if (parentURL) - { - CFStringRef path = CFURLCopyFileSystemPath(parentURL, kCFURLPOSIXPathStyle); - if (path) - { - CFStringGetCString(path, buffer, MAXPATHLEN, kCFStringEncodingUTF8); - CFRelease(path); - } - CFRelease(parentURL); - } - CFRelease(bundleURL); - } - } - if (*buffer) return PR_TRUE; - -#elif defined(XP_UNIX) - - // In the absence of a good way to get the executable directory let - // us try this for unix: - // - if MOZILLA_FIVE_HOME is defined, that is it - // - else give the current directory - - // The MOZ_DEFAULT_MOZILLA_FIVE_HOME variable can be set at configure time with - // a --with-default-mozilla-five-home=foo autoconf flag. - // - // The idea here is to allow for builds that have a default MOZILLA_FIVE_HOME - // regardless of the environment. This makes it easier to write apps that - // embed mozilla without having to worry about setting up the environment - // - // We do this py putenv()ing the default value into the environment. Note that - // we only do this if it is not already set. -#ifdef MOZ_DEFAULT_MOZILLA_FIVE_HOME - if (getenv("MOZILLA_FIVE_HOME") == nsnull) - { - putenv("MOZILLA_FIVE_HOME=" MOZ_DEFAULT_MOZILLA_FIVE_HOME); - } -#endif - - char *moz5 = getenv("MOZILLA_FIVE_HOME"); - - if (moz5 && *moz5) - { - if (!realpath(moz5, buffer)) - strcpy(buffer, moz5); - - return PR_TRUE; - } - else - { -#if defined(DEBUG) - static PRBool firstWarning = PR_TRUE; - - if(firstWarning) { - // Warn that MOZILLA_FIVE_HOME not set, once. - printf("Warning: MOZILLA_FIVE_HOME not set.\n"); - firstWarning = PR_FALSE; - } -#endif /* DEBUG */ - - // Fall back to current directory. - if (getcwd(buffer, MAXPATHLEN)) - { - return PR_TRUE; - } - } - -#elif defined(XP_OS2) - PPIB ppib; - PTIB ptib; - char* p; - DosGetInfoBlocks( &ptib, &ppib); - DosQueryModuleName( ppib->pib_hmte, MAXPATHLEN, buffer); - p = strrchr( buffer, '\\'); // XXX DBCS misery - if (p) { - *p = '\0'; - return PR_TRUE; - } - -#elif defined(XP_BEOS) -// We are able to get actual path for running app. - int32 cookie = 0; - image_info info; - char *lastSlash; - *buffer = 0; - if (get_next_image_info(0, &cookie, &info) == B_OK) - { - strcpy(buffer, info.name); - if ((lastSlash = strrchr(buffer, '/')) != 0) - { - *lastSlash = '\0'; - return PR_TRUE; - } - } - -#endif - - return PR_FALSE; -} - -/** - * the GRE location is stored in a static buffer so that we don't have - * to compute it multiple times. - */ - -static char sXPCOMPath[MAXPATHLEN] = ""; - -extern "C" char const * -GRE_GetXPCOMPath() -{ - // we've already done this... - if (*sXPCOMPath) - return sXPCOMPath; - - char buffer[MAXPATHLEN]; - - // If the xpcom library exists in the current process directory, - // then we will not use any GRE. The assumption here is that the - // GRE is in the same directory as the executable. - if (GRE_GetCurrentProcessDirectory(buffer)) { - PRUint32 pathlen = strlen(buffer); - strcpy(buffer + pathlen, XPCOM_FILE_PATH_SEPARATOR XPCOM_DLL); - - struct stat libStat; - int statResult = stat(buffer, &libStat); - - if (statResult != -1) { - //found our xpcom lib in the current process directory - strcpy(sXPCOMPath, buffer); - return sXPCOMPath; - } - } - - static const GREVersionRange version = { - GRE_BUILD_ID, PR_TRUE, - GRE_BUILD_ID, PR_TRUE - }; - - GRE_GetGREPathWithProperties(&version, 1, - nsnull, 0, - sXPCOMPath, MAXPATHLEN); - if (*sXPCOMPath) - return sXPCOMPath; - - return nsnull; -} - -extern "C" nsresult -GRE_GetGREDirectory(nsILocalFile* *_retval) -{ - NS_ENSURE_ARG_POINTER(_retval); - nsresult rv = NS_ERROR_FAILURE; - - // Get the path of the GRE which is compatible with our embedding application - // from the registry - - const char *pGREDir = GRE_GetXPCOMPath(); - if(!pGREDir) - return NS_ERROR_FAILURE; - - nsCOMPtr xpcomPath; - nsEmbedCString leaf(pGREDir); - rv = NS_NewNativeLocalFile(leaf, PR_TRUE, getter_AddRefs(xpcomPath)); - - if (NS_FAILED(rv)) - return rv; - - nsCOMPtr directory; - rv = xpcomPath->GetParent(getter_AddRefs(directory)); - if (NS_FAILED(rv)) - return rv; - - return CallQueryInterface(directory, _retval); -} diff --git a/xpcom/glue/standalone/nsGREDirServiceProvider.h b/xpcom/glue/standalone/nsGREDirServiceProvider.h deleted file mode 100644 index 021adef599c..00000000000 --- a/xpcom/glue/standalone/nsGREDirServiceProvider.h +++ /dev/null @@ -1,58 +0,0 @@ -/* ***** 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 Communicator. - * - * The Initial Developer of the Original Code is - * Netscape Communications Corp. - * Portions created by the Initial Developer are Copyright (C) 2003 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * Sean Su - * - * 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 ***** */ - -#ifndef nsGREDirServiceProvider_h_ -#define nsGREDirServiceProvider_h_ - -#include "nsIDirectoryService.h" - -/** - * the directoryserviceprovider used by GRE_Startup when calling NS_InitXPCOM2 - */ -class nsGREDirServiceProvider : public nsIDirectoryServiceProvider -{ -public: - nsGREDirServiceProvider() { } - - NS_DECL_ISUPPORTS - NS_DECL_NSIDIRECTORYSERVICEPROVIDER - -private: - ~nsGREDirServiceProvider() { } -}; - -#endif // nsGREDirServiceProvider.h diff --git a/xpcom/glue/standalone/nsXPCOMGlue.cpp b/xpcom/glue/standalone/nsXPCOMGlue.cpp index e348394ba7e..9394c4f4636 100644 --- a/xpcom/glue/standalone/nsXPCOMGlue.cpp +++ b/xpcom/glue/standalone/nsXPCOMGlue.cpp @@ -42,7 +42,6 @@ #include "nspr.h" #include "nsDebug.h" #include "nsIServiceManager.h" -#include "nsGREDirServiceProvider.h" #include "nsXPCOMPrivate.h" #include "nsCOMPtr.h" #include @@ -518,68 +517,3 @@ NS_InvokeByIndex(nsISupports* that, PRUint32 methodIndex, return xpcomFunctions.invokeByIndexFunc(that, methodIndex, paramCount, params); } - -// Default GRE startup/shutdown code - -extern "C" -nsresult GRE_Startup() -{ - const char* xpcomLocation = GRE_GetXPCOMPath(); - - // Startup the XPCOM Glue that links us up with XPCOM. - nsresult rv = XPCOMGlueStartup(xpcomLocation); - - if (NS_FAILED(rv)) { - NS_WARNING("gre: XPCOMGlueStartup failed"); - return rv; - } - -#ifdef XP_WIN - // On windows we have legacy GRE code that does not load the GRE dependent - // libs (seamonkey GRE, not libxul)... add the GRE to the PATH. - // See bug 301043. - - const char *lastSlash = strrchr(xpcomLocation, '\\'); - if (lastSlash) { - int xpcomPathLen = lastSlash - xpcomLocation; - DWORD pathLen = GetEnvironmentVariable("PATH", nsnull, 0); - - char *newPath = (char*) _alloca(xpcomPathLen + pathLen + 1); - strncpy(newPath, xpcomLocation, xpcomPathLen); - // in case GetEnvironmentVariable fails - newPath[xpcomPathLen] = ';'; - newPath[xpcomPathLen + 1] = '\0'; - - GetEnvironmentVariable("PATH", newPath + xpcomPathLen + 1, pathLen); - SetEnvironmentVariable("PATH", newPath); - } -#endif - - nsGREDirServiceProvider *provider = new nsGREDirServiceProvider(); - if ( !provider ) { - NS_WARNING("GRE_Startup failed"); - XPCOMGlueShutdown(); - return NS_ERROR_OUT_OF_MEMORY; - } - - nsCOMPtr servMan; - NS_ADDREF( provider ); - rv = NS_InitXPCOM2(getter_AddRefs(servMan), nsnull, provider); - NS_RELEASE(provider); - - if ( NS_FAILED(rv) || !servMan) { - NS_WARNING("gre: NS_InitXPCOM failed"); - XPCOMGlueShutdown(); - return rv; - } - - return NS_OK; -} - -extern "C" -nsresult GRE_Shutdown() -{ - NS_ShutdownXPCOM(nsnull); - XPCOMGlueShutdown(); - return NS_OK; -} diff --git a/xpcom/glue/standalone/nsXPCOMGlue.h b/xpcom/glue/standalone/nsXPCOMGlue.h index a94a4b2d9ca..9a93c9aefec 100644 --- a/xpcom/glue/standalone/nsXPCOMGlue.h +++ b/xpcom/glue/standalone/nsXPCOMGlue.h @@ -127,45 +127,5 @@ XPCOMGlueLoadXULFunctions(const nsDynamicFunctionLoad *symbols); extern "C" NS_HIDDEN_(nsresult) XPCOMGlueShutdown(); - -/** - * Locate the path of the XPCOM shared library of a compatible GRE. - * The result of this function is normally passed directly to - * XPCOMGlueStartup. This looks for the GRE version in - * nsBuildID.h, which is generated at build time. Unless you set - * MOZ_MILESTONE_RELEASE this will probably not be a useful GRE version string. - * - * @return string buffer pointing to the XPCOM DLL path. Callers do - * not need to free this buffer. - * @status DEPRECATED - Use GRE_GetGREPathWithProperties - */ -extern "C" NS_HIDDEN_(char const *) -GRE_GetXPCOMPath(); - - -/** - * Locate the directory of a compatible GRE as an nsIFile - * - * @param _retval Ordinary XPCOM getter, returns an addrefed interface. - */ -extern "C" NS_HIDDEN_(nsresult) -GRE_GetGREDirectory(nsILocalFile* *_retval); - - -/** - * Embedding applications which don't need a custom - * directoryserviceprovider may use GRE_Startup to start the XPCOM - * glue and initialize the GRE in one step. - */ -extern "C" NS_HIDDEN_(nsresult) -GRE_Startup(); - - -/** - * Shut down XPCOM and the XPCOM glue in one step. - */ -extern "C" NS_HIDDEN_(nsresult) -GRE_Shutdown(); - #endif // XPCOM_GLUE #endif // nsXPCOMGlue_h__ diff --git a/xpcom/system/Makefile.in b/xpcom/system/Makefile.in index a2797486c81..ac09fdd7996 100644 --- a/xpcom/system/Makefile.in +++ b/xpcom/system/Makefile.in @@ -45,6 +45,7 @@ include $(DEPTH)/config/autoconf.mk MODULE = xpcom XPIDLSRCS = \ + nsIXULAppInfo.idl \ nsIGConfService.idl \ nsIGnomeVFSService.idl \ $(NULL) diff --git a/toolkit/xre/nsIXULAppInfo.idl b/xpcom/system/nsIXULAppInfo.idl similarity index 100% rename from toolkit/xre/nsIXULAppInfo.idl rename to xpcom/system/nsIXULAppInfo.idl diff --git a/xpinstall/src/nsJSInstall.cpp b/xpinstall/src/nsJSInstall.cpp index 2745e79b7bc..a3f87687b0e 100644 --- a/xpinstall/src/nsJSInstall.cpp +++ b/xpinstall/src/nsJSInstall.cpp @@ -41,7 +41,6 @@ #include "nscore.h" #include "nsIScriptContext.h" -#include "nsBuildID.h" #include "nsString.h" #include "nsReadableUtils.h" #include "nsInstall.h" @@ -1856,8 +1855,6 @@ static JSConstDoubleSpec install_constants[] = { CHROME_DELAYED, "DELAYED_CHROME" }, { CHROME_SELECT, "SELECT_CHROME" }, - { NS_BUILD_ID, "buildID" }, - {0} }; diff --git a/xpinstall/src/nsSoftwareUpdate.cpp b/xpinstall/src/nsSoftwareUpdate.cpp index 854981d35c7..e37851221ac 100644 --- a/xpinstall/src/nsSoftwareUpdate.cpp +++ b/xpinstall/src/nsSoftwareUpdate.cpp @@ -69,7 +69,6 @@ #include "nsTopProgressNotifier.h" #include "nsLoggingProgressNotifier.h" -#include "nsBuildID.h" #include "nsProcess.h" /* For Javascript Namespace Access */ diff --git a/xulrunner/app/nsRegisterGRE.h b/xulrunner/app/nsRegisterGRE.h index 3a9576edf5c..006b7860018 100755 --- a/xulrunner/app/nsRegisterGRE.h +++ b/xulrunner/app/nsRegisterGRE.h @@ -39,6 +39,7 @@ #define nsRegisterGRE_h__ #include "nscore.h" +#include "nsStringAPI.h" class nsIFile; struct GREProperty; @@ -52,4 +53,9 @@ RegisterXULRunner(PRBool aRegisterGlobally, nsIFile* aLocation, NS_HIDDEN_(void) UnregisterXULRunner(PRBool aUnregisterGlobally, nsIFile* aLocation); +// Shared helper function to get the GRE milestone and optionally buildid +// from platform.ini +NS_HIDDEN_(nsresult) +GetGREVersion(const char *argv0, nsACString *aMilestone, nsACString *aVersion); + #endif // nsRegisterGRE_h__ diff --git a/xulrunner/app/nsXULRunnerApp.cpp b/xulrunner/app/nsXULRunnerApp.cpp index a72128d779c..5952ed948c1 100644 --- a/xulrunner/app/nsXULRunnerApp.cpp +++ b/xulrunner/app/nsXULRunnerApp.cpp @@ -50,12 +50,12 @@ #include "nsCOMPtr.h" #include "nsMemory.h" #include "nsCRTGlue.h" -#include "nsBuildID.h" #include "nsStringAPI.h" #include "nsServiceManagerUtils.h" #include "plstr.h" #include "prprf.h" #include "prenv.h" +#include "nsINIParser.h" /** * Output a string to the user. This method is really only meant to be used to @@ -110,8 +110,11 @@ static PRBool IsArg(const char* arg, const char* s) return PR_FALSE; } -static void Usage() +static void Usage(const char *argv0) { + nsCAutoString milestone; + GetGREVersion(argv0, &milestone, nsnull); + // display additional information (XXX make localizable?) Output(PR_FALSE, "Mozilla XULRunner %s\n\n" @@ -139,7 +142,7 @@ static void Usage() "\n" "APP-OPTIONS\n" " Application specific options.\n", - GRE_BUILD_ID); + milestone.get()); } static nsresult @@ -161,6 +164,41 @@ GetXULRunnerDir(const char *argv0, nsIFile* *aResult) return rv; } +nsresult +GetGREVersion(const char *argv0, + nsACString *aMilestone, + nsACString *aVersion) +{ + if (aMilestone) + aMilestone->Assign(""); + if (aVersion) + aVersion->Assign(""); + + nsCOMPtr iniFile; + nsresult rv = XRE_GetBinaryPath(argv0, getter_AddRefs(iniFile)); + if (NS_FAILED(rv)) + return rv; + + iniFile->SetNativeLeafName(NS_LITERAL_CSTRING("platform.ini")); + + nsINIParser parser; + rv = parser.Init(iniFile); + if (NS_FAILED(rv)) + return rv; + + if (aMilestone) { + rv = parser.GetString("Build", "Milestone", *aMilestone); + if (NS_FAILED(rv)) + return rv; + } + if (aVersion) { + rv = parser.GetString("Build", "BuildID", *aVersion); + if (NS_FAILED(rv)) + return rv; + } + return NS_OK; +} + static int InstallXULApp(nsIFile* aXULRunnerDir, const char *aAppLocation, @@ -242,13 +280,17 @@ int main(int argc, char* argv[]) IsArg(argv[1], "help") || IsArg(argv[1], "?"))) { - Usage(); + Usage(argv[0]); return 0; } if (argc == 2 && (IsArg(argv[1], "v") || IsArg(argv[1], "version"))) { - Output(PR_FALSE, "Mozilla XULRunner %s\n", GRE_BUILD_ID); + nsCAutoString milestone; + nsCAutoString version; + GetGREVersion(argv[0], &milestone, &version); + Output(PR_FALSE, "Mozilla XULRunner %s - %s\n", + milestone.get(), version.get()); return 0; } @@ -257,7 +299,7 @@ int main(int argc, char* argv[]) PRBool registerUser = IsArg(argv[1], "register-user"); if (registerGlobal || registerUser) { if (argc != 2) { - Usage(); + Usage(argv[0]); return 1; } @@ -275,7 +317,7 @@ int main(int argc, char* argv[]) registerUser = IsArg(argv[1], "unregister-user"); if (registerGlobal || registerUser) { if (argc != 2) { - Usage(); + Usage(argv[0]); return 1; } @@ -289,7 +331,7 @@ int main(int argc, char* argv[]) if (IsArg(argv[1], "find-gre")) { if (argc != 3) { - Usage(); + Usage(argv[0]); return 1; } @@ -314,17 +356,21 @@ int main(int argc, char* argv[]) if (IsArg(argv[1], "gre-version")) { if (argc != 2) { - Usage(); + Usage(argv[0]); return 1; } - printf("%s\n", GRE_BUILD_ID); + nsCAutoString milestone; + nsresult rv = GetGREVersion(argv[0], &milestone, nsnull); + if (NS_FAILED(rv)) + return 1; + printf("%s\n", milestone.get()); return 0; } if (IsArg(argv[1], "install-app")) { if (argc < 3 || argc > 5) { - Usage(); + Usage(argv[0]); return 1; } @@ -357,13 +403,13 @@ int main(int argc, char* argv[]) if (!(appDataFile && *appDataFile)) { if (argc < 2) { - Usage(); + Usage(argv[0]); return 1; } if (IsArg(argv[1], "app")) { if (argc == 2) { - Usage(); + Usage(argv[0]); return 1; } argv[1] = argv[0]; diff --git a/xulrunner/examples/simple/application.ini b/xulrunner/examples/simple/application.ini index dcb631af5d4..39c051422a1 100644 --- a/xulrunner/examples/simple/application.ini +++ b/xulrunner/examples/simple/application.ini @@ -13,7 +13,7 @@ Version=0.1 ; ; This field specifies your application's build ID (timestamp). This field is ; required. -BuildID=@BUILD_ID@ +BuildID=20070625 ; ; This field specifies a compact copyright notice for your application. This ; field is optional.