summaryrefslogtreecommitdiffstats
path: root/src/libs/xpcom18a4/xpcom/io
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-11 08:17:27 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-11 08:17:27 +0000
commitf215e02bf85f68d3a6106c2a1f4f7f063f819064 (patch)
tree6bb5b92c046312c4e95ac2620b10ddf482d3fa8b /src/libs/xpcom18a4/xpcom/io
parentInitial commit. (diff)
downloadvirtualbox-f215e02bf85f68d3a6106c2a1f4f7f063f819064.tar.xz
virtualbox-f215e02bf85f68d3a6106c2a1f4f7f063f819064.zip
Adding upstream version 7.0.14-dfsg.upstream/7.0.14-dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/libs/xpcom18a4/xpcom/io')
-rw-r--r--src/libs/xpcom18a4/xpcom/io/.cvsignore1
-rw-r--r--src/libs/xpcom18a4/xpcom/io/Makefile.in175
-rw-r--r--src/libs/xpcom18a4/xpcom/io/SpecialSystemDirectory.cpp770
-rw-r--r--src/libs/xpcom18a4/xpcom/io/SpecialSystemDirectory.h136
-rw-r--r--src/libs/xpcom18a4/xpcom/io/macDirectoryCopy.c784
-rw-r--r--src/libs/xpcom18a4/xpcom/io/macDirectoryCopy.h158
-rw-r--r--src/libs/xpcom18a4/xpcom/io/nsAppDirectoryServiceDefs.h120
-rw-r--r--src/libs/xpcom18a4/xpcom/io/nsAppFileLocationProvider.cpp608
-rw-r--r--src/libs/xpcom18a4/xpcom/io/nsAppFileLocationProvider.h67
-rw-r--r--src/libs/xpcom18a4/xpcom/io/nsBinaryStream.cpp663
-rw-r--r--src/libs/xpcom18a4/xpcom/io/nsBinaryStream.h125
-rw-r--r--src/libs/xpcom18a4/xpcom/io/nsByteArrayInputStream.cpp164
-rw-r--r--src/libs/xpcom18a4/xpcom/io/nsByteArrayInputStream.h59
-rw-r--r--src/libs/xpcom18a4/xpcom/io/nsDirectoryService.cpp1241
-rw-r--r--src/libs/xpcom18a4/xpcom/io/nsDirectoryService.h177
-rw-r--r--src/libs/xpcom18a4/xpcom/io/nsDirectoryServiceDefs.h197
-rw-r--r--src/libs/xpcom18a4/xpcom/io/nsDirectoryServiceUtils.h61
-rw-r--r--src/libs/xpcom18a4/xpcom/io/nsEscape.cpp491
-rw-r--r--src/libs/xpcom18a4/xpcom/io/nsEscape.h196
-rw-r--r--src/libs/xpcom18a4/xpcom/io/nsFastLoadFile.cpp2581
-rw-r--r--src/libs/xpcom18a4/xpcom/io/nsFastLoadFile.h566
-rw-r--r--src/libs/xpcom18a4/xpcom/io/nsFastLoadPtr.h112
-rw-r--r--src/libs/xpcom18a4/xpcom/io/nsFastLoadService.cpp571
-rw-r--r--src/libs/xpcom18a4/xpcom/io/nsFastLoadService.h71
-rw-r--r--src/libs/xpcom18a4/xpcom/io/nsIAsyncInputStream.idl136
-rw-r--r--src/libs/xpcom18a4/xpcom/io/nsIAsyncOutputStream.idl136
-rw-r--r--src/libs/xpcom18a4/xpcom/io/nsIBaseStream.idl45
-rw-r--r--src/libs/xpcom18a4/xpcom/io/nsIBinaryInputStream.idl121
-rw-r--r--src/libs/xpcom18a4/xpcom/io/nsIBinaryOutputStream.idl119
-rw-r--r--src/libs/xpcom18a4/xpcom/io/nsIByteArrayInputStream.idl48
-rw-r--r--src/libs/xpcom18a4/xpcom/io/nsIDirectoryEnumerator.idl68
-rw-r--r--src/libs/xpcom18a4/xpcom/io/nsIDirectoryService.idl138
-rw-r--r--src/libs/xpcom18a4/xpcom/io/nsIFastLoadFileControl.idl123
-rw-r--r--src/libs/xpcom18a4/xpcom/io/nsIFastLoadService.idl155
-rw-r--r--src/libs/xpcom18a4/xpcom/io/nsIFile.idl343
-rw-r--r--src/libs/xpcom18a4/xpcom/io/nsIInputStream.idl139
-rw-r--r--src/libs/xpcom18a4/xpcom/io/nsIInputStreamTee.idl61
-rw-r--r--src/libs/xpcom18a4/xpcom/io/nsILineInputStream.idl54
-rw-r--r--src/libs/xpcom18a4/xpcom/io/nsILocalFile.idl180
-rw-r--r--src/libs/xpcom18a4/xpcom/io/nsILocalFileMac.idl262
-rw-r--r--src/libs/xpcom18a4/xpcom/io/nsILocalFileOS2.idl90
-rw-r--r--src/libs/xpcom18a4/xpcom/io/nsIMultiplexInputStream.idl88
-rw-r--r--src/libs/xpcom18a4/xpcom/io/nsIObjectInputStream.idl87
-rw-r--r--src/libs/xpcom18a4/xpcom/io/nsIObjectOutputStream.idl131
-rw-r--r--src/libs/xpcom18a4/xpcom/io/nsIObservableInputStream.idl67
-rw-r--r--src/libs/xpcom18a4/xpcom/io/nsIObservableOutputStream.idl67
-rw-r--r--src/libs/xpcom18a4/xpcom/io/nsIOutputStream.idl167
-rw-r--r--src/libs/xpcom18a4/xpcom/io/nsIPipe.idl211
-rw-r--r--src/libs/xpcom18a4/xpcom/io/nsIScriptableInputStream.idl73
-rw-r--r--src/libs/xpcom18a4/xpcom/io/nsISeekableStream.idl102
-rw-r--r--src/libs/xpcom18a4/xpcom/io/nsIStorageStream.idl107
-rw-r--r--src/libs/xpcom18a4/xpcom/io/nsIStreamBufferAccess.idl191
-rw-r--r--src/libs/xpcom18a4/xpcom/io/nsIStringStream.idl138
-rw-r--r--src/libs/xpcom18a4/xpcom/io/nsIUnicharInputStream.h94
-rw-r--r--src/libs/xpcom18a4/xpcom/io/nsInputStreamTee.cpp224
-rw-r--r--src/libs/xpcom18a4/xpcom/io/nsLinebreakConverter.cpp495
-rw-r--r--src/libs/xpcom18a4/xpcom/io/nsLinebreakConverter.h156
-rw-r--r--src/libs/xpcom18a4/xpcom/io/nsLocalFile.h118
-rw-r--r--src/libs/xpcom18a4/xpcom/io/nsLocalFileCommon.cpp278
-rw-r--r--src/libs/xpcom18a4/xpcom/io/nsLocalFileMac.cpp3554
-rw-r--r--src/libs/xpcom18a4/xpcom/io/nsLocalFileMac.h137
-rw-r--r--src/libs/xpcom18a4/xpcom/io/nsLocalFileOS2.cpp1742
-rw-r--r--src/libs/xpcom18a4/xpcom/io/nsLocalFileOS2.h113
-rw-r--r--src/libs/xpcom18a4/xpcom/io/nsLocalFileOSX.cpp2542
-rw-r--r--src/libs/xpcom18a4/xpcom/io/nsLocalFileOSX.h119
-rw-r--r--src/libs/xpcom18a4/xpcom/io/nsLocalFileUnicode.h47
-rw-r--r--src/libs/xpcom18a4/xpcom/io/nsLocalFileUnix.cpp1724
-rw-r--r--src/libs/xpcom18a4/xpcom/io/nsLocalFileUnix.h132
-rw-r--r--src/libs/xpcom18a4/xpcom/io/nsLocalFileWin.cpp2440
-rw-r--r--src/libs/xpcom18a4/xpcom/io/nsLocalFileWin.h113
-rw-r--r--src/libs/xpcom18a4/xpcom/io/nsMultiplexInputStream.cpp389
-rw-r--r--src/libs/xpcom18a4/xpcom/io/nsMultiplexInputStream.h63
-rw-r--r--src/libs/xpcom18a4/xpcom/io/nsNativeCharsetUtils.cpp1280
-rw-r--r--src/libs/xpcom18a4/xpcom/io/nsNativeCharsetUtils.h66
-rw-r--r--src/libs/xpcom18a4/xpcom/io/nsPipe3.cpp1276
-rw-r--r--src/libs/xpcom18a4/xpcom/io/nsScriptableInputStream.cpp102
-rw-r--r--src/libs/xpcom18a4/xpcom/io/nsScriptableInputStream.h71
-rw-r--r--src/libs/xpcom18a4/xpcom/io/nsSegmentedBuffer.cpp207
-rw-r--r--src/libs/xpcom18a4/xpcom/io/nsSegmentedBuffer.h125
-rw-r--r--src/libs/xpcom18a4/xpcom/io/nsStorageStream.cpp563
-rw-r--r--src/libs/xpcom18a4/xpcom/io/nsStorageStream.h98
-rw-r--r--src/libs/xpcom18a4/xpcom/io/nsStreamUtils.cpp583
-rw-r--r--src/libs/xpcom18a4/xpcom/io/nsStreamUtils.h111
-rw-r--r--src/libs/xpcom18a4/xpcom/io/nsStringIO.h86
-rw-r--r--src/libs/xpcom18a4/xpcom/io/nsStringStream.cpp457
-rw-r--r--src/libs/xpcom18a4/xpcom/io/nsStringStream.h84
-rw-r--r--src/libs/xpcom18a4/xpcom/io/nsUnicharInputStream.cpp415
87 files changed, 33745 insertions, 0 deletions
diff --git a/src/libs/xpcom18a4/xpcom/io/.cvsignore b/src/libs/xpcom18a4/xpcom/io/.cvsignore
new file mode 100644
index 00000000..f3c7a7c5
--- /dev/null
+++ b/src/libs/xpcom18a4/xpcom/io/.cvsignore
@@ -0,0 +1 @@
+Makefile
diff --git a/src/libs/xpcom18a4/xpcom/io/Makefile.in b/src/libs/xpcom18a4/xpcom/io/Makefile.in
new file mode 100644
index 00000000..ed2f3f6d
--- /dev/null
+++ b/src/libs/xpcom18a4/xpcom/io/Makefile.in
@@ -0,0 +1,175 @@
+#
+# ***** 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 *****
+
+DEPTH = ../..
+topsrcdir = @top_srcdir@
+srcdir = @srcdir@
+VPATH = @srcdir@
+
+include $(DEPTH)/config/autoconf.mk
+
+MODULE = xpcom
+XPIDL_MODULE = xpcom_io
+LIBRARY_NAME = xpcomio_s
+GRE_MODULE = 1
+REQUIRES = string \
+ $(NULL)
+
+ifneq (,$(filter mac cocoa,$(MOZ_WIDGET_TOOLKIT)))
+REQUIRES += macmorefiles
+endif
+
+CPPSRCS = \
+ nsAppFileLocationProvider.cpp \
+ nsBinaryStream.cpp \
+ nsByteArrayInputStream.cpp \
+ nsDirectoryService.cpp \
+ nsEscape.cpp \
+ nsFastLoadFile.cpp \
+ nsFastLoadService.cpp \
+ nsInputStreamTee.cpp \
+ nsLinebreakConverter.cpp \
+ nsLocalFileCommon.cpp \
+ nsMultiplexInputStream.cpp \
+ nsPipe3.cpp \
+ nsStreamUtils.cpp \
+ nsScriptableInputStream.cpp \
+ nsSegmentedBuffer.cpp \
+ SpecialSystemDirectory.cpp \
+ nsStorageStream.cpp \
+ nsStringStream.cpp \
+ nsUnicharInputStream.cpp \
+ nsNativeCharsetUtils.cpp \
+ $(NULL)
+
+ifeq ($(MOZ_WIDGET_TOOLKIT),os2)
+CPPSRCS += nsLocalFileOS2.cpp
+else
+ifneq (,$(filter mac cocoa,$(MOZ_WIDGET_TOOLKIT)))
+CPPSRCS += nsLocalFileOSX.cpp
+else
+ifeq ($(MOZ_WIDGET_TOOLKIT),windows)
+CPPSRCS += nsLocalFileWin.cpp
+else
+CPPSRCS += nsLocalFileUnix.cpp
+endif # windows
+endif # mac
+endif # OS2
+
+EXPORTS = \
+ nsAppDirectoryServiceDefs.h \
+ nsDirectoryService.h \
+ nsDirectoryServiceUtils.h \
+ nsEscape.h \
+ nsFastLoadPtr.h \
+ nsFastLoadService.h \
+ nsIUnicharInputStream.h \
+ nsLinebreakConverter.h \
+ nsLocalFile.h \
+ nsMultiplexInputStream.h \
+ nsScriptableInputStream.h \
+ nsStorageStream.h \
+ nsStringIO.h \
+ nsStringStream.h \
+ nsStreamUtils.h \
+ nsNativeCharsetUtils.h \
+ $(NULL)
+
+ifeq ($(MOZ_WIDGET_TOOLKIT),os2)
+EXPORTS += nsLocalFileOS2.h
+else
+ifneq (,$(filter mac cocoa,$(MOZ_WIDGET_TOOLKIT)))
+EXPORTS += nsLocalFileOSX.h
+else
+ifeq ($(MOZ_WIDGET_TOOLKIT),windows)
+EXPORTS += nsLocalFileWin.h
+else
+EXPORTS += nsLocalFileUnix.h
+endif # windows
+endif # mac
+endif # os2
+
+XPIDLSRCS = \
+ nsIBinaryInputStream.idl \
+ nsIBinaryOutputStream.idl \
+ nsIByteArrayInputStream.idl \
+ nsIFastLoadFileControl.idl \
+ nsIFastLoadService.idl \
+ nsIInputStreamTee.idl \
+ nsILineInputStream.idl \
+ nsIMultiplexInputStream.idl \
+ nsIObjectInputStream.idl \
+ nsIObjectOutputStream.idl \
+ nsIPipe.idl \
+ nsISeekableStream.idl \
+ nsIStorageStream.idl \
+ nsIStringStream.idl \
+ nsIStreamBufferAccess.idl \
+ nsIAsyncInputStream.idl \
+ nsIAsyncOutputStream.idl \
+ $(NULL)
+
+ifneq (,$(filter mac cocoa,$(MOZ_WIDGET_TOOLKIT)))
+XPIDLSRCS += nsILocalFileMac.idl
+endif
+
+SDK_XPIDLSRCS = \
+ nsIDirectoryService.idl \
+ nsIFile.idl \
+ nsILocalFile.idl \
+ nsIInputStream.idl \
+ nsIOutputStream.idl \
+ nsIScriptableInputStream.idl \
+ $(NULL)
+
+SDK_HEADERS = \
+ nsDirectoryServiceDefs.h \
+ $(NULL)
+
+EXPORTS := $(addprefix $(srcdir)/, $(EXPORTS))
+
+# we don't want the shared lib, but we want to force the creation of a static lib.
+FORCE_STATIC_LIB = 1
+
+# Force use of PIC
+FORCE_USE_PIC = 1
+
+include $(topsrcdir)/config/rules.mk
+
+DEFINES += -D_IMPL_NS_COM
+
+LOCAL_INCLUDES = -I..
diff --git a/src/libs/xpcom18a4/xpcom/io/SpecialSystemDirectory.cpp b/src/libs/xpcom18a4/xpcom/io/SpecialSystemDirectory.cpp
new file mode 100644
index 00000000..5d1bc4fd
--- /dev/null
+++ b/src/libs/xpcom18a4/xpcom/io/SpecialSystemDirectory.cpp
@@ -0,0 +1,770 @@
+/* -*- 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, released
+ * March 31, 1998.
+ *
+ * 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):
+ * Doug Turner <dougt@netscape.com>
+ * IBM Corp.
+ *
+ * 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 "SpecialSystemDirectory.h"
+#include "nsString.h"
+#include "nsDependentString.h"
+
+
+#ifdef XP_MAC
+
+#include <Folders.h>
+#include <Files.h>
+#include <Memory.h>
+#include <Processes.h>
+#include <Gestalt.h>
+#include "nsIInternetConfigService.h"
+
+
+#if UNIVERSAL_INTERFACES_VERSION < 0x0340
+ enum {
+ kSystemDomain = -32766, /* Read-only system hierarchy.*/
+ kLocalDomain = -32765, /* All users of a single machine have access to these resources.*/
+ kNetworkDomain = -32764, /* All users configured to use a common network server has access to these resources.*/
+ kUserDomain = -32763, /* Read/write. Resources that are private to the user.*/
+ kClassicDomain = -32762, /* Domain referring to the currently configured Classic System Folder*/
+
+ kDomainLibraryFolderType = FOUR_CHAR_CODE('dlib')
+ };
+#endif
+
+#elif defined(XP_WIN)
+
+#include <windows.h>
+#include <shlobj.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#elif defined(XP_OS2)
+
+#define MAX_PATH _MAX_PATH
+#define INCL_WINWORKPLACE
+#define INCL_DOSMISC
+#define INCL_DOSMODULEMGR
+#define INCL_DOSPROCESS
+#define INCL_WINSHELLDATA
+#include <os2.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include "prenv.h"
+
+#elif defined(XP_UNIX)
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/param.h>
+#include "prenv.h"
+# if defined(XP_MACOSX) && defined(VBOX_WITH_NEWER_OSX_SDK)
+# include <Folders.h>
+# endif
+
+#elif defined(XP_BEOS)
+
+#include <FindDirectory.h>
+#include <Path.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/param.h>
+#include <OS.h>
+#include <image.h>
+#include "prenv.h"
+
+#endif
+
+#if defined(VMS)
+#include <unixlib.h>
+#endif
+
+
+
+#if defined (XP_WIN)
+typedef BOOL (WINAPI * GetSpecialPathProc) (HWND hwndOwner, LPSTR lpszPath, int nFolder, BOOL fCreate);
+GetSpecialPathProc gGetSpecialPathProc = NULL;
+static HINSTANCE gShell32DLLInst = NULL;
+#endif
+NS_COM void StartupSpecialSystemDirectory()
+{
+#if defined (XP_WIN)
+ /* On windows, the old method to get file locations is incredibly slow.
+ As of this writing, 3 calls to GetWindowsFolder accounts for 3% of mozilla
+ startup. Replacing these older calls with a single call to SHGetSpecialFolderPath
+ effectively removes these calls from the performace radar. We need to
+ support the older way of file location lookup on systems that do not have
+ IE4. (Note: gets the ansi version: SHGetSpecialFolderPathA).
+ */
+ gShell32DLLInst = LoadLibrary("Shell32.dll");
+ if(gShell32DLLInst)
+ {
+ gGetSpecialPathProc = (GetSpecialPathProc) GetProcAddress(gShell32DLLInst,
+ "SHGetSpecialFolderPathA");
+ }
+#endif
+}
+
+NS_COM void ShutdownSpecialSystemDirectory()
+{
+#if defined (XP_WIN)
+ if (gShell32DLLInst)
+ {
+ FreeLibrary(gShell32DLLInst);
+ gShell32DLLInst = NULL;
+ gGetSpecialPathProc = NULL;
+ }
+#endif
+}
+
+#if defined (XP_WIN)
+
+//----------------------------------------------------------------------------------------
+static nsresult GetWindowsFolder(int folder, nsILocalFile** aFile)
+//----------------------------------------------------------------------------------------
+{
+ if (gGetSpecialPathProc) {
+ TCHAR path[MAX_PATH];
+ HRESULT result = gGetSpecialPathProc(NULL, path, folder, true);
+
+ if (!SUCCEEDED(result))
+ return NS_ERROR_FAILURE;
+
+ // Append the trailing slash
+ int len = strlen(path);
+ if (len>1 && path[len-1] != '\\')
+ {
+ path[len] = '\\';
+ path[len + 1] = '\0';
+ }
+
+ return NS_NewNativeLocalFile(nsDependentCString(path),
+ PR_TRUE,
+ aFile);
+ }
+
+ nsresult rv = NS_ERROR_FAILURE;
+ LPMALLOC pMalloc = NULL;
+ LPSTR pBuffer = NULL;
+ LPITEMIDLIST pItemIDList = NULL;
+ int len;
+
+ // Get the shell's allocator.
+ if (!SUCCEEDED(SHGetMalloc(&pMalloc)))
+ return NS_ERROR_FAILURE;
+
+ // Allocate a buffer
+ if ((pBuffer = (LPSTR) pMalloc->Alloc(MAX_PATH + 2)) == NULL)
+ return NS_ERROR_FAILURE;
+
+ // Get the PIDL for the folder.
+ if (!SUCCEEDED(SHGetSpecialFolderLocation(
+ NULL, folder, &pItemIDList)))
+ goto Clean;
+
+ if (!SUCCEEDED(SHGetPathFromIDList(pItemIDList, pBuffer)))
+ goto Clean;
+
+ // Append the trailing slash
+ len = strlen(pBuffer);
+ pBuffer[len] = '\\';
+ pBuffer[len + 1] = '\0';
+
+ // Assign the directory
+ rv = NS_NewNativeLocalFile(nsDependentCString(pBuffer),
+ PR_TRUE,
+ aFile);
+
+Clean:
+ // Clean up.
+ if (pItemIDList)
+ pMalloc->Free(pItemIDList);
+ if (pBuffer)
+ pMalloc->Free(pBuffer);
+
+ pMalloc->Release();
+
+ return rv;
+}
+
+#endif // XP_WIN
+
+
+
+
+nsresult
+GetSpecialSystemDirectory(SystemDirectories aSystemSystemDirectory,
+ nsILocalFile** aFile)
+{
+#ifdef XP_MAC
+ OSErr err;
+ short vRefNum;
+ long dirID;
+#endif
+
+ switch (aSystemSystemDirectory)
+ {
+ case OS_DriveDirectory:
+#if defined (XP_WIN)
+ {
+ char path[_MAX_PATH];
+ PRInt32 len = GetWindowsDirectory( path, _MAX_PATH );
+ if (len)
+ {
+ if ( path[1] == ':' && path[2] == '\\' )
+ path[3] = 0;
+ }
+
+ return NS_NewNativeLocalFile(nsDependentCString(path),
+ PR_TRUE,
+ aFile);
+ }
+#elif defined(XP_OS2)
+ {
+ ULONG ulBootDrive = 0;
+ char buffer[] = " :\\OS2\\";
+ DosQuerySysInfo( QSV_BOOT_DRIVE, QSV_BOOT_DRIVE,
+ &ulBootDrive, sizeof ulBootDrive);
+ buffer[0] = 'A' - 1 + ulBootDrive; // duh, 1-based index...
+
+ return NS_NewNativeLocalFile(nsDependentCString(buffer),
+ PR_TRUE,
+ aFile);
+ }
+#elif defined(XP_MAC)
+ {
+ return nsIFileFromOSType(kVolumeRootFolderType, aFile);
+ }
+#else
+ return NS_NewNativeLocalFile(nsDependentCString("/"),
+ PR_TRUE,
+ aFile);
+
+#endif
+
+ case OS_TemporaryDirectory:
+#if defined (XP_WIN)
+ {
+ char path[_MAX_PATH];
+ DWORD len = GetTempPath(_MAX_PATH, path);
+ return NS_NewNativeLocalFile(nsDependentCString(path),
+ PR_TRUE,
+ aFile);
+ }
+#elif defined(XP_OS2)
+ {
+ char buffer[CCHMAXPATH] = "";
+ char *c = getenv( "TMP");
+ if( c) strcpy( buffer, c);
+ else
+ {
+ c = getenv( "TEMP");
+ if( c) strcpy( buffer, c);
+ }
+
+ return NS_NewNativeLocalFile(nsDependentCString(buffer),
+ PR_TRUE,
+ aFile);
+ }
+#elif defined(XP_MAC)
+ return nsIFileFromOSType(kTemporaryFolderType, aFile);
+
+#elif defined(XP_MACOSX)
+ {
+ return GetOSXFolderType(kUserDomain, kTemporaryFolderType, aFile);
+ }
+
+#elif defined(XP_UNIX) || defined(XP_BEOS)
+ {
+ static const char *tPath = nsnull;
+ if (!tPath) {
+ tPath = PR_GetEnv("TMPDIR");
+ if (!tPath || !*tPath) {
+ tPath = PR_GetEnv("TMP");
+ if (!tPath || !*tPath) {
+ tPath = PR_GetEnv("TEMP");
+ if (!tPath || !*tPath) {
+ tPath = "/tmp/";
+ }
+ }
+ }
+ }
+ return NS_NewNativeLocalFile(nsDependentCString(tPath),
+ PR_TRUE,
+ aFile);
+ }
+#else
+ break;
+#endif
+
+#if defined(XP_MAC)
+ case Mac_SystemDirectory:
+ return nsIFileFromOSType(kSystemFolderType, aFile);
+
+ case Mac_DesktopDirectory:
+ return nsIFileFromOSType(kDesktopFolderType, aFile);
+
+ case Mac_TrashDirectory:
+ return nsIFileFromOSType(kTrashFolderType, aFile);
+
+ case Mac_StartupDirectory:
+ return nsIFileFromOSType(kStartupFolderType, aFile);
+
+ case Mac_ShutdownDirectory:
+ return nsIFileFromOSType(kShutdownFolderType, aFile);
+
+ case Mac_AppleMenuDirectory:
+ return nsIFileFromOSType(kAppleMenuFolderType, aFile);
+
+ case Mac_ControlPanelDirectory:
+ return nsIFileFromOSType(kControlPanelFolderType, aFile);
+
+ case Mac_ExtensionDirectory:
+ return nsIFileFromOSType(kExtensionFolderType, aFile);
+
+ case Mac_FontsDirectory:
+ return nsIFileFromOSType(kFontsFolderType, aFile);
+
+ case Mac_ClassicPreferencesDirectory:
+ {
+ // whether Mac OS X or pre-Mac OS X, return Classic's Prefs folder
+ short domain;
+ long response;
+ err = ::Gestalt(gestaltSystemVersion, &response);
+ domain = (!err && response >= 0x00001000) ? kClassicDomain : kOnSystemDisk;
+ err = ::FindFolder(domain, kPreferencesFolderType, true, &vRefNum, &dirID);
+ if (!err) {
+ err = ::FSMakeFSSpec(vRefNum, dirID, "\p", &mSpec);
+ }
+ return NS_FILE_RESULT(err);
+ }
+
+ case Mac_PreferencesDirectory:
+ {
+ // if Mac OS X, return Mac OS X's Prefs folder
+ // if pre-Mac OS X, return Mac OS's Prefs folder
+ err = ::FindFolder(kOnSystemDisk, kPreferencesFolderType, true, &vRefNum, &dirID);
+ if (!err) {
+ err = ::FSMakeFSSpec(vRefNum, dirID, "\p", &mSpec);
+ }
+ return NS_FILE_RESULT(err);
+ }
+
+ case Mac_DocumentsDirectory:
+ return nsIFileFromOSType(kDocumentsFolderType, aFile);
+
+ case Mac_InternetSearchDirectory:
+ return nsIFileFromOSType(kInternetSearchSitesFolderType, aFile);
+
+ case Mac_DefaultDownloadDirectory:
+ return nsIFileFromOSType(kDefaultDownloadFolderType, aFile);
+
+ case Mac_UserLibDirectory:
+ {
+ FSSpec spec;
+ err = ::FindFolder(kUserDomain, kDomainLibraryFolderType, true, &vRefNum, &dirID);
+ if (!err) {
+ err = ::FSMakeFSSpec(vRefNum, dirID, "\p", &spec);
+ }
+
+ return NS_NewLocalFileWithFSSpec(&spec, PR_FALUE, aFile);
+ }
+#endif
+
+#if defined (XP_WIN)
+ case Win_SystemDirectory:
+ {
+ char path[_MAX_PATH];
+ PRInt32 len = GetSystemDirectory( path, _MAX_PATH );
+
+ // Need enough space to add the trailing backslash
+ if (len > _MAX_PATH-2)
+ break;
+ path[len] = '\\';
+ path[len+1] = '\0';
+
+ return NS_NewNativeLocalFile(nsDependentCString(path),
+ PR_TRUE,
+ aFile);
+ }
+
+ case Win_WindowsDirectory:
+ {
+ char path[_MAX_PATH];
+ PRInt32 len = GetWindowsDirectory( path, _MAX_PATH );
+
+ // Need enough space to add the trailing backslash
+ if (len > _MAX_PATH-2)
+ break;
+
+ path[len] = '\\';
+ path[len+1] = '\0';
+
+ return NS_NewNativeLocalFile(nsDependentCString(path),
+ PR_TRUE,
+ aFile);
+ }
+
+ case Win_HomeDirectory:
+ {
+ char path[_MAX_PATH];
+ if (GetEnvironmentVariable(TEXT("HOME"), path, _MAX_PATH) > 0)
+ {
+ PRInt32 len = strlen(path);
+ // Need enough space to add the trailing backslash
+ if (len > _MAX_PATH - 2)
+ break;
+
+ path[len] = '\\';
+ path[len+1] = '\0';
+
+ return NS_NewNativeLocalFile(nsDependentCString(path),
+ PR_TRUE,
+ aFile);
+ }
+
+ if (GetEnvironmentVariable(TEXT("HOMEDRIVE"), path, _MAX_PATH) > 0)
+ {
+ char temp[_MAX_PATH];
+ if (GetEnvironmentVariable(TEXT("HOMEPATH"), temp, _MAX_PATH) > 0)
+ strncat(path, temp, _MAX_PATH);
+
+ PRInt32 len = strlen(path);
+
+ // Need enough space to add the trailing backslash
+ if (len > _MAX_PATH - 2)
+ break;
+
+ path[len] = '\\';
+ path[len+1] = '\0';
+
+ return NS_NewNativeLocalFile(nsDependentCString(path),
+ PR_TRUE,
+ aFile);
+ }
+ }
+ case Win_Desktop:
+ {
+ return GetWindowsFolder(CSIDL_DESKTOP, aFile);
+ }
+ case Win_Programs:
+ {
+ return GetWindowsFolder(CSIDL_PROGRAMS, aFile);
+ }
+ case Win_Controls:
+ {
+ return GetWindowsFolder(CSIDL_CONTROLS, aFile);
+ }
+ case Win_Printers:
+ {
+ return GetWindowsFolder(CSIDL_PRINTERS, aFile);
+ }
+ case Win_Personal:
+ {
+ return GetWindowsFolder(CSIDL_PERSONAL, aFile);
+ }
+ case Win_Favorites:
+ {
+ return GetWindowsFolder(CSIDL_FAVORITES, aFile);
+ }
+ case Win_Startup:
+ {
+ return GetWindowsFolder(CSIDL_STARTUP, aFile);
+ }
+ case Win_Recent:
+ {
+ return GetWindowsFolder(CSIDL_RECENT, aFile);
+ }
+ case Win_Sendto:
+ {
+ return GetWindowsFolder(CSIDL_SENDTO, aFile);
+ }
+ case Win_Bitbucket:
+ {
+ return GetWindowsFolder(CSIDL_BITBUCKET, aFile);
+ }
+ case Win_Startmenu:
+ {
+ return GetWindowsFolder(CSIDL_STARTMENU, aFile);
+ }
+ case Win_Desktopdirectory:
+ {
+ return GetWindowsFolder(CSIDL_DESKTOPDIRECTORY, aFile);
+ }
+ case Win_Drives:
+ {
+ return GetWindowsFolder(CSIDL_DRIVES, aFile);
+ }
+ case Win_Network:
+ {
+ return GetWindowsFolder(CSIDL_NETWORK, aFile);
+ }
+ case Win_Nethood:
+ {
+ return GetWindowsFolder(CSIDL_NETHOOD, aFile);
+ }
+ case Win_Fonts:
+ {
+ return GetWindowsFolder(CSIDL_FONTS, aFile);
+ }
+ case Win_Templates:
+ {
+ return GetWindowsFolder(CSIDL_TEMPLATES, aFile);
+ }
+ case Win_Common_Startmenu:
+ {
+ return GetWindowsFolder(CSIDL_COMMON_STARTMENU, aFile);
+ }
+ case Win_Common_Programs:
+ {
+ return GetWindowsFolder(CSIDL_COMMON_PROGRAMS, aFile);
+ }
+ case Win_Common_Startup:
+ {
+ return GetWindowsFolder(CSIDL_COMMON_STARTUP, aFile);
+ }
+ case Win_Common_Desktopdirectory:
+ {
+ return GetWindowsFolder(CSIDL_COMMON_DESKTOPDIRECTORY, aFile);
+ }
+ case Win_Appdata:
+ {
+ return GetWindowsFolder(CSIDL_APPDATA, aFile);
+ }
+ case Win_Printhood:
+ {
+ return GetWindowsFolder(CSIDL_PRINTHOOD, aFile);
+ }
+ case Win_Cookies:
+ {
+ return GetWindowsFolder(CSIDL_COOKIES, aFile);
+ }
+#endif // XP_WIN
+
+#if defined(XP_UNIX)
+ case Unix_LocalDirectory:
+ return NS_NewNativeLocalFile(nsDependentCString("/usr/local/netscape/"),
+ PR_TRUE,
+ aFile);
+ case Unix_LibDirectory:
+ return NS_NewNativeLocalFile(nsDependentCString("/usr/local/lib/netscape/"),
+ PR_TRUE,
+ aFile);
+
+ case Unix_HomeDirectory:
+#ifdef VMS
+ {
+ char *pHome;
+ pHome = getenv("HOME");
+ if (*pHome == '/') {
+ return NS_NewNativeLocalFile(nsDependentCString(pHome),
+ PR_TRUE,
+ aFile);
+
+ }
+ else
+ {
+ return NS_NewNativeLocalFile(nsDependentCString(decc$translate_vms(pHome)),
+ PR_TRUE,
+ aFile);
+ }
+ }
+#else
+ return NS_NewNativeLocalFile(nsDependentCString(PR_GetEnv("HOME")),
+ PR_TRUE,
+ aFile);
+
+#endif
+
+#endif
+
+#ifdef XP_BEOS
+ case BeOS_SettingsDirectory:
+ {
+ char path[MAXPATHLEN];
+ find_directory(B_USER_SETTINGS_DIRECTORY, 0, 0, path, MAXPATHLEN);
+ // Need enough space to add the trailing backslash
+ int len = strlen(path);
+ if (len > MAXPATHLEN-2)
+ break;
+ path[len] = '/';
+ path[len+1] = '\0';
+ return NS_NewNativeLocalFile(nsDependentCString(path),
+ PR_TRUE,
+ aFile);
+ }
+
+ case BeOS_HomeDirectory:
+ {
+ char path[MAXPATHLEN];
+ find_directory(B_USER_DIRECTORY, 0, 0, path, MAXPATHLEN);
+ // Need enough space to add the trailing backslash
+ int len = strlen(path);
+ if (len > MAXPATHLEN-2)
+ break;
+ path[len] = '/';
+ path[len+1] = '\0';
+
+ return NS_NewNativeLocalFile(nsDependentCString(path),
+ PR_TRUE,
+ aFile);
+ }
+
+ case BeOS_DesktopDirectory:
+ {
+ char path[MAXPATHLEN];
+ find_directory(B_DESKTOP_DIRECTORY, 0, 0, path, MAXPATHLEN);
+ // Need enough space to add the trailing backslash
+ int len = strlen(path);
+ if (len > MAXPATHLEN-2)
+ break;
+ path[len] = '/';
+ path[len+1] = '\0';
+
+ return NS_NewNativeLocalFile(nsDependentCString(path),
+ PR_TRUE,
+ aFile);
+ }
+
+ case BeOS_SystemDirectory:
+ {
+ char path[MAXPATHLEN];
+ find_directory(B_BEOS_DIRECTORY, 0, 0, path, MAXPATHLEN);
+ // Need enough space to add the trailing backslash
+ int len = strlen(path);
+ if (len > MAXPATHLEN-2)
+ break;
+ path[len] = '/';
+ path[len+1] = '\0';
+
+ return NS_NewNativeLocalFile(nsDependentCString(path),
+ PR_TRUE,
+ aFile);
+ }
+#endif
+#ifdef XP_OS2
+ case OS2_SystemDirectory:
+ {
+ ULONG ulBootDrive = 0;
+ char buffer[] = " :\\OS2\\System\\";
+ DosQuerySysInfo( QSV_BOOT_DRIVE, QSV_BOOT_DRIVE,
+ &ulBootDrive, sizeof ulBootDrive);
+ buffer[0] = 'A' - 1 + ulBootDrive; // duh, 1-based index...
+
+ return NS_NewNativeLocalFile(nsDependentCString(buffer),
+ PR_TRUE,
+ aFile);
+ }
+
+ case OS2_OS2Directory:
+ {
+ ULONG ulBootDrive = 0;
+ char buffer[] = " :\\OS2\\";
+ DosQuerySysInfo( QSV_BOOT_DRIVE, QSV_BOOT_DRIVE,
+ &ulBootDrive, sizeof ulBootDrive);
+ buffer[0] = 'A' - 1 + ulBootDrive; // duh, 1-based index...
+
+ return NS_NewNativeLocalFile(nsDependentCString(buffer),
+ PR_TRUE,
+ aFile);
+ }
+
+ case OS2_HomeDirectory:
+ {
+ nsresult rv;
+ char *tPath = PR_GetEnv("MOZILLA_HOME");
+ char buffer[CCHMAXPATH];
+ /* If MOZILLA_HOME is not set, use GetCurrentProcessDirectory */
+ /* To ensure we get a long filename system */
+ if (!tPath || !*tPath) {
+ PPIB ppib;
+ PTIB ptib;
+ DosGetInfoBlocks( &ptib, &ppib);
+ DosQueryModuleName( ppib->pib_hmte, CCHMAXPATH, buffer);
+ *strrchr( buffer, '\\') = '\0'; // XXX DBCS misery
+ tPath = buffer;
+ }
+ rv = NS_NewNativeLocalFile(nsDependentCString(tPath),
+ PR_TRUE,
+ aFile);
+
+ PrfWriteProfileString(HINI_USERPROFILE, "Mozilla", "Home", tPath);
+ return rv;
+ }
+
+ case OS2_DesktopDirectory:
+ {
+ char szPath[CCHMAXPATH + 1];
+ BOOL fSuccess;
+ fSuccess = WinQueryActiveDesktopPathname (szPath, sizeof(szPath));
+ int len = strlen (szPath);
+ if (len > CCHMAXPATH -1)
+ break;
+ szPath[len] = '\\';
+ szPath[len + 1] = '\0';
+
+ return NS_NewNativeLocalFile(nsDependentCString(szPath),
+ PR_TRUE,
+ aFile);
+ }
+#endif
+ default:
+ break;
+ }
+ return NS_ERROR_NOT_AVAILABLE;
+}
+
+#if defined (XP_MACOSX)
+nsresult
+GetOSXFolderType(short aDomain, OSType aFolderType, nsILocalFile **localFile)
+{
+ OSErr err;
+ FSRef fsRef;
+ nsresult rv = NS_ERROR_FAILURE;
+
+ err = ::FSFindFolder(aDomain, aFolderType, kCreateFolder, &fsRef);
+ if (err == noErr)
+ {
+ NS_NewLocalFile(EmptyString(), PR_TRUE, localFile);
+ nsCOMPtr<nsILocalFileMac> localMacFile(do_QueryInterface(*localFile));
+ if (localMacFile)
+ rv = localMacFile->InitWithFSRef(&fsRef);
+ }
+ return rv;
+}
+#endif
+
diff --git a/src/libs/xpcom18a4/xpcom/io/SpecialSystemDirectory.h b/src/libs/xpcom18a4/xpcom/io/SpecialSystemDirectory.h
new file mode 100644
index 00000000..b4aaf30e
--- /dev/null
+++ b/src/libs/xpcom18a4/xpcom/io/SpecialSystemDirectory.h
@@ -0,0 +1,136 @@
+/* -*- 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 client code, released
+ * March 31, 1998.
+ *
+ * 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):
+ * Doug Turner <dougt@netscape.com>
+ * IBM Corp.
+ *
+ * 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 ***** */
+
+#ifndef _SPECIALSYSTEMDIRECTORY_H_
+#define _SPECIALSYSTEMDIRECTORY_H_
+
+#include "nscore.h"
+#include "nsILocalFile.h"
+
+#if defined(XP_MAC) || defined(XP_MACOSX)
+# ifndef VBOX_WITH_NEWER_OSX_SDK
+# include <Types.h>
+# endif
+#include "nsILocalFileMac.h"
+#include "prenv.h"
+#endif
+
+
+extern NS_COM void StartupSpecialSystemDirectory();
+extern NS_COM void ShutdownSpecialSystemDirectory();
+
+
+enum SystemDirectories {
+ OS_DriveDirectory = 1,
+ OS_TemporaryDirectory = 2,
+ OS_CurrentProcessDirectory= 3,
+ OS_CurrentWorkingDirectory= 4,
+ XPCOM_CurrentProcessComponentDirectory= 5,
+ XPCOM_CurrentProcessComponentRegistry= 6,
+
+ Moz_BinDirectory = 100 ,
+ Mac_SystemDirectory = 101,
+ Mac_DesktopDirectory = 102,
+ Mac_TrashDirectory = 103,
+ Mac_StartupDirectory = 104,
+ Mac_ShutdownDirectory = 105,
+ Mac_AppleMenuDirectory = 106,
+ Mac_ControlPanelDirectory = 107,
+ Mac_ExtensionDirectory = 108,
+ Mac_FontsDirectory = 109,
+ Mac_ClassicPreferencesDirectory = 110,
+ Mac_DocumentsDirectory = 111,
+ Mac_InternetSearchDirectory = 112,
+ Mac_DefaultDownloadDirectory = 113,
+ Mac_UserLibDirectory = 114,
+ Mac_PreferencesDirectory = 115,
+
+ Win_SystemDirectory = 201,
+ Win_WindowsDirectory = 202,
+ Win_HomeDirectory = 203,
+ Win_Desktop = 204,
+ Win_Programs = 205,
+ Win_Controls = 206,
+ Win_Printers = 207,
+ Win_Personal = 208,
+ Win_Favorites = 209,
+ Win_Startup = 210,
+ Win_Recent = 211,
+ Win_Sendto = 212,
+ Win_Bitbucket = 213,
+ Win_Startmenu = 214,
+ Win_Desktopdirectory = 215,
+ Win_Drives = 216,
+ Win_Network = 217,
+ Win_Nethood = 218,
+ Win_Fonts = 219,
+ Win_Templates = 220,
+ Win_Common_Startmenu = 221,
+ Win_Common_Programs = 222,
+ Win_Common_Startup = 223,
+ Win_Common_Desktopdirectory = 224,
+ Win_Appdata = 225,
+ Win_Printhood = 226,
+ Win_Cookies = 227,
+
+ Unix_LocalDirectory = 301,
+ Unix_LibDirectory = 302,
+ Unix_HomeDirectory = 303,
+
+ BeOS_SettingsDirectory = 401,
+ BeOS_HomeDirectory = 402,
+ BeOS_DesktopDirectory = 403,
+ BeOS_SystemDirectory = 404,
+
+ OS2_SystemDirectory = 501,
+ OS2_OS2Directory = 502,
+ OS2_DesktopDirectory = 503,
+ OS2_HomeDirectory = 504
+};
+
+nsresult
+GetSpecialSystemDirectory(SystemDirectories aSystemSystemDirectory,
+ nsILocalFile** aFile);
+#if defined(XP_MACOSX)
+nsresult
+GetOSXFolderType(short aDomain, OSType aFolderType, nsILocalFile **localFile);
+#endif
+
+#endif
diff --git a/src/libs/xpcom18a4/xpcom/io/macDirectoryCopy.c b/src/libs/xpcom18a4/xpcom/io/macDirectoryCopy.c
new file mode 100644
index 00000000..838dffa2
--- /dev/null
+++ b/src/libs/xpcom18a4/xpcom/io/macDirectoryCopy.c
@@ -0,0 +1,784 @@
+/*
+** Apple Macintosh Developer Technical Support
+**
+** DirectoryCopy: A robust, general purpose directory copy routine.
+**
+** by Jim Luther, Apple Developer Technical Support Emeritus
+**
+** File: DirectoryCopy.c
+**
+** Copyright © 1992-1998 Apple Computer, Inc.
+** All rights reserved.
+**
+** You may incorporate this sample code into your applications without
+** restriction, though the sample code has been provided "AS IS" and the
+** responsibility for its operation is 100% yours. However, what you are
+** not permitted to do is to redistribute the source as "DSC Sample Code"
+** after having made changes. If you're going to re-distribute the source,
+** we require that you make it clear in the source that the code was
+** descended from Apple Sample Code, but that you've made changes.
+*/
+
+// Modified to allow renaming the destination folder
+
+#include <Types.h>
+#include <Errors.h>
+#include <Memory.h>
+#include <Files.h>
+#include <Script.h>
+
+#define __COMPILINGMOREFILES
+
+#include "MoreFiles.h"
+#include "MoreFilesExtras.h"
+#include "MoreDesktopMgr.h"
+#include "FileCopy.h"
+#include "MacDirectoryCopy.h"
+#include <string.h>
+
+
+/*****************************************************************************/
+
+enum
+{
+ getNextItemOp = 1, /* couldn't access items in this directory - no access privileges */
+ copyDirCommentOp = 2, /* couldn't copy directory's Finder comment */
+ copyDirAccessPrivsOp = 3, /* couldn't copy directory's AFP access privileges */
+ copyDirFMAttributesOp = 4, /* couldn't copy directory's File Manager attributes */
+ dirCreateOp = 5, /* couldn't create destination directory */
+ fileCopyOp = 6 /* couldn't copy file */
+};
+
+/*****************************************************************************/
+
+
+
+#define CallCopyErrProc(userRoutine, error, failedOperation, srcVRefNum, srcDirID, srcName, dstVRefNum, dstDirID, dstName) \
+ (*(userRoutine))((error), (failedOperation), (srcVRefNum), (srcDirID), (srcName), (dstVRefNum), (dstDirID), (dstName))
+
+/*****************************************************************************/
+
+typedef pascal Boolean (*CopyFilterProcPtr) (const CInfoPBRec * const cpbPtr);
+
+/* ¦ Prototype for the CopyFilterProc function.
+ This is the prototype for the CopyFilterProc function called by
+ FilteredDirectoryCopy and GetLevelSize. If true is returned,
+ the file/folder is included in the copy, otherwise it is excluded.
+
+ pb input: Points to the CInfoPBRec for the item under consideration.
+
+ __________
+
+ Also see: FilteredDirectoryCopy, FSpFilteredDirectoryCopy
+*/
+
+#define CallCopyFilterProc(userRoutine, cpbPtr) (*(userRoutine))((cpbPtr))
+
+
+
+/*****************************************************************************/
+
+/* local constants */
+
+enum
+{
+ dirCopyBigCopyBuffSize = 0x00004000,
+ dirCopyMinCopyBuffSize = 0x00000200
+};
+
+
+/*****************************************************************************/
+
+/* local data structures */
+
+/* The EnumerateGlobals structure is used to minimize the amount of
+** stack space used when recursively calling CopyLevel and to hold
+** global information that might be needed at any time. */
+
+#if PRAGMA_STRUCT_ALIGN
+#pragma options align=mac68k
+#endif
+struct EnumerateGlobals
+{
+ Ptr copyBuffer; /* pointer to buffer used for file copy operations */
+ long bufferSize; /* the size of the copy buffer */
+ CopyErrProcPtr errorHandler; /* pointer to error handling function */
+ CopyFilterProcPtr copyFilterProc; /* pointer to filter function */
+ OSErr error; /* temporary holder of results - saves 2 bytes of stack each level */
+ Boolean bailout; /* set to true to by error handling function if fatal error */
+ short destinationVRefNum; /* the destination vRefNum */
+ Str63 itemName; /* the name of the current item */
+ CInfoPBRec myCPB; /* the parameter block used for PBGetCatInfo calls */
+};
+#if PRAGMA_STRUCT_ALIGN
+#pragma options align=reset
+#endif
+
+typedef struct EnumerateGlobals EnumerateGlobals;
+typedef EnumerateGlobals *EnumerateGlobalsPtr;
+
+
+/* The PreflightGlobals structure is used to minimize the amount of
+** stack space used when recursively calling GetLevelSize and to hold
+** global information that might be needed at any time. */
+
+#if PRAGMA_STRUCT_ALIGN
+#pragma options align=mac68k
+#endif
+struct PreflightGlobals
+{
+ OSErr result; /* temporary holder of results - saves 2 bytes of stack each level */
+ Str63 itemName; /* the name of the current item */
+ CInfoPBRec myCPB; /* the parameter block used for PBGetCatInfo calls */
+
+ unsigned long dstBlksPerAllocBlk; /* the number of 512 byte blocks per allocation block on destination */
+
+ unsigned long allocBlksNeeded; /* the total number of allocation blocks needed */
+
+ unsigned long tempBlocks; /* temporary storage for calculations (save some stack space) */
+ CopyFilterProcPtr copyFilterProc; /* pointer to filter function */
+};
+#if PRAGMA_STRUCT_ALIGN
+#pragma options align=reset
+#endif
+
+typedef struct PreflightGlobals PreflightGlobals;
+typedef PreflightGlobals *PreflightGlobalsPtr;
+
+/*****************************************************************************/
+
+/* static prototypes */
+
+static void GetLevelSize(long currentDirID,
+ PreflightGlobals *theGlobals);
+
+static OSErr PreflightDirectoryCopySpace(short srcVRefNum,
+ long srcDirID,
+ short dstVRefNum,
+ CopyFilterProcPtr copyFilterProc,
+ Boolean *spaceOK);
+
+static void CopyLevel(long sourceDirID,
+ long dstDirID,
+ EnumerateGlobals *theGlobals);
+
+/*****************************************************************************/
+
+static void GetLevelSize(long currentDirID,
+ PreflightGlobals *theGlobals)
+{
+ short index = 1;
+
+ do
+ {
+ theGlobals->myCPB.dirInfo.ioFDirIndex = index;
+ theGlobals->myCPB.dirInfo.ioDrDirID = currentDirID; /* we need to do this every time */
+ /* through, since GetCatInfo */
+ /* returns ioFlNum in this field */
+ theGlobals->result = PBGetCatInfoSync(&theGlobals->myCPB);
+ if ( theGlobals->result == noErr )
+ {
+ if ( (theGlobals->copyFilterProc == NULL) ||
+ CallCopyFilterProc(theGlobals->copyFilterProc, &theGlobals->myCPB) ) /* filter if filter proc was supplied */
+ {
+ /* Either there's no filter proc OR the filter proc says to use this item */
+ if ( (theGlobals->myCPB.dirInfo.ioFlAttrib & ioDirMask) != 0 )
+ {
+ /* we have a directory */
+
+ GetLevelSize(theGlobals->myCPB.dirInfo.ioDrDirID, theGlobals); /* recurse */
+ theGlobals->result = noErr; /* clear error return on way back */
+ }
+ else
+ {
+ /* We have a file - add its allocation blocks to allocBlksNeeded. */
+ /* Since space on Mac OS disks is always allocated in allocation blocks, */
+ /* this takes into account rounding up to the end of an allocation block. */
+
+ /* get number of 512-byte blocks needed for data fork */
+ if ( ((unsigned long)theGlobals->myCPB.hFileInfo.ioFlLgLen & 0x000001ff) != 0 )
+ {
+ theGlobals->tempBlocks = ((unsigned long)theGlobals->myCPB.hFileInfo.ioFlLgLen >> 9) + 1;
+ }
+ else
+ {
+ theGlobals->tempBlocks = (unsigned long)theGlobals->myCPB.hFileInfo.ioFlLgLen >> 9;
+ }
+ /* now, calculate number of new allocation blocks needed for the data fork and add it to the total */
+ if ( theGlobals->tempBlocks % theGlobals->dstBlksPerAllocBlk )
+ {
+ theGlobals->allocBlksNeeded += (theGlobals->tempBlocks / theGlobals->dstBlksPerAllocBlk) + 1;
+ }
+ else
+ {
+ theGlobals->allocBlksNeeded += theGlobals->tempBlocks / theGlobals->dstBlksPerAllocBlk;
+ }
+
+ /* get number of 512-byte blocks needed for resource fork */
+ if ( ((unsigned long)theGlobals->myCPB.hFileInfo.ioFlRLgLen & 0x000001ff) != 0 )
+ {
+ theGlobals->tempBlocks = ((unsigned long)theGlobals->myCPB.hFileInfo.ioFlRLgLen >> 9) + 1;
+ }
+ else
+ {
+ theGlobals->tempBlocks = (unsigned long)theGlobals->myCPB.hFileInfo.ioFlRLgLen >> 9;
+ }
+ /* now, calculate number of new allocation blocks needed for the resource fork and add it to the total */
+ if ( theGlobals->tempBlocks % theGlobals->dstBlksPerAllocBlk )
+ {
+ theGlobals->allocBlksNeeded += (theGlobals->tempBlocks / theGlobals->dstBlksPerAllocBlk) + 1;
+ }
+ else
+ {
+ theGlobals->allocBlksNeeded += theGlobals->tempBlocks / theGlobals->dstBlksPerAllocBlk;
+ }
+ }
+ }
+ }
+ ++index;
+ } while ( theGlobals->result == noErr );
+}
+
+/*****************************************************************************/
+
+static OSErr PreflightDirectoryCopySpace(short srcVRefNum,
+ long srcDirID,
+ short dstVRefNum,
+ CopyFilterProcPtr copyFilterProc,
+ Boolean *spaceOK)
+{
+ XVolumeParam pb;
+ OSErr error;
+ unsigned long dstFreeBlocks;
+ PreflightGlobals theGlobals;
+
+ error = XGetVolumeInfoNoName(NULL, dstVRefNum, &pb);
+ if ( error == noErr )
+ {
+ /* Convert freeBytes to free disk blocks (512-byte blocks) */
+#if (UNIVERSAL_INTERFACES_VERSION >= 0x0330)
+ dstFreeBlocks = (pb.ioVFreeBytes >> 9);
+#else
+ dstFreeBlocks = (pb.ioVFreeBytes.hi << 23) + (pb.ioVFreeBytes.lo >> 9);
+#endif
+ /* get allocation block size (always multiple of 512) and divide by 512
+ to get number of 512-byte blocks per allocation block */
+ theGlobals.dstBlksPerAllocBlk = ((unsigned long)pb.ioVAlBlkSiz >> 9);
+
+ theGlobals.allocBlksNeeded = 0;
+
+ theGlobals.myCPB.dirInfo.ioNamePtr = theGlobals.itemName;
+ theGlobals.myCPB.dirInfo.ioVRefNum = srcVRefNum;
+
+ theGlobals.copyFilterProc = copyFilterProc;
+
+ GetLevelSize(srcDirID, &theGlobals);
+
+ /* Is there enough room on the destination volume for the source file? */
+ /* Note: This will work because the largest number of disk blocks supported */
+ /* on a 2TB volume is 0xffffffff and (allocBlksNeeded * dstBlksPerAllocBlk) */
+ /* will always be less than 0xffffffff. */
+ *spaceOK = ((theGlobals.allocBlksNeeded * theGlobals.dstBlksPerAllocBlk) <= dstFreeBlocks);
+ }
+
+ return ( error );
+}
+
+/*****************************************************************************/
+
+static void CopyLevel(long sourceDirID,
+ long dstDirID,
+ EnumerateGlobals *theGlobals)
+{
+ long currentSrcDirID;
+ long newDirID;
+ short index = 1;
+
+ do
+ {
+ /* Get next source item at the current directory level */
+
+ theGlobals->myCPB.dirInfo.ioFDirIndex = index;
+ theGlobals->myCPB.dirInfo.ioDrDirID = sourceDirID;
+ theGlobals->error = PBGetCatInfoSync(&theGlobals->myCPB);
+
+ if ( theGlobals->error == noErr )
+ {
+ if ( (theGlobals->copyFilterProc == NULL) ||
+ CallCopyFilterProc(theGlobals->copyFilterProc, &theGlobals->myCPB) ) /* filter if filter proc was supplied */
+ {
+ /* Either there's no filter proc OR the filter proc says to use this item */
+
+ /* We have an item. Is it a file or directory? */
+ if ( (theGlobals->myCPB.hFileInfo.ioFlAttrib & ioDirMask) != 0 )
+ {
+ /* We have a directory */
+
+ /* Create a new directory at the destination. No errors allowed! */
+ theGlobals->error = DirCreate(theGlobals->destinationVRefNum, dstDirID, theGlobals->itemName, &newDirID);
+ if ( theGlobals->error == noErr )
+ {
+ /* Save the current source directory ID where we can get it when we come back
+ ** from recursion land. */
+ currentSrcDirID = theGlobals->myCPB.dirInfo.ioDrDirID;
+
+ /* Dive again (copy the directory level we just found below this one) */
+ CopyLevel(theGlobals->myCPB.dirInfo.ioDrDirID, newDirID, theGlobals);
+
+ if ( !theGlobals->bailout )
+ {
+ /* Copy comment from old to new directory. */
+ /* Ignore the result because we really don't care if it worked or not. */
+ (void) DTCopyComment(theGlobals->myCPB.dirInfo.ioVRefNum, currentSrcDirID, NULL, theGlobals->destinationVRefNum, newDirID, NULL);
+
+ /* Copy directory attributes (dates, etc.) to newDirID. */
+ /* No errors allowed */
+ theGlobals->error = CopyFileMgrAttributes(theGlobals->myCPB.dirInfo.ioVRefNum, currentSrcDirID, NULL, theGlobals->destinationVRefNum, newDirID, NULL, true);
+
+ /* handle any errors from CopyFileMgrAttributes */
+ if ( theGlobals->error != noErr )
+ {
+ if ( theGlobals->errorHandler != NULL )
+ {
+ theGlobals->bailout = CallCopyErrProc(theGlobals->errorHandler, theGlobals->error, copyDirFMAttributesOp,
+ theGlobals->myCPB.dirInfo.ioVRefNum, currentSrcDirID, NULL,
+ theGlobals->destinationVRefNum, newDirID, NULL);
+ }
+ else
+ {
+ /* If you don't handle the errors with an error handler, */
+ /* then the copy stops here. */
+ theGlobals->bailout = true;
+ }
+ }
+ }
+ }
+ else /* error handling for DirCreate */
+ {
+ if ( theGlobals->errorHandler != NULL )
+ {
+ theGlobals->bailout = CallCopyErrProc(theGlobals->errorHandler, theGlobals->error, dirCreateOp,
+ theGlobals->myCPB.dirInfo.ioVRefNum, currentSrcDirID, NULL,
+ theGlobals->destinationVRefNum, dstDirID, theGlobals->itemName);
+ }
+ else
+ {
+ /* If you don't handle the errors with an error handler, */
+ /* then the copy stops here. */
+ theGlobals->bailout = true;
+ }
+ }
+
+ if ( !theGlobals->bailout )
+ {
+ /* clear error return on way back if we aren't bailing out */
+ theGlobals->error = noErr;
+ }
+ }
+ else
+ {
+ /* We have a file, so copy it */
+
+ theGlobals->error = FileCopy(theGlobals->myCPB.hFileInfo.ioVRefNum,
+ theGlobals->myCPB.hFileInfo.ioFlParID,
+ theGlobals->itemName,
+ theGlobals->destinationVRefNum,
+ dstDirID,
+ NULL,
+ NULL,
+ theGlobals->copyBuffer,
+ theGlobals->bufferSize,
+ false);
+
+ /* handle any errors from FileCopy */
+ if ( theGlobals->error != noErr )
+ {
+ if ( theGlobals->errorHandler != NULL )
+ {
+ theGlobals->bailout = CallCopyErrProc(theGlobals->errorHandler, theGlobals->error, fileCopyOp,
+ theGlobals->myCPB.hFileInfo.ioVRefNum, theGlobals->myCPB.hFileInfo.ioFlParID, theGlobals->itemName,
+ theGlobals->destinationVRefNum, dstDirID, NULL);
+ if ( !theGlobals->bailout )
+ {
+ /* If the CopyErrProc handled the problem, clear the error here */
+ theGlobals->error = noErr;
+ }
+ }
+ else
+ {
+ /* If you don't handle the errors with an error handler, */
+ /* then the copy stops here. */
+ theGlobals->bailout = true;
+ }
+ }
+ }
+ }
+ }
+ else
+ { /* error handling for PBGetCatInfo */
+ /* it's normal to get a fnfErr when indexing; that only means you've hit the end of the directory */
+ if ( theGlobals->error != fnfErr )
+ {
+ if ( theGlobals->errorHandler != NULL )
+ {
+ theGlobals->bailout = CallCopyErrProc(theGlobals->errorHandler, theGlobals->error, getNextItemOp,
+ theGlobals->myCPB.dirInfo.ioVRefNum, sourceDirID, NULL, 0, 0, NULL);
+ if ( !theGlobals->bailout )
+ {
+ /* If the CopyErrProc handled the problem, clear the error here */
+ theGlobals->error = noErr;
+ }
+ }
+ else
+ {
+ /* If you don't handle the errors with an error handler, */
+ /* then the copy stops here. */
+ theGlobals->bailout = true;
+ }
+ }
+ }
+ ++index; /* prepare to get next item */
+ } while ( (theGlobals->error == noErr) && (!theGlobals->bailout) ); /* time to fall back a level? */
+}
+
+/*****************************************************************************/
+
+pascal OSErr FilteredDirectoryCopy(short srcVRefNum,
+ long srcDirID,
+ ConstStr255Param srcName,
+ short dstVRefNum,
+ long dstDirID,
+ ConstStr255Param dstName,
+ void *copyBufferPtr,
+ long copyBufferSize,
+ Boolean preflight,
+ CopyErrProcPtr copyErrHandler,
+ CopyFilterProcPtr copyFilterProc, ConstStr255Param newName );
+/* ¦ Make a copy of a directory structure in a new location with item filtering.
+ The FilteredDirectoryCopy function makes a copy of a directory
+ structure in a new location. If copyBufferPtr <> NIL, it points to
+ a buffer of copyBufferSize that is used to copy files data. The
+ larger the supplied buffer, the faster the copy. If
+ copyBufferPtr = NIL, then this routine allocates a buffer in the
+ application heap. If you pass a copy buffer to this routine, make
+ its size a multiple of 512 ($200) bytes for optimum performance.
+
+ The optional copyFilterProc parameter lets a routine you define
+ decide what files or directories are copied to the destination.
+
+ FilteredDirectoryCopy normally creates a new directory *in* the
+ specified destination directory and copies the source directory's
+ content into the new directory. However, if root parent directory
+ (fsRtParID) is passed as the dstDirID parameter and NULL is
+ passed as the dstName parameter, DirectoryCopy renames the
+ destination volume to the source directory's name (truncating
+ if the name is longer than 27 characters) and copies the source
+ directory's content into the destination volume's root directory.
+ This special case is supported by FilteredDirectoryCopy, but
+ not by FSpFilteredDirectoryCopy since with FSpFilteredDirectoryCopy,
+ the dstName parameter can not be NULL.
+
+ srcVRefNum input: Source volume specification.
+ srcDirID input: Source directory ID.
+ srcName input: Source directory name, or nil if
+ srcDirID specifies the directory.
+ dstVRefNum input: Destination volume specification.
+ dstDirID input: Destination directory ID.
+ dstName input: Destination directory name, or nil if
+ dstDirID specifies the directory.
+ copyBufferPtr input: Points to a buffer of copyBufferSize that
+ is used the i/o buffer for the copy or
+ nil if you want DirectoryCopy to allocate its
+ own buffer in the application heap.
+ copyBufferSize input: The size of the buffer pointed to
+ by copyBufferPtr.
+ preflight input: If true, DirectoryCopy makes sure there are
+ enough allocation blocks on the destination
+ volume to hold the directory's files before
+ starting the copy.
+ copyErrHandler input: A pointer to the routine you want called if an
+ error condition is detected during the copy, or
+ nil if you don't want to handle error conditions.
+ If you don't handle error conditions, the first
+ error will cause the copy to quit and
+ DirectoryCopy will return the error.
+ Error handling is recommended...
+ copyFilterProc input: A pointer to the filter routine you want called
+ for each item in the source directory, or NULL
+ if you don't want to filter.
+
+ Result Codes
+ noErr 0 No error
+ readErr Ð19 Driver does not respond to read requests
+ writErr Ð20 Driver does not respond to write requests
+ badUnitErr Ð21 Driver reference number does not
+ match unit table
+ unitEmptyErr Ð22 Driver reference number specifies a
+ nil handle in unit table
+ abortErr Ð27 Request aborted by KillIO
+ notOpenErr Ð28 Driver not open
+ dskFulErr -34 Destination volume is full
+ nsvErr -35 No such volume
+ ioErr -36 I/O error
+ bdNamErr -37 Bad filename
+ tmfoErr -42 Too many files open
+ fnfErr -43 Source file not found, or destination
+ directory does not exist
+ wPrErr -44 Volume locked by hardware
+ fLckdErr -45 File is locked
+ vLckdErr -46 Destination volume is read-only
+ fBsyErr -47 The source or destination file could
+ not be opened with the correct access
+ modes
+ dupFNErr -48 Destination file already exists
+ opWrErr -49 File already open for writing
+ paramErr -50 No default volume or function not
+ supported by volume
+ permErr -54 File is already open and cannot be opened using specified deny modes
+ memFullErr -108 Copy buffer could not be allocated
+ dirNFErr -120 Directory not found or incomplete pathname
+ wrgVolTypErr -123 Function not supported by volume
+ afpAccessDenied -5000 User does not have the correct access
+ afpDenyConflict -5006 The source or destination file could
+ not be opened with the correct access
+ modes
+ afpObjectTypeErr -5025 Source is a directory, directory not found
+ or incomplete pathname
+
+ __________
+
+ Also see: CopyErrProcPtr, CopyFilterProcPtr, FSpFilteredDirectoryCopy,
+ DirectoryCopy, FSpDirectoryCopy, FileCopy, FSpFileCopy
+*/
+
+/*****************************************************************************/
+
+pascal OSErr FilteredDirectoryCopy(short srcVRefNum,
+ long srcDirID,
+ ConstStr255Param srcName,
+ short dstVRefNum,
+ long dstDirID,
+ ConstStr255Param dstName,
+ void *copyBufferPtr,
+ long copyBufferSize,
+ Boolean preflight,
+ CopyErrProcPtr copyErrHandler,
+ CopyFilterProcPtr copyFilterProc, ConstStr255Param newName)
+{
+ EnumerateGlobals theGlobals;
+ Boolean isDirectory;
+ OSErr error;
+ Boolean ourCopyBuffer = false;
+ Str63 srcDirName, oldDiskName;
+ Boolean spaceOK;
+
+ /* Make sure a copy buffer is allocated. */
+ if ( copyBufferPtr == NULL )
+ {
+ /* The caller didn't supply a copy buffer so grab one from the application heap.
+ ** Try to get a big copy buffer, if we can't, try for a 512-byte buffer.
+ ** If 512 bytes aren't available, we're in trouble. */
+ copyBufferSize = dirCopyBigCopyBuffSize;
+ copyBufferPtr = NewPtr(copyBufferSize);
+ if ( copyBufferPtr == NULL )
+ {
+ copyBufferSize = dirCopyMinCopyBuffSize;
+ copyBufferPtr = NewPtr(copyBufferSize);
+ if ( copyBufferPtr == NULL )
+ {
+ return ( memFullErr );
+ }
+ }
+ ourCopyBuffer = true;
+ }
+
+ /* Get the real dirID where we're copying from and make sure it is a directory. */
+ error = GetDirectoryID(srcVRefNum, srcDirID, srcName, &srcDirID, &isDirectory);
+ if ( error != noErr )
+ {
+ goto ErrorExit;
+ }
+ if ( !isDirectory )
+ {
+ error = dirNFErr;
+ goto ErrorExit;
+ }
+
+ /* Special case destination if it is the root parent directory. */
+ /* Since you can't create the root directory, this is needed if */
+ /* you want to copy a directory's content to a disk's root directory. */
+ if ( (dstDirID == fsRtParID) && (dstName == NULL) )
+ {
+ dstDirID = fsRtParID;
+ isDirectory = true;
+ error = noErr;
+ }
+ else
+ {
+ /* Get the real dirID where we're going to put the copy and make sure it is a directory. */
+ error = GetDirectoryID(dstVRefNum, dstDirID, dstName, &dstDirID, &isDirectory);
+ if ( error != noErr )
+ {
+ goto ErrorExit;
+ }
+ if ( !isDirectory )
+ {
+ error = dirNFErr;
+ goto ErrorExit;
+ }
+ }
+
+ /* Get the real vRefNum of both the source and destination */
+ error = DetermineVRefNum(srcName, srcVRefNum, &srcVRefNum);
+ if ( error != noErr )
+ {
+ goto ErrorExit;
+ }
+ error = DetermineVRefNum(dstName, dstVRefNum, &dstVRefNum);
+ if ( error != noErr )
+ {
+ goto ErrorExit;
+ }
+
+ if ( preflight )
+ {
+ error = PreflightDirectoryCopySpace(srcVRefNum, srcDirID, dstVRefNum, copyFilterProc, &spaceOK);
+ if ( error != noErr )
+ {
+ goto ErrorExit;
+ }
+ if ( !spaceOK )
+ {
+ error = dskFulErr; /* not enough room on destination */
+ goto ErrorExit;
+ }
+ }
+
+ /* Create the new directory in the destination directory with the */
+ /* same name as the source directory. */
+ /*
+ if newName is not empty use it rather than the original dir name.
+ */
+ if ( newName[0] == 0 )
+ {
+ error = GetDirName(srcVRefNum, srcDirID, srcDirName);
+ if ( error != noErr )
+ {
+ goto ErrorExit;
+ }
+ }
+ else
+ {
+ memcpy(srcDirName, newName, 32 );
+
+ }
+ /* Again, special case destination if the destination is the */
+ /* root parent directory. This time, we'll rename the disk to */
+ /* the source directory name. */
+ if ( dstDirID == fsRtParID )
+ {
+ /* Get the current name of the destination disk */
+ error = GetDirName(dstVRefNum, fsRtDirID, oldDiskName);
+ if ( error == noErr )
+ {
+ /* Shorten the name if it's too long to be the volume name */
+ TruncPString(srcDirName, srcDirName, 27);
+
+ /* Rename the disk */
+ error = HRename(dstVRefNum, fsRtParID, oldDiskName, srcDirName);
+ /* and copy to the root directory */
+ dstDirID = fsRtDirID;
+ }
+ }
+ else
+ {
+ error = DirCreate(dstVRefNum, dstDirID, srcDirName, &dstDirID);
+ }
+ if ( error != noErr )
+ {
+ /* handle any errors from DirCreate */
+ if ( copyErrHandler != NULL )
+ {
+ if ( CallCopyErrProc(copyErrHandler, error, dirCreateOp,
+ srcVRefNum, srcDirID, NULL,
+ dstVRefNum, dstDirID, srcDirName) )
+ {
+ goto ErrorExit;
+ }
+ else
+ {
+ /* If the CopyErrProc handled the problem, clear the error here */
+ /* and continue */
+ error = noErr;
+ }
+ }
+ else
+ {
+ /* If you don't handle the errors with an error handler, */
+ /* then the copy stops here. */
+ goto ErrorExit;
+ }
+ }
+
+ /* dstDirID is now the newly created directory! */
+
+ /* Set up the globals we need to access from the recursive routine. */
+ theGlobals.copyBuffer = (Ptr)copyBufferPtr;
+ theGlobals.bufferSize = copyBufferSize;
+ theGlobals.destinationVRefNum = dstVRefNum; /* so we can get to it always */
+ theGlobals.myCPB.hFileInfo.ioNamePtr = (StringPtr)&theGlobals.itemName;
+ theGlobals.myCPB.hFileInfo.ioVRefNum = srcVRefNum;
+ theGlobals.errorHandler = copyErrHandler;
+ theGlobals.bailout = false;
+ theGlobals.copyFilterProc = copyFilterProc;
+
+ /* Here we go into recursion land... */
+ CopyLevel(srcDirID, dstDirID, &theGlobals);
+ error = theGlobals.error; /* get the result */
+
+ if ( !theGlobals.bailout )
+ {
+ /* Copy comment from source to destination directory. */
+ /* Ignore the result because we really don't care if it worked or not. */
+ (void) DTCopyComment(srcVRefNum, srcDirID, NULL, dstVRefNum, dstDirID, NULL);
+
+ /* Copy the File Manager attributes */
+ error = CopyFileMgrAttributes(srcVRefNum, srcDirID, NULL,
+ dstVRefNum, dstDirID, NULL, true);
+
+ /* handle any errors from CopyFileMgrAttributes */
+ if ( (error != noErr) && (copyErrHandler != NULL) )
+ {
+ theGlobals.bailout = CallCopyErrProc(copyErrHandler, error, copyDirFMAttributesOp,
+ srcVRefNum, srcDirID, NULL,
+ dstVRefNum, dstDirID, NULL);
+ }
+ }
+
+ErrorExit:
+ /* Get rid of the copy buffer if we allocated it. */
+ if ( ourCopyBuffer )
+ {
+ DisposePtr((Ptr)copyBufferPtr);
+ }
+
+ return ( error );
+}
+
+
+/*****************************************************************************/
+
+
+pascal OSErr MacFSpDirectoryCopyRename(const FSSpec *srcSpec,
+ const FSSpec *dstSpec,
+ ConstStr255Param newName,
+ void *copyBufferPtr,
+ long copyBufferSize,
+ Boolean preflight,
+ CopyErrProcPtr copyErrHandler)
+{
+ return ( FilteredDirectoryCopy(srcSpec->vRefNum, srcSpec->parID, srcSpec->name,
+ dstSpec->vRefNum, dstSpec->parID, dstSpec->name,
+ copyBufferPtr, copyBufferSize, preflight,
+ copyErrHandler, NULL, newName) );
+}
diff --git a/src/libs/xpcom18a4/xpcom/io/macDirectoryCopy.h b/src/libs/xpcom18a4/xpcom/io/macDirectoryCopy.h
new file mode 100644
index 00000000..361c820e
--- /dev/null
+++ b/src/libs/xpcom18a4/xpcom/io/macDirectoryCopy.h
@@ -0,0 +1,158 @@
+/*
+** Apple Macintosh Developer Technical Support
+**
+** DirectoryCopy: A robust, general purpose directory copy routine.
+**
+** by Jim Luther, Apple Developer Technical Support Emeritus
+**
+** File: DirectoryCopy.h
+**
+** Copyright © 1992-1998 Apple Computer, Inc.
+** All rights reserved.
+**
+** You may incorporate this sample code into your applications without
+** restriction, though the sample code has been provided "AS IS" and the
+** responsibility for its operation is 100% yours. However, what you are
+** not permitted to do is to redistribute the source as "DSC Sample Code"
+** after having made changes. If you're going to re-distribute the source,
+** we require that you make it clear in the source that the code was
+** descended from Apple Sample Code, but that you've made changes.
+*/
+
+// Modified to allow renaming the destination folder
+
+#ifndef __MACDIRECTORYCOPY__
+#define __MACDIRECTORYCOPY__
+
+#include <Types.h>
+#include <Files.h>
+
+#include "Optimization.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef pascal Boolean (*CopyErrProcPtr) (OSErr error,
+ short failedOperation,
+ short srcVRefNum,
+ long srcDirID,
+ ConstStr255Param srcName,
+ short dstVRefNum,
+ long dstDirID,
+ ConstStr255Param dstName);
+/* ¦ Prototype for the CopyErrProc function DirectoryCopy calls.
+ This is the prototype for the CopyErrProc function DirectoryCopy
+ calls if an error condition is detected sometime during the copy. If
+ CopyErrProc returns false, then DirectoryCopy attempts to continue with
+ the directory copy operation. If CopyErrProc returns true, then
+ DirectoryCopy stops the directory copy operation.
+
+ error input: The error result code that caused CopyErrProc to
+ be called.
+ failedOperation input: The operation that returned an error to
+ DirectoryCopy.
+ srcVRefNum input: Source volume specification.
+ srcDirID input: Source directory ID.
+ srcName input: Source file or directory name, or nil if
+ srcDirID specifies the directory.
+ dstVRefNum input: Destination volume specification.
+ dstDirID input: Destination directory ID.
+ dstName input: Destination file or directory name, or nil if
+ dstDirID specifies the directory.
+
+ __________
+
+ Also see: FilteredDirectoryCopy, FSpFilteredDirectoryCopy, DirectoryCopy, FSpDirectoryCopy
+*/
+
+pascal OSErr MacFSpDirectoryCopyRename(const FSSpec *srcSpec,
+ const FSSpec *dstSpec,
+ ConstStr255Param newName,
+ void *copyBufferPtr,
+ long copyBufferSize,
+ Boolean preflight,
+ CopyErrProcPtr copyErrHandler);
+/* ¦ Make a copy of a directory structure in a new location.
+ The FSpDirectoryCopy function makes a copy of a directory structure in a
+ new location. If copyBufferPtr <> NIL, it points to a buffer of
+ copyBufferSize that is used to copy files data. The larger the
+ supplied buffer, the faster the copy. If copyBufferPtr = NIL, then this
+ routine allocates a buffer in the application heap. If you pass a
+ copy buffer to this routine, make its size a multiple of 512
+ ($200) bytes for optimum performance.
+
+ srcSpec input: An FSSpec record specifying the directory to copy.
+ dstSpec input: An FSSpec record specifying destination directory
+ of the copy.
+ copyBufferPtr input: Points to a buffer of copyBufferSize that
+ is used the i/o buffer for the copy or
+ nil if you want DirectoryCopy to allocate its
+ own buffer in the application heap.
+ copyBufferSize input: The size of the buffer pointed to
+ by copyBufferPtr.
+ preflight input: If true, FSpDirectoryCopy makes sure there are
+ enough allocation blocks on the destination
+ volume to hold the directory's files before
+ starting the copy.
+ copyErrHandler input: A pointer to the routine you want called if an
+ error condition is detected during the copy, or
+ nil if you don't want to handle error conditions.
+ If you don't handle error conditions, the first
+ error will cause the copy to quit and
+ DirectoryCopy will return the error.
+ Error handling is recommended...
+
+ Result Codes
+ noErr 0 No error
+ readErr Ð19 Driver does not respond to read requests
+ writErr Ð20 Driver does not respond to write requests
+ badUnitErr Ð21 Driver reference number does not
+ match unit table
+ unitEmptyErr Ð22 Driver reference number specifies a
+ nil handle in unit table
+ abortErr Ð27 Request aborted by KillIO
+ notOpenErr Ð28 Driver not open
+ dskFulErr -34 Destination volume is full
+ nsvErr -35 No such volume
+ ioErr -36 I/O error
+ bdNamErr -37 Bad filename
+ tmfoErr -42 Too many files open
+ fnfErr -43 Source file not found, or destination
+ directory does not exist
+ wPrErr -44 Volume locked by hardware
+ fLckdErr -45 File is locked
+ vLckdErr -46 Destination volume is read-only
+ fBsyErr -47 The source or destination file could
+ not be opened with the correct access
+ modes
+ dupFNErr -48 Destination file already exists
+ opWrErr -49 File already open for writing
+ paramErr -50 No default volume or function not
+ supported by volume
+ permErr -54 File is already open and cannot be opened using specified deny modes
+ memFullErr -108 Copy buffer could not be allocated
+ dirNFErr -120 Directory not found or incomplete pathname
+ wrgVolTypErr -123 Function not supported by volume
+ afpAccessDenied -5000 User does not have the correct access
+ afpDenyConflict -5006 The source or destination file could
+ not be opened with the correct access
+ modes
+ afpObjectTypeErr -5025 Source is a directory, directory not found
+ or incomplete pathname
+
+ __________
+
+ Also see: CopyErrProcPtr, DirectoryCopy, FilteredDirectoryCopy,
+ FSpFilteredDirectoryCopy, FileCopy, FSpFileCopy
+*/
+
+/*****************************************************************************/
+
+#ifdef __cplusplus
+}
+#endif
+
+#include "OptimizationEnd.h"
+
+#endif /* __DIRECTORYCOPY__ */
diff --git a/src/libs/xpcom18a4/xpcom/io/nsAppDirectoryServiceDefs.h b/src/libs/xpcom18a4/xpcom/io/nsAppDirectoryServiceDefs.h
new file mode 100644
index 00000000..768cb38f
--- /dev/null
+++ b/src/libs/xpcom18a4/xpcom/io/nsAppDirectoryServiceDefs.h
@@ -0,0 +1,120 @@
+/* -*- 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) 2000
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Conrad Carlen conrad@ingress.com
+ *
+ * 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 ***** */
+
+#ifndef nsAppDirectoryServiceDefs_h___
+#define nsAppDirectoryServiceDefs_h___
+
+//========================================================================================
+//
+// Defines property names for directories available from standard nsIDirectoryServiceProviders.
+// These keys are not guaranteed to exist because the nsIDirectoryServiceProviders which
+// provide them are optional.
+//
+// Keys whose definition ends in "DIR" or "FILE" return a single nsIFile (or subclass).
+// Keys whose definition ends in "LIST" return an nsISimpleEnumerator which enumerates a
+// list of file objects.
+//
+// System and XPCOM level properties are defined in nsDirectoryServiceDefs.h.
+//
+//========================================================================================
+
+
+// --------------------------------------------------------------------------------------
+// Files and directories which exist on a per-product basis
+// --------------------------------------------------------------------------------------
+
+#define NS_APP_APPLICATION_REGISTRY_FILE "AppRegF"
+#define NS_APP_APPLICATION_REGISTRY_DIR "AppRegD"
+
+#define NS_APP_DEFAULTS_50_DIR "DefRt" // The root dir of all defaults dirs
+#define NS_APP_PREF_DEFAULTS_50_DIR "PrfDef"
+#define NS_APP_PROFILE_DEFAULTS_50_DIR "profDef" // The profile defaults of the "current"
+ // locale. Should be first choice.
+#define NS_APP_PROFILE_DEFAULTS_NLOC_50_DIR "ProfDefNoLoc" // The profile defaults of the "default"
+ // installed locale. Second choice
+ // when above is not available.
+
+#define NS_APP_USER_PROFILES_ROOT_DIR "DefProfRt" // The dir where user profile dirs get created.
+
+#define NS_APP_RES_DIR "ARes"
+#define NS_APP_CHROME_DIR "AChrom"
+#define NS_APP_PLUGINS_DIR "APlugns" // Deprecated - use NS_APP_PLUGINS_DIR_LIST
+#define NS_APP_SEARCH_DIR "SrchPlugns"
+
+#define NS_APP_CHROME_DIR_LIST "AChromDL"
+#define NS_APP_PLUGINS_DIR_LIST "APluginsDL"
+
+// --------------------------------------------------------------------------------------
+// Files and directories which exist on a per-profile basis
+// These locations are typically provided by the profile mgr
+// --------------------------------------------------------------------------------------
+
+// In a shared profile environment, prefixing a profile-relative
+// key with NS_SHARED returns a location that is shared by
+// other users of the profile. Without this prefix, the consumer
+// has exclusive access to this location.
+
+#define NS_SHARED "SHARED"
+
+#define NS_APP_PREFS_50_DIR "PrefD" // Directory which contains user prefs
+#define NS_APP_PREFS_50_FILE "PrefF"
+#define NS_APP_PREFS_DEFAULTS_DIR_LIST "PrefDL"
+
+#define NS_APP_USER_PROFILE_50_DIR "ProfD"
+
+#define NS_APP_USER_CHROME_DIR "UChrm"
+
+#define NS_APP_LOCALSTORE_50_FILE "LclSt"
+#define NS_APP_HISTORY_50_FILE "UHist"
+#define NS_APP_USER_PANELS_50_FILE "UPnls"
+#define NS_APP_USER_MIMETYPES_50_FILE "UMimTyp"
+#define NS_APP_CACHE_PARENT_DIR "cachePDir"
+
+#define NS_APP_BOOKMARKS_50_FILE "BMarks"
+
+#define NS_APP_DOWNLOADS_50_FILE "DLoads"
+
+#define NS_APP_SEARCH_50_FILE "SrchF"
+
+#define NS_APP_MAIL_50_DIR "MailD"
+#define NS_APP_IMAP_MAIL_50_DIR "IMapMD"
+#define NS_APP_NEWS_50_DIR "NewsD"
+#define NS_APP_MESSENGER_FOLDER_CACHE_50_DIR "MFCaD"
+
+#define NS_APP_INSTALL_CLEANUP_DIR "XPIClnupD" //location of xpicleanup.dat xpicleanup.exe
+#endif
diff --git a/src/libs/xpcom18a4/xpcom/io/nsAppFileLocationProvider.cpp b/src/libs/xpcom18a4/xpcom/io/nsAppFileLocationProvider.cpp
new file mode 100644
index 00000000..9d4f785d
--- /dev/null
+++ b/src/libs/xpcom18a4/xpcom/io/nsAppFileLocationProvider.cpp
@@ -0,0 +1,608 @@
+/* -*- 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, released
+ * March 31, 1998.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2000
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Conrad Carlen <conrad@ingress.com>
+ *
+ * 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 "nsAppFileLocationProvider.h"
+#include "nsAppDirectoryServiceDefs.h"
+#include "nsDirectoryServiceDefs.h"
+#include "nsIAtom.h"
+#include "nsILocalFile.h"
+#include "nsString.h"
+#include "nsXPIDLString.h"
+#include "nsISimpleEnumerator.h"
+#include "prenv.h"
+#include "nsCRT.h"
+
+#if (defined(XP_MAC) || defined(XP_MACOSX)) && !defined(VBOX_MACOSX_FOLLOWS_UNIX_IO)
+#include <Folders.h>
+#include <Script.h>
+#include <Processes.h>
+#include <Gestalt.h>
+#include "nsILocalFileMac.h"
+#elif defined(XP_OS2)
+#define INCL_DOSPROCESS
+#define INCL_DOSMODULEMGR
+#include <os2.h>
+#elif defined(XP_WIN)
+#include <windows.h>
+#include <shlobj.h>
+#elif defined(XP_UNIX)
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/param.h>
+#elif defined(XP_BEOS)
+#include <sys/param.h>
+#include <kernel/image.h>
+#include <storage/FindDirectory.h>
+#endif
+
+
+// WARNING: These hard coded names need to go away. They need to
+// come from localizable resources
+
+#if defined(XP_MAC) || defined(XP_MACOSX)
+#define APP_REGISTRY_NAME NS_LITERAL_CSTRING("Application Registry")
+#define ESSENTIAL_FILES NS_LITERAL_CSTRING("Essential Files")
+#elif defined(XP_WIN) || defined(XP_OS2)
+#define APP_REGISTRY_NAME NS_LITERAL_CSTRING("registry.dat")
+#else
+#define APP_REGISTRY_NAME NS_LITERAL_CSTRING("appreg")
+#endif
+
+// define default product directory
+#ifdef XP_MAC
+#define DEFAULT_PRODUCT_DIR NS_LITERAL_CSTRING("Mozilla")
+#else
+#define DEFAULT_PRODUCT_DIR NS_LITERAL_CSTRING(MOZ_USER_DIR)
+#endif
+
+// Locally defined keys used by nsAppDirectoryEnumerator
+#define NS_ENV_PLUGINS_DIR "EnvPlugins" // env var MOZ_PLUGIN_PATH
+#define NS_USER_PLUGINS_DIR "UserPlugins"
+
+#if defined(XP_MAC) || defined(XP_MACOSX)
+#define NS_MACOSX_USER_PLUGIN_DIR "OSXUserPlugins"
+#define NS_MACOSX_LOCAL_PLUGIN_DIR "OSXLocalPlugins"
+#define NS_MAC_CLASSIC_PLUGIN_DIR "MacSysPlugins"
+#endif
+
+#if defined(XP_MAC)
+#define DEFAULTS_DIR_NAME NS_LITERAL_CSTRING("Defaults")
+#define DEFAULTS_PREF_DIR_NAME NS_LITERAL_CSTRING("Pref")
+#define DEFAULTS_PROFILE_DIR_NAME NS_LITERAL_CSTRING("Profile")
+#define RES_DIR_NAME NS_LITERAL_CSTRING("Res")
+#define CHROME_DIR_NAME NS_LITERAL_CSTRING("Chrome")
+#define PLUGINS_DIR_NAME NS_LITERAL_CSTRING("Plug-ins")
+#define SEARCH_DIR_NAME NS_LITERAL_CSTRING("Search Plugins")
+#else
+#define DEFAULTS_DIR_NAME NS_LITERAL_CSTRING("defaults")
+#define DEFAULTS_PREF_DIR_NAME NS_LITERAL_CSTRING("pref")
+#define DEFAULTS_PROFILE_DIR_NAME NS_LITERAL_CSTRING("profile")
+#define RES_DIR_NAME NS_LITERAL_CSTRING("res")
+#define CHROME_DIR_NAME NS_LITERAL_CSTRING("chrome")
+#define PLUGINS_DIR_NAME NS_LITERAL_CSTRING("plugins")
+#define SEARCH_DIR_NAME NS_LITERAL_CSTRING("searchplugins")
+#endif
+
+//*****************************************************************************
+// nsAppFileLocationProvider::Constructor/Destructor
+//*****************************************************************************
+
+nsAppFileLocationProvider::nsAppFileLocationProvider()
+{
+}
+
+//*****************************************************************************
+// nsAppFileLocationProvider::nsISupports
+//*****************************************************************************
+
+NS_IMPL_THREADSAFE_ISUPPORTS2(nsAppFileLocationProvider, nsIDirectoryServiceProvider, nsIDirectoryServiceProvider2)
+
+//*****************************************************************************
+// nsAppFileLocationProvider::nsIDirectoryServiceProvider
+//*****************************************************************************
+
+NS_IMETHODIMP
+nsAppFileLocationProvider::GetFile(const char *prop, PRBool *persistant, nsIFile **_retval)
+{
+ nsCOMPtr<nsILocalFile> localFile;
+ nsresult rv = NS_ERROR_FAILURE;
+
+ NS_ENSURE_ARG(prop);
+ *_retval = nsnull;
+ *persistant = PR_TRUE;
+
+#if (defined (XP_MAC) || defined(XP_MACOSX)) && !defined(VBOX_MACOSX_FOLLOWS_UNIX_IO)
+ short foundVRefNum;
+ SInt32 foundDirID;
+ FSSpec fileSpec;
+ nsCOMPtr<nsILocalFileMac> macFile;
+#endif
+
+ if (nsCRT::strcmp(prop, NS_APP_APPLICATION_REGISTRY_DIR) == 0)
+ {
+ rv = GetProductDirectory(getter_AddRefs(localFile));
+ }
+ else if (nsCRT::strcmp(prop, NS_APP_APPLICATION_REGISTRY_FILE) == 0)
+ {
+ rv = GetProductDirectory(getter_AddRefs(localFile));
+ if (NS_SUCCEEDED(rv))
+ rv = localFile->AppendNative(APP_REGISTRY_NAME);
+ }
+ else if (nsCRT::strcmp(prop, NS_APP_DEFAULTS_50_DIR) == 0)
+ {
+ rv = CloneMozBinDirectory(getter_AddRefs(localFile));
+ if (NS_SUCCEEDED(rv))
+ rv = localFile->AppendRelativeNativePath(DEFAULTS_DIR_NAME);
+ }
+ else if (nsCRT::strcmp(prop, NS_APP_PREF_DEFAULTS_50_DIR) == 0)
+ {
+ rv = CloneMozBinDirectory(getter_AddRefs(localFile));
+ if (NS_SUCCEEDED(rv)) {
+ rv = localFile->AppendRelativeNativePath(DEFAULTS_DIR_NAME);
+ if (NS_SUCCEEDED(rv))
+ rv = localFile->AppendRelativeNativePath(DEFAULTS_PREF_DIR_NAME);
+ }
+ }
+ else if (nsCRT::strcmp(prop, NS_APP_PROFILE_DEFAULTS_50_DIR) == 0 ||
+ nsCRT::strcmp(prop, NS_APP_PROFILE_DEFAULTS_NLOC_50_DIR) == 0)
+ {
+ rv = CloneMozBinDirectory(getter_AddRefs(localFile));
+ if (NS_SUCCEEDED(rv)) {
+ rv = localFile->AppendRelativeNativePath(DEFAULTS_DIR_NAME);
+ if (NS_SUCCEEDED(rv))
+ rv = localFile->AppendRelativeNativePath(DEFAULTS_PROFILE_DIR_NAME);
+ }
+ }
+ else if (nsCRT::strcmp(prop, NS_APP_USER_PROFILES_ROOT_DIR) == 0)
+ {
+ rv = GetDefaultUserProfileRoot(getter_AddRefs(localFile));
+ }
+ else if (nsCRT::strcmp(prop, NS_APP_RES_DIR) == 0)
+ {
+ rv = CloneMozBinDirectory(getter_AddRefs(localFile));
+ if (NS_SUCCEEDED(rv))
+ rv = localFile->AppendRelativeNativePath(RES_DIR_NAME);
+ }
+ else if (nsCRT::strcmp(prop, NS_APP_CHROME_DIR) == 0)
+ {
+ rv = CloneMozBinDirectory(getter_AddRefs(localFile));
+ if (NS_SUCCEEDED(rv))
+ rv = localFile->AppendRelativeNativePath(CHROME_DIR_NAME);
+ }
+ else if (nsCRT::strcmp(prop, NS_APP_PLUGINS_DIR) == 0)
+ {
+ rv = CloneMozBinDirectory(getter_AddRefs(localFile));
+ if (NS_SUCCEEDED(rv))
+ rv = localFile->AppendRelativeNativePath(PLUGINS_DIR_NAME);
+ }
+#if (defined(XP_MAC) || defined(XP_MACOSX)) && !defined(VBOX_MACOSX_FOLLOWS_UNIX_IO)
+ else if (nsCRT::strcmp(prop, NS_MACOSX_USER_PLUGIN_DIR) == 0)
+ {
+ if (!(::FindFolder(kUserDomain,
+ kInternetPlugInFolderType,
+ kDontCreateFolder, &foundVRefNum, &foundDirID)) &&
+ !(::FSMakeFSSpec(foundVRefNum, foundDirID, "\p", &fileSpec))) {
+ rv = NS_NewLocalFileWithFSSpec(&fileSpec, PR_TRUE, getter_AddRefs(macFile));
+ if (NS_SUCCEEDED(rv))
+ localFile = macFile;
+ }
+ }
+ else if (nsCRT::strcmp(prop, NS_MACOSX_LOCAL_PLUGIN_DIR) == 0)
+ {
+ if (!(::FindFolder(kLocalDomain,
+ kInternetPlugInFolderType,
+ kDontCreateFolder, &foundVRefNum, &foundDirID)) &&
+ !(::FSMakeFSSpec(foundVRefNum, foundDirID, "\p", &fileSpec))) {
+ rv = NS_NewLocalFileWithFSSpec(&fileSpec, PR_TRUE, getter_AddRefs(macFile));
+ if (NS_SUCCEEDED(rv))
+ localFile = macFile;
+ }
+ }
+ else if (nsCRT::strcmp(prop, NS_MAC_CLASSIC_PLUGIN_DIR) == 0)
+ {
+ if (!(::FindFolder(kOnAppropriateDisk,
+ kInternetPlugInFolderType,
+ kDontCreateFolder, &foundVRefNum, &foundDirID)) &&
+ !(::FSMakeFSSpec(foundVRefNum, foundDirID, "\p", &fileSpec))) {
+ rv = NS_NewLocalFileWithFSSpec(&fileSpec, PR_TRUE, getter_AddRefs(macFile));
+ if (NS_SUCCEEDED(rv))
+ localFile = macFile;
+ }
+ }
+#else
+ else if (nsCRT::strcmp(prop, NS_ENV_PLUGINS_DIR) == 0)
+ {
+ NS_ERROR("Don't use nsAppFileLocationProvider::GetFile(NS_ENV_PLUGINS_DIR, ...). "
+ "Use nsAppFileLocationProvider::GetFiles(...).");
+ const char *pathVar = PR_GetEnv("VBOX_XPCOM_PLUGIN_PATH");
+ if (pathVar)
+ rv = NS_NewNativeLocalFile(nsDependentCString(pathVar), PR_TRUE, getter_AddRefs(localFile));
+ }
+ else if (nsCRT::strcmp(prop, NS_USER_PLUGINS_DIR) == 0)
+ {
+ rv = GetProductDirectory(getter_AddRefs(localFile));
+ if (NS_SUCCEEDED(rv))
+ rv = localFile->AppendRelativeNativePath(PLUGINS_DIR_NAME);
+ }
+#endif
+ else if (nsCRT::strcmp(prop, NS_APP_SEARCH_DIR) == 0)
+ {
+ rv = CloneMozBinDirectory(getter_AddRefs(localFile));
+ if (NS_SUCCEEDED(rv))
+ rv = localFile->AppendRelativeNativePath(SEARCH_DIR_NAME);
+ }
+ else if (nsCRT::strcmp(prop, NS_APP_INSTALL_CLEANUP_DIR) == 0)
+ {
+ // This is cloned so that embeddors will have a hook to override
+ // with their own cleanup dir. See bugzilla bug #105087
+ rv = CloneMozBinDirectory(getter_AddRefs(localFile));
+#ifdef XP_MAC
+ if (NS_SUCCEEDED(rv))
+ rv = localFile->AppendNative(ESSENTIAL_FILES);
+#endif
+
+ }
+
+ if (localFile && NS_SUCCEEDED(rv))
+ return localFile->QueryInterface(NS_GET_IID(nsIFile), (void**)_retval);
+
+ return rv;
+}
+
+
+NS_METHOD nsAppFileLocationProvider::CloneMozBinDirectory(nsILocalFile **aLocalFile)
+{
+ NS_ENSURE_ARG_POINTER(aLocalFile);
+ nsresult rv;
+
+ if (!mMozBinDirectory)
+ {
+ // Get the mozilla bin directory
+ // 1. Check the directory service first for NS_XPCOM_CURRENT_PROCESS_DIR
+ // This will be set if a directory was passed to NS_InitXPCOM
+ // 2. If that doesn't work, set it to be the current process directory
+ nsCOMPtr<nsIProperties>
+ directoryService(do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID, &rv));
+ if (NS_FAILED(rv))
+ return rv;
+
+ rv = directoryService->Get(NS_XPCOM_CURRENT_PROCESS_DIR, NS_GET_IID(nsIFile), getter_AddRefs(mMozBinDirectory));
+ if (NS_FAILED(rv)) {
+ rv = directoryService->Get(NS_OS_CURRENT_PROCESS_DIR, NS_GET_IID(nsIFile), getter_AddRefs(mMozBinDirectory));
+ if (NS_FAILED(rv))
+ return rv;
+ }
+ }
+
+ nsCOMPtr<nsIFile> aFile;
+ rv = mMozBinDirectory->Clone(getter_AddRefs(aFile));
+ if (NS_FAILED(rv))
+ return rv;
+
+ nsCOMPtr<nsILocalFile> lfile = do_QueryInterface (aFile);
+ if (!lfile)
+ return NS_ERROR_FAILURE;
+
+ NS_IF_ADDREF(*aLocalFile = lfile);
+ return NS_OK;
+}
+
+
+//----------------------------------------------------------------------------------------
+// GetProductDirectory - Gets the directory which contains the application data folder
+//
+// UNIX : ~/.mozilla/
+// WIN : <Application Data folder on user's machine>\Mozilla
+// Mac : :Documents:Mozilla:
+//----------------------------------------------------------------------------------------
+NS_METHOD nsAppFileLocationProvider::GetProductDirectory(nsILocalFile **aLocalFile)
+{
+ NS_ENSURE_ARG_POINTER(aLocalFile);
+
+ nsresult rv;
+ PRBool exists;
+ nsCOMPtr<nsILocalFile> localDir;
+
+#if defined(XP_MAC)
+ nsCOMPtr<nsIProperties> directoryService =
+ do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID, &rv);
+ if (NS_FAILED(rv)) return rv;
+ OSErr err;
+ long response;
+ err = ::Gestalt(gestaltSystemVersion, &response);
+ const char *prop = (!err && response >= 0x00001000) ? NS_MAC_USER_LIB_DIR : NS_MAC_DOCUMENTS_DIR;
+ rv = directoryService->Get(prop, NS_GET_IID(nsILocalFile), getter_AddRefs(localDir));
+ if (NS_FAILED(rv)) return rv;
+#elif defined(XP_MACOSX) && !defined(VBOX_MACOSX_FOLLOWS_UNIX_IO)
+ FSRef fsRef;
+ OSErr err = ::FSFindFolder(kUserDomain, kDomainLibraryFolderType, kCreateFolder, &fsRef);
+ if (err) return NS_ERROR_FAILURE;
+ NS_NewLocalFile(EmptyString(), PR_TRUE, getter_AddRefs(localDir));
+ if (!localDir) return NS_ERROR_FAILURE;
+ nsCOMPtr<nsILocalFileMac> localDirMac(do_QueryInterface(localDir));
+ rv = localDirMac->InitWithFSRef(&fsRef);
+ if (NS_FAILED(rv)) return rv;
+#elif defined(XP_OS2)
+ nsCOMPtr<nsIProperties> directoryService =
+ do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID, &rv);
+ if (NS_FAILED(rv)) return rv;
+ rv = directoryService->Get(NS_OS2_HOME_DIR, NS_GET_IID(nsILocalFile), getter_AddRefs(localDir));
+ if (NS_FAILED(rv)) return rv;
+#elif defined(XP_WIN)
+ nsCOMPtr<nsIProperties> directoryService =
+ do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID, &rv);
+ if (NS_FAILED(rv)) return rv;
+ rv = directoryService->Get(NS_WIN_APPDATA_DIR, NS_GET_IID(nsILocalFile), getter_AddRefs(localDir));
+ if (NS_SUCCEEDED(rv))
+ rv = localDir->Exists(&exists);
+ if (NS_FAILED(rv) || !exists)
+ {
+ // On some Win95 machines, NS_WIN_APPDATA_DIR does not exist - revert to NS_WIN_WINDOWS_DIR
+ localDir = nsnull;
+ rv = directoryService->Get(NS_WIN_WINDOWS_DIR, NS_GET_IID(nsILocalFile), getter_AddRefs(localDir));
+ }
+ if (NS_FAILED(rv)) return rv;
+#elif defined(XP_UNIX)
+ rv = NS_NewNativeLocalFile(nsDependentCString(PR_GetEnv("HOME")), PR_TRUE, getter_AddRefs(localDir));
+ if (NS_FAILED(rv)) return rv;
+#elif defined(XP_BEOS)
+ char path[MAXPATHLEN];
+ find_directory(B_USER_SETTINGS_DIRECTORY, 0, 0, path, MAXPATHLEN);
+ // Need enough space to add the trailing backslash
+ int len = strlen(path);
+ if (len > MAXPATHLEN-2)
+ return NS_ERROR_FAILURE;
+ path[len] = '/';
+ path[len+1] = '\0';
+ rv = NS_NewNativeLocalFile(nsDependentCString(path), PR_TRUE, getter_AddRefs(localDir));
+ if (NS_FAILED(rv)) return rv;
+#else
+#error dont_know_how_to_get_product_dir_on_your_platform
+#endif
+
+ rv = localDir->AppendRelativeNativePath(DEFAULT_PRODUCT_DIR);
+ if (NS_FAILED(rv)) return rv;
+ rv = localDir->Exists(&exists);
+ if (NS_SUCCEEDED(rv) && !exists)
+ rv = localDir->Create(nsIFile::DIRECTORY_TYPE, 0700);
+ if (NS_FAILED(rv)) return rv;
+
+ *aLocalFile = localDir;
+ NS_ADDREF(*aLocalFile);
+
+ return rv;
+}
+
+
+//----------------------------------------------------------------------------------------
+// GetDefaultUserProfileRoot - Gets the directory which contains each user profile dir
+//
+// UNIX : ~/.mozilla/
+// WIN : <Application Data folder on user's machine>\Mozilla\Profiles
+// Mac : :Documents:Mozilla:Profiles:
+//----------------------------------------------------------------------------------------
+NS_METHOD nsAppFileLocationProvider::GetDefaultUserProfileRoot(nsILocalFile **aLocalFile)
+{
+ NS_ENSURE_ARG_POINTER(aLocalFile);
+
+ nsresult rv;
+ nsCOMPtr<nsILocalFile> localDir;
+
+ rv = GetProductDirectory(getter_AddRefs(localDir));
+ if (NS_FAILED(rv)) return rv;
+
+#if defined(XP_MAC) || defined(XP_MACOSX) || defined(XP_OS2) || defined(XP_WIN)
+ // These 3 platforms share this part of the path - do them as one
+ rv = localDir->AppendRelativeNativePath(NS_LITERAL_CSTRING("Profiles"));
+ if (NS_FAILED(rv)) return rv;
+
+ PRBool exists;
+ rv = localDir->Exists(&exists);
+ if (NS_SUCCEEDED(rv) && !exists)
+ rv = localDir->Create(nsIFile::DIRECTORY_TYPE, 0775);
+ if (NS_FAILED(rv)) return rv;
+#endif
+
+ *aLocalFile = localDir;
+ NS_ADDREF(*aLocalFile);
+
+ return rv;
+}
+
+//*****************************************************************************
+// nsAppFileLocationProvider::nsIDirectoryServiceProvider2
+//*****************************************************************************
+
+class nsAppDirectoryEnumerator : public nsISimpleEnumerator
+{
+ public:
+ NS_DECL_ISUPPORTS
+
+ /**
+ * aKeyList is a null-terminated list of properties which are provided by aProvider
+ * They do not need to be publicly defined keys.
+ */
+ nsAppDirectoryEnumerator(nsIDirectoryServiceProvider *aProvider,
+ const char* aKeyList[]) :
+ mProvider(aProvider),
+ mCurrentKey(aKeyList)
+ {
+ }
+
+ NS_IMETHOD HasMoreElements(PRBool *result)
+ {
+ while (!mNext && *mCurrentKey)
+ {
+ PRBool dontCare;
+ nsCOMPtr<nsIFile> testFile;
+ (void)mProvider->GetFile(*mCurrentKey++, &dontCare, getter_AddRefs(testFile));
+ // Don't return a file which does not exist.
+ PRBool exists;
+ if (testFile && NS_SUCCEEDED(testFile->Exists(&exists)) && exists)
+ mNext = testFile;
+ }
+ *result = mNext != nsnull;
+ return NS_OK;
+ }
+
+ NS_IMETHOD GetNext(nsISupports **result)
+ {
+ NS_ENSURE_ARG_POINTER(result);
+ *result = nsnull;
+
+ PRBool hasMore;
+ HasMoreElements(&hasMore);
+ if (!hasMore)
+ return NS_ERROR_FAILURE;
+
+ *result = mNext;
+ NS_IF_ADDREF(*result);
+ mNext = nsnull;
+
+ return *result ? NS_OK : NS_ERROR_FAILURE;
+ }
+
+ // Virtual destructor since subclass nsPathsDirectoryEnumerator
+ // does not re-implement Release()
+
+ virtual ~nsAppDirectoryEnumerator()
+ {
+ }
+
+ protected:
+ nsIDirectoryServiceProvider *mProvider;
+ const char** mCurrentKey;
+ nsCOMPtr<nsIFile> mNext;
+};
+
+NS_IMPL_ISUPPORTS1(nsAppDirectoryEnumerator, nsISimpleEnumerator)
+
+/* nsPathsDirectoryEnumerator and PATH_SEPARATOR
+ * are not used on MacOS/X. */
+
+#if defined(XP_WIN) || defined(XP_OS2)/* Win32, Win16, and OS/2 */
+#define PATH_SEPARATOR ';'
+#else /*if defined(XP_UNIX) || defined(XP_BEOS)*/
+#define PATH_SEPARATOR ':'
+#endif
+
+class nsPathsDirectoryEnumerator : public nsAppDirectoryEnumerator
+{
+ public:
+ /**
+ * aKeyList is a null-terminated list.
+ * The first element is a path list.
+ * The remainder are properties provided by aProvider.
+ * They do not need to be publicly defined keys.
+ */
+ nsPathsDirectoryEnumerator(nsIDirectoryServiceProvider *aProvider,
+ const char* aKeyList[]) :
+ nsAppDirectoryEnumerator(aProvider, aKeyList+1),
+ mEndPath(aKeyList[0])
+ {
+ }
+
+ NS_IMETHOD HasMoreElements(PRBool *result)
+ {
+ if (mEndPath)
+ while (!mNext && *mEndPath)
+ {
+ const char *pathVar = mEndPath;
+ do { ++mEndPath; } while (*mEndPath && *mEndPath != PATH_SEPARATOR);
+
+ nsCOMPtr<nsILocalFile> localFile;
+ NS_NewNativeLocalFile(Substring(pathVar, mEndPath),
+ PR_TRUE,
+ getter_AddRefs(localFile));
+ if (*mEndPath == PATH_SEPARATOR)
+ ++mEndPath;
+ // Don't return a "file" (directory) which does not exist.
+ PRBool exists;
+ if (localFile &&
+ NS_SUCCEEDED(localFile->Exists(&exists)) &&
+ exists)
+ mNext = localFile;
+ }
+ if (mNext)
+ *result = PR_TRUE;
+ else
+ nsAppDirectoryEnumerator::HasMoreElements(result);
+
+ return NS_OK;
+ }
+
+ protected:
+ const char *mEndPath;
+};
+
+NS_IMETHODIMP
+nsAppFileLocationProvider::GetFiles(const char *prop, nsISimpleEnumerator **_retval)
+{
+ NS_ENSURE_ARG_POINTER(_retval);
+ *_retval = nsnull;
+ nsresult rv = NS_ERROR_FAILURE;
+
+ if (!nsCRT::strcmp(prop, NS_APP_PLUGINS_DIR_LIST))
+ {
+#if (defined(XP_MAC) || defined(XP_MACOSX)) && !defined(VBOX_MACOSX_FOLLOWS_UNIX_IO)
+ static const char* osXKeys[] = { NS_APP_PLUGINS_DIR, NS_MACOSX_USER_PLUGIN_DIR, NS_MACOSX_LOCAL_PLUGIN_DIR, nsnull };
+ static const char* os9Keys[] = { NS_APP_PLUGINS_DIR, NS_MAC_CLASSIC_PLUGIN_DIR, nsnull };
+ static const char** keys;
+
+ if (!keys) {
+ OSErr err;
+ SInt32 response;
+ err = ::Gestalt(gestaltSystemVersion, &response);
+ keys = (!err && response >= 0x00001000) ? osXKeys : os9Keys;
+ }
+
+ *_retval = new nsAppDirectoryEnumerator(this, keys);
+#else
+ static const char* keys[] = { nsnull, NS_APP_PLUGINS_DIR, nsnull };
+ if (!keys[0] && !(keys[0] = PR_GetEnv("VBOX_XPCOM_PLUGIN_PATH"))) {
+ static const char nullstr = 0;
+ keys[0] = &nullstr;
+ }
+ *_retval = new nsPathsDirectoryEnumerator(this, keys);
+#endif
+ NS_IF_ADDREF(*_retval);
+ rv = *_retval ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
+ }
+ return rv;
+}
diff --git a/src/libs/xpcom18a4/xpcom/io/nsAppFileLocationProvider.h b/src/libs/xpcom18a4/xpcom/io/nsAppFileLocationProvider.h
new file mode 100644
index 00000000..677ae00e
--- /dev/null
+++ b/src/libs/xpcom18a4/xpcom/io/nsAppFileLocationProvider.h
@@ -0,0 +1,67 @@
+/* -*- 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, released
+ * March 31, 1998.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2000
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Conrad Carlen <conrad@ingress.com>
+ *
+ * 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 "nsIDirectoryService.h"
+#include "nsILocalFile.h"
+
+class nsIFile;
+
+//*****************************************************************************
+// class nsAppFileLocationProvider
+//*****************************************************************************
+
+class nsAppFileLocationProvider : public nsIDirectoryServiceProvider2
+{
+public:
+ nsAppFileLocationProvider();
+
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIDIRECTORYSERVICEPROVIDER
+ NS_DECL_NSIDIRECTORYSERVICEPROVIDER2
+
+private:
+ ~nsAppFileLocationProvider() {}
+
+protected:
+ NS_METHOD CloneMozBinDirectory(nsILocalFile **aLocalFile);
+ NS_METHOD GetProductDirectory(nsILocalFile **aLocalFile);
+ NS_METHOD GetDefaultUserProfileRoot(nsILocalFile **aLocalFile);
+
+ nsCOMPtr<nsILocalFile> mMozBinDirectory;
+};
diff --git a/src/libs/xpcom18a4/xpcom/io/nsBinaryStream.cpp b/src/libs/xpcom18a4/xpcom/io/nsBinaryStream.cpp
new file mode 100644
index 00000000..bc60eea3
--- /dev/null
+++ b/src/libs/xpcom18a4/xpcom/io/nsBinaryStream.cpp
@@ -0,0 +1,663 @@
+/* -*- 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, released
+ * March 31, 1998.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1998-1999
+ * 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 ***** */
+
+/**
+ * This file contains implementations of the nsIBinaryInputStream and
+ * nsIBinaryOutputStream interfaces. Together, these interfaces allows reading
+ * and writing of primitive data types (integers, floating-point values,
+ * booleans, etc.) to a stream in a binary, untagged, fixed-endianness format.
+ * This might be used, for example, to implement network protocols or to
+ * produce architecture-neutral binary disk files, i.e. ones that can be read
+ * and written by both big-endian and little-endian platforms. Output is
+ * written in big-endian order (high-order byte first), as this is traditional
+ * network order.
+ *
+ * @See nsIBinaryInputStream
+ * @See nsIBinaryOutputStream
+ */
+#include <string.h>
+#include "nsBinaryStream.h"
+#include "nsCRT.h"
+#include "nsIStreamBufferAccess.h"
+#include "nsMemory.h"
+#include "prlong.h"
+#include "nsGenericFactory.h"
+
+NS_IMPL_ISUPPORTS3(nsBinaryOutputStream, nsIObjectOutputStream, nsIBinaryOutputStream, nsIOutputStream)
+
+NS_IMETHODIMP
+nsBinaryOutputStream::Flush() { return mOutputStream->Flush(); }
+
+NS_IMETHODIMP
+nsBinaryOutputStream::Close() { return mOutputStream->Close(); }
+
+NS_IMETHODIMP
+nsBinaryOutputStream::Write(const char *aBuf, PRUint32 aCount, PRUint32 *aActualBytes)
+{
+ return mOutputStream->Write(aBuf, aCount, aActualBytes);
+}
+
+NS_IMETHODIMP
+nsBinaryOutputStream::WriteFrom(nsIInputStream *inStr, PRUint32 count, PRUint32 *_retval)
+{
+ NS_NOTREACHED("WriteFrom");
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsBinaryOutputStream::WriteSegments(nsReadSegmentFun reader, void * closure, PRUint32 count, PRUint32 *_retval)
+{
+ NS_NOTREACHED("WriteSegments");
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsBinaryOutputStream::IsNonBlocking(PRBool *aNonBlocking)
+{
+ return mOutputStream->IsNonBlocking(aNonBlocking);
+}
+
+nsresult
+nsBinaryOutputStream::WriteFully(const char *aBuf, PRUint32 aCount)
+{
+ nsresult rv;
+ PRUint32 bytesWritten;
+
+ rv = mOutputStream->Write(aBuf, aCount, &bytesWritten);
+ if (NS_FAILED(rv)) return rv;
+ if (bytesWritten != aCount)
+ return NS_ERROR_FAILURE;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsBinaryOutputStream::SetOutputStream(nsIOutputStream *aOutputStream)
+{
+ NS_ENSURE_ARG_POINTER(aOutputStream);
+ mOutputStream = aOutputStream;
+ mBufferAccess = do_QueryInterface(aOutputStream);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsBinaryOutputStream::WriteBoolean(PRBool aBoolean)
+{
+ return Write8(aBoolean);
+}
+
+NS_IMETHODIMP
+nsBinaryOutputStream::Write8(PRUint8 aByte)
+{
+ return WriteFully((const char*)&aByte, sizeof aByte);
+}
+
+NS_IMETHODIMP
+nsBinaryOutputStream::Write16(PRUint16 a16)
+{
+ a16 = NS_SWAP16(a16);
+ return WriteFully((const char*)&a16, sizeof a16);
+}
+
+NS_IMETHODIMP
+nsBinaryOutputStream::Write32(PRUint32 a32)
+{
+ a32 = NS_SWAP32(a32);
+ return WriteFully((const char*)&a32, sizeof a32);
+}
+
+NS_IMETHODIMP
+nsBinaryOutputStream::Write64(PRUint64 a64)
+{
+ nsresult rv;
+ PRUint32 bytesWritten;
+
+ a64 = NS_SWAP64(a64);
+ rv = Write(NS_REINTERPRET_CAST(char*, &a64), sizeof a64, &bytesWritten);
+ if (NS_FAILED(rv)) return rv;
+ if (bytesWritten != sizeof a64)
+ return NS_ERROR_FAILURE;
+ return rv;
+}
+
+NS_IMETHODIMP
+nsBinaryOutputStream::WriteFloat(float aFloat)
+{
+ NS_ASSERTION(sizeof(float) == sizeof (PRUint32),
+ "False assumption about sizeof(float)");
+ return Write32(*NS_REINTERPRET_CAST(PRUint32*, &aFloat));
+}
+
+NS_IMETHODIMP
+nsBinaryOutputStream::WriteDouble(double aDouble)
+{
+ NS_ASSERTION(sizeof(double) == sizeof(PRUint64),
+ "False assumption about sizeof(double)");
+ return Write64(*NS_REINTERPRET_CAST(PRUint64*, &aDouble));
+}
+
+NS_IMETHODIMP
+nsBinaryOutputStream::WriteStringZ(const char *aString)
+{
+ PRUint32 length;
+ nsresult rv;
+
+ length = strlen(aString);
+ rv = Write32(length);
+ if (NS_FAILED(rv)) return rv;
+ return WriteFully(aString, length);
+}
+
+NS_IMETHODIMP
+nsBinaryOutputStream::WriteWStringZ(const PRUnichar* aString)
+{
+ PRUint32 length, byteCount;
+ nsresult rv;
+
+ length = nsCRT::strlen(aString);
+ rv = Write32(length);
+ if (NS_FAILED(rv)) return rv;
+
+ if (length == 0)
+ return NS_OK;
+ byteCount = length * sizeof(PRUnichar);
+
+#ifdef IS_BIG_ENDIAN
+ rv = WriteBytes(NS_REINTERPRET_CAST(const char*, aString), byteCount);
+#else
+ // XXX use WriteSegments here to avoid copy!
+ PRUnichar *copy, temp[64];
+ if (length <= 64) {
+ copy = temp;
+ } else {
+ copy = NS_REINTERPRET_CAST(PRUnichar*, nsMemory::Alloc(byteCount));
+ if (!copy)
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ NS_ASSERTION((PRUptrdiff(aString) & 0x1) == 0, "aString not properly aligned");
+ for (PRUint32 i = 0; i < length; i++)
+ copy[i] = NS_SWAP16(aString[i]);
+ rv = WriteBytes(NS_REINTERPRET_CAST(const char*, copy), byteCount);
+ if (copy != temp)
+ nsMemory::Free(copy);
+#endif
+
+ return rv;
+}
+
+NS_IMETHODIMP
+nsBinaryOutputStream::WriteUtf8Z(const PRUnichar* aString)
+{
+ NS_NOTREACHED("WriteUtf8Z");
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsBinaryOutputStream::WriteBytes(const char *aString, PRUint32 aLength)
+{
+ nsresult rv;
+ PRUint32 bytesWritten;
+
+ rv = Write(aString, aLength, &bytesWritten);
+ if (NS_FAILED(rv)) return rv;
+ if (bytesWritten != aLength)
+ return NS_ERROR_FAILURE;
+ return rv;
+}
+
+NS_IMETHODIMP
+nsBinaryOutputStream::WriteByteArray(PRUint8 *aBytes, PRUint32 aLength)
+{
+ return WriteBytes(NS_REINTERPRET_CAST(char *, aBytes), aLength);
+}
+
+NS_IMETHODIMP
+nsBinaryOutputStream::WriteObject(nsISupports* aObject, PRBool aIsStrongRef)
+{
+ NS_NOTREACHED("WriteObject");
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsBinaryOutputStream::WriteSingleRefObject(nsISupports* aObject)
+{
+ NS_NOTREACHED("WriteSingleRefObject");
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsBinaryOutputStream::WriteCompoundObject(nsISupports* aObject,
+ const nsIID& aIID,
+ PRBool aIsStrongRef)
+{
+ NS_NOTREACHED("WriteCompoundObject");
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsBinaryOutputStream::WriteID(const nsIID& aIID)
+{
+ NS_NOTREACHED("WriteID");
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP_(char*)
+nsBinaryOutputStream::GetBuffer(PRUint32 aLength, PRUint32 aAlignMask)
+{
+ if (mBufferAccess)
+ return mBufferAccess->GetBuffer(aLength, aAlignMask);
+ return nsnull;
+}
+
+NS_IMETHODIMP_(void)
+nsBinaryOutputStream::PutBuffer(char* aBuffer, PRUint32 aLength)
+{
+ if (mBufferAccess)
+ mBufferAccess->PutBuffer(aBuffer, aLength);
+}
+
+NS_IMPL_ISUPPORTS3(nsBinaryInputStream, nsIObjectInputStream, nsIBinaryInputStream, nsIInputStream)
+
+NS_IMETHODIMP
+nsBinaryInputStream::Available(PRUint32* aResult)
+{
+ return mInputStream->Available(aResult);
+}
+
+NS_IMETHODIMP
+nsBinaryInputStream::Read(char* aBuffer, PRUint32 aCount, PRUint32 *aNumRead)
+{
+ return mInputStream->Read(aBuffer, aCount, aNumRead);
+}
+
+
+// when forwarding ReadSegments to mInputStream, we need to make sure
+// 'this' is being passed to the writer each time. To do this, we need
+// a thunking function which keeps the real input stream around.
+
+// the closure wrapper
+struct ReadSegmentsClosure {
+ nsIInputStream* mRealInputStream;
+ void* mRealClosure;
+ nsWriteSegmentFun mRealWriter;
+};
+
+// the thunking function
+static NS_METHOD
+ReadSegmentForwardingThunk(nsIInputStream* aStream,
+ void *aClosure,
+ const char* aFromSegment,
+ PRUint32 aToOffset,
+ PRUint32 aCount,
+ PRUint32 *aWriteCount)
+{
+ ReadSegmentsClosure* thunkClosure =
+ NS_REINTERPRET_CAST(ReadSegmentsClosure*, aClosure);
+
+ return thunkClosure->mRealWriter(thunkClosure->mRealInputStream,
+ thunkClosure->mRealClosure,
+ aFromSegment, aToOffset,
+ aCount, aWriteCount);
+}
+
+
+NS_IMETHODIMP
+nsBinaryInputStream::ReadSegments(nsWriteSegmentFun writer, void * closure, PRUint32 count, PRUint32 *_retval)
+{
+ ReadSegmentsClosure thunkClosure = { this, closure, writer };
+
+ return mInputStream->ReadSegments(ReadSegmentForwardingThunk, &thunkClosure, count, _retval);
+}
+
+NS_IMETHODIMP
+nsBinaryInputStream::IsNonBlocking(PRBool *aNonBlocking)
+{
+ return mInputStream->IsNonBlocking(aNonBlocking);
+}
+
+NS_IMETHODIMP
+nsBinaryInputStream::Close() { return mInputStream->Close(); }
+
+NS_IMETHODIMP
+nsBinaryInputStream::SetInputStream(nsIInputStream *aInputStream)
+{
+ NS_ENSURE_ARG_POINTER(aInputStream);
+ mInputStream = aInputStream;
+ mBufferAccess = do_QueryInterface(aInputStream);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsBinaryInputStream::ReadBoolean(PRBool* aBoolean)
+{
+ PRUint8 byteResult;
+ nsresult rv = Read8(&byteResult);
+ *aBoolean = byteResult;
+ return rv;
+}
+
+NS_IMETHODIMP
+nsBinaryInputStream::Read8(PRUint8* aByte)
+{
+ nsresult rv;
+ PRUint32 bytesRead;
+
+ rv = Read(NS_REINTERPRET_CAST(char*, aByte), sizeof(*aByte), &bytesRead);
+ if (NS_FAILED(rv)) return rv;
+ if (bytesRead != 1)
+ return NS_ERROR_FAILURE;
+ return rv;
+}
+
+NS_IMETHODIMP
+nsBinaryInputStream::Read16(PRUint16* a16)
+{
+ nsresult rv;
+ PRUint32 bytesRead;
+
+ rv = Read(NS_REINTERPRET_CAST(char*, a16), sizeof *a16, &bytesRead);
+ if (NS_FAILED(rv)) return rv;
+ if (bytesRead != sizeof *a16)
+ return NS_ERROR_FAILURE;
+ *a16 = NS_SWAP16(*a16);
+ return rv;
+}
+
+NS_IMETHODIMP
+nsBinaryInputStream::Read32(PRUint32* a32)
+{
+ nsresult rv;
+ PRUint32 bytesRead;
+
+ rv = Read(NS_REINTERPRET_CAST(char*, a32), sizeof *a32, &bytesRead);
+ if (NS_FAILED(rv)) return rv;
+ if (bytesRead != sizeof *a32)
+ return NS_ERROR_FAILURE;
+ *a32 = NS_SWAP32(*a32);
+ return rv;
+}
+
+NS_IMETHODIMP
+nsBinaryInputStream::Read64(PRUint64* a64)
+{
+ nsresult rv;
+ PRUint32 bytesRead;
+
+ rv = Read(NS_REINTERPRET_CAST(char*, a64), sizeof *a64, &bytesRead);
+ if (NS_FAILED(rv)) return rv;
+ if (bytesRead != sizeof *a64)
+ return NS_ERROR_FAILURE;
+ *a64 = NS_SWAP64(*a64);
+ return rv;
+}
+
+NS_IMETHODIMP
+nsBinaryInputStream::ReadFloat(float* aFloat)
+{
+ NS_ASSERTION(sizeof(float) == sizeof (PRUint32),
+ "False assumption about sizeof(float)");
+ return Read32(NS_REINTERPRET_CAST(PRUint32*, aFloat));
+}
+
+NS_IMETHODIMP
+nsBinaryInputStream::ReadDouble(double* aDouble)
+{
+ NS_ASSERTION(sizeof(double) == sizeof(PRUint64),
+ "False assumption about sizeof(double)");
+ return Read64(NS_REINTERPRET_CAST(PRUint64*, aDouble));
+}
+
+static NS_METHOD
+WriteSegmentToCString(nsIInputStream* aStream,
+ void *aClosure,
+ const char* aFromSegment,
+ PRUint32 aToOffset,
+ PRUint32 aCount,
+ PRUint32 *aWriteCount)
+{
+ nsACString* outString = NS_STATIC_CAST(nsACString*,aClosure);
+
+ outString->Append(aFromSegment, aCount);
+
+ *aWriteCount = aCount;
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsBinaryInputStream::ReadCString(nsACString& aString)
+{
+ nsresult rv;
+ PRUint32 length, bytesRead;
+
+ rv = Read32(&length);
+ if (NS_FAILED(rv)) return rv;
+
+ aString.Truncate();
+ rv = ReadSegments(WriteSegmentToCString, &aString, length, &bytesRead);
+ if (NS_FAILED(rv)) return rv;
+
+ if (bytesRead != length)
+ return NS_ERROR_FAILURE;
+
+ return NS_OK;
+}
+
+
+// sometimes, WriteSegmentToString will be handed an odd-number of
+// bytes, which means we only have half of the last PRUnichar
+struct WriteStringClosure {
+ PRUnichar *mWriteCursor;
+ PRPackedBool mHasCarryoverByte;
+ char mCarryoverByte;
+};
+
+// there are a few cases we have to account for here:
+// * even length buffer, no carryover - easy, just append
+// * odd length buffer, no carryover - the last byte needs to be saved
+// for carryover
+// * odd length buffer, with carryover - first byte needs to be used
+// with the carryover byte, and
+// the rest of the even length
+// buffer is appended as normal
+// * even length buffer, with carryover - the first byte needs to be
+// used with the previous carryover byte.
+// this gives you an odd length buffer,
+// so you have to save the last byte for
+// the next carryover
+
+
+// same version of the above, but with correct casting and endian swapping
+static NS_METHOD
+WriteSegmentToString(nsIInputStream* aStream,
+ void *aClosure,
+ const char* aFromSegment,
+ PRUint32 aToOffset,
+ PRUint32 aCount,
+ PRUint32 *aWriteCount)
+{
+ NS_PRECONDITION(aCount > 0, "Why are we being told to write 0 bytes?");
+ NS_PRECONDITION(sizeof(PRUnichar) == 2, "We can't handle other sizes!");
+
+ WriteStringClosure* closure = NS_STATIC_CAST(WriteStringClosure*,aClosure);
+ PRUnichar *cursor = closure->mWriteCursor;
+
+ // we're always going to consume the whole buffer no matter what
+ // happens, so take care of that right now.. that allows us to
+ // tweak aCount later. Do NOT move this!
+ *aWriteCount = aCount;
+
+ // if the last Write had an odd-number of bytes read, then
+ if (closure->mHasCarryoverByte) {
+ // re-create the two-byte sequence we want to work with
+ char bytes[2] = { closure->mCarryoverByte, *aFromSegment };
+ *cursor = *(PRUnichar*)bytes;
+ // Now the little endianness dance
+#ifdef IS_LITTLE_ENDIAN
+ *cursor = (PRUnichar) NS_SWAP16(*cursor);
+#endif
+ ++cursor;
+
+ // now skip past the first byte of the buffer.. code from here
+ // can assume normal operations, but should not assume aCount
+ // is relative to the ORIGINAL buffer
+ ++aFromSegment;
+ --aCount;
+
+ closure->mHasCarryoverByte = PR_FALSE;
+ }
+
+ // this array is possibly unaligned... be careful how we access it!
+ const PRUnichar *unicodeSegment =
+ NS_REINTERPRET_CAST(const PRUnichar*, aFromSegment);
+
+ // calculate number of full characters in segment (aCount could be odd!)
+ PRUint32 segmentLength = aCount / sizeof(PRUnichar);
+
+ // copy all data into our aligned buffer. byte swap if necessary.
+ memcpy(cursor, unicodeSegment, segmentLength * sizeof(PRUnichar));
+ PRUnichar *end = cursor + segmentLength;
+#ifdef IS_LITTLE_ENDIAN
+ for (; cursor < end; ++cursor)
+ *cursor = (PRUnichar) NS_SWAP16(*cursor);
+#endif
+ closure->mWriteCursor = end;
+
+ // remember this is the modifed aCount and aFromSegment,
+ // so that will take into account the fact that we might have
+ // skipped the first byte in the buffer
+ if (aCount % sizeof(PRUnichar) != 0) {
+ // we must have had a carryover byte, that we'll need the next
+ // time around
+ closure->mCarryoverByte = aFromSegment[aCount - 1];
+ closure->mHasCarryoverByte = PR_TRUE;
+ }
+
+ return NS_OK;
+}
+
+
+NS_IMETHODIMP
+nsBinaryInputStream::ReadString(nsAString& aString)
+{
+ nsresult rv;
+ PRUint32 length, bytesRead;
+
+ rv = Read32(&length);
+ if (NS_FAILED(rv)) return rv;
+
+ // pre-allocate output buffer, and get direct access to buffer...
+ aString.SetLength(length);
+ nsAString::iterator start;
+ aString.BeginWriting(start);
+
+ WriteStringClosure closure;
+ closure.mWriteCursor = start.get();
+ closure.mHasCarryoverByte = PR_FALSE;
+
+ rv = ReadSegments(WriteSegmentToString, &closure,
+ length*sizeof(PRUnichar), &bytesRead);
+ if (NS_FAILED(rv)) return rv;
+
+ NS_ASSERTION(!closure.mHasCarryoverByte, "some strange stream corruption!");
+
+ if (bytesRead != length*sizeof(PRUnichar))
+ return NS_ERROR_FAILURE;
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsBinaryInputStream::ReadBytes(PRUint32 aLength, char* *_rval)
+{
+ nsresult rv;
+ PRUint32 bytesRead;
+ char* s;
+
+ s = NS_REINTERPRET_CAST(char*, nsMemory::Alloc(aLength));
+ if (!s)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ rv = Read(s, aLength, &bytesRead);
+ if (NS_FAILED(rv)) {
+ nsMemory::Free(s);
+ return rv;
+ }
+ if (bytesRead != aLength) {
+ nsMemory::Free(s);
+ return NS_ERROR_FAILURE;
+ }
+
+ *_rval = s;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsBinaryInputStream::ReadByteArray(PRUint32 aLength, PRUint8* *_rval)
+{
+ return ReadBytes(aLength, NS_REINTERPRET_CAST(char **, _rval));
+}
+
+NS_IMETHODIMP
+nsBinaryInputStream::ReadObject(PRBool aIsStrongRef, nsISupports* *aObject)
+{
+ NS_NOTREACHED("ReadObject");
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsBinaryInputStream::ReadID(nsID *aResult)
+{
+ NS_NOTREACHED("ReadID");
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP_(char*)
+nsBinaryInputStream::GetBuffer(PRUint32 aLength, PRUint32 aAlignMask)
+{
+ if (mBufferAccess)
+ return mBufferAccess->GetBuffer(aLength, aAlignMask);
+ return nsnull;
+}
+
+NS_IMETHODIMP_(void)
+nsBinaryInputStream::PutBuffer(char* aBuffer, PRUint32 aLength)
+{
+ if (mBufferAccess)
+ mBufferAccess->PutBuffer(aBuffer, aLength);
+}
+
diff --git a/src/libs/xpcom18a4/xpcom/io/nsBinaryStream.h b/src/libs/xpcom18a4/xpcom/io/nsBinaryStream.h
new file mode 100644
index 00000000..7be46dc7
--- /dev/null
+++ b/src/libs/xpcom18a4/xpcom/io/nsBinaryStream.h
@@ -0,0 +1,125 @@
+/* -*- 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, released
+ * March 31, 1998.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1998-2001
+ * 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 ***** */
+
+#ifndef nsBinaryStream_h___
+#define nsBinaryStream_h___
+
+#include "nsCOMPtr.h"
+#include "nsAString.h"
+#include "nsIObjectInputStream.h"
+#include "nsIObjectOutputStream.h"
+#include "nsIStreamBufferAccess.h"
+
+#define NS_BINARYOUTPUTSTREAM_CID \
+{ /* 86c37b9a-74e7-4672-844e-6e7dd83ba484 */ \
+ 0x86c37b9a, \
+ 0x74e7, \
+ 0x4672, \
+ {0x84, 0x4e, 0x6e, 0x7d, 0xd8, 0x3b, 0xa4, 0x84} \
+}
+
+#define NS_BINARYOUTPUTSTREAM_CONTRACTID "@mozilla.org/binaryoutputstream;1"
+#define NS_BINARYOUTPUTSTREAM_CLASSNAME "Binary Output Stream"
+
+// Derive from nsIObjectOutputStream so this class can be used as a superclass
+// by nsObjectOutputStream.
+class nsBinaryOutputStream : public nsIObjectOutputStream
+{
+public:
+ nsBinaryOutputStream() {};
+ // virtual dtor since subclasses call our Release()
+ virtual ~nsBinaryOutputStream() {};
+
+protected:
+ // nsISupports methods
+ NS_DECL_ISUPPORTS
+
+ // nsIOutputStream methods
+ NS_DECL_NSIOUTPUTSTREAM
+
+ // nsIBinaryOutputStream methods
+ NS_DECL_NSIBINARYOUTPUTSTREAM
+
+ // nsIObjectOutputStream methods
+ NS_DECL_NSIOBJECTOUTPUTSTREAM
+
+ // Call Write(), ensuring that all proffered data is written
+ nsresult WriteFully(const char *aBuf, PRUint32 aCount);
+
+ nsCOMPtr<nsIOutputStream> mOutputStream;
+ nsCOMPtr<nsIStreamBufferAccess> mBufferAccess;
+};
+
+#define NS_BINARYINPUTSTREAM_CID \
+{ /* c521a612-2aad-46db-b6ab-3b821fb150b1 */ \
+ 0xc521a612, \
+ 0x2aad, \
+ 0x46db, \
+ {0xb6, 0xab, 0x3b, 0x82, 0x1f, 0xb1, 0x50, 0xb1} \
+}
+
+#define NS_BINARYINPUTSTREAM_CONTRACTID "@mozilla.org/binaryinputstream;1"
+#define NS_BINARYINPUTSTREAM_CLASSNAME "Binary Input Stream"
+
+// Derive from nsIObjectInputStream so this class can be used as a superclass
+// by nsObjectInputStream.
+class nsBinaryInputStream : public nsIObjectInputStream
+{
+public:
+ nsBinaryInputStream() {};
+ // virtual dtor since subclasses call our Release()
+ virtual ~nsBinaryInputStream() {};
+
+protected:
+ // nsISupports methods
+ NS_DECL_ISUPPORTS
+
+ // nsIInputStream methods
+ NS_DECL_NSIINPUTSTREAM
+
+ // nsIBinaryInputStream methods
+ NS_DECL_NSIBINARYINPUTSTREAM
+
+ // nsIObjectInputStream methods
+ NS_DECL_NSIOBJECTINPUTSTREAM
+
+ nsCOMPtr<nsIInputStream> mInputStream;
+ nsCOMPtr<nsIStreamBufferAccess> mBufferAccess;
+};
+
+#endif // nsBinaryStream_h___
diff --git a/src/libs/xpcom18a4/xpcom/io/nsByteArrayInputStream.cpp b/src/libs/xpcom18a4/xpcom/io/nsByteArrayInputStream.cpp
new file mode 100644
index 00000000..364f71d5
--- /dev/null
+++ b/src/libs/xpcom18a4/xpcom/io/nsByteArrayInputStream.cpp
@@ -0,0 +1,164 @@
+/* -*- 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, released
+ * March 31, 1998.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1998-1999
+ * 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 ***** */
+
+#include "nsByteArrayInputStream.h"
+#include "nsMemory.h"
+
+NS_IMPL_THREADSAFE_ISUPPORTS2(nsByteArrayInputStream, nsIInputStream, nsIByteArrayInputStream)
+
+nsByteArrayInputStream::nsByteArrayInputStream (char *buffer, PRUint32 bytes)
+ : _buffer (buffer), _nbytes (bytes), _pos (0)
+{
+}
+
+nsByteArrayInputStream::~nsByteArrayInputStream ()
+{
+ if (_buffer != NULL)
+ nsMemory::Free (_buffer);
+}
+
+NS_IMETHODIMP
+nsByteArrayInputStream::Available (PRUint32* aResult)
+{
+ if (aResult == NULL)
+ return NS_ERROR_NULL_POINTER;
+
+ if (_nbytes == 0 || _buffer == NULL)
+ *aResult = 0;
+ else
+ *aResult = _nbytes - _pos;
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsByteArrayInputStream::Read (char* aBuffer, PRUint32 aCount, PRUint32 *aNumRead)
+{
+ if (aBuffer == NULL || aNumRead == NULL)
+ return NS_ERROR_NULL_POINTER;
+
+ if (_nbytes == 0)
+ return NS_ERROR_FAILURE;
+
+ if (aCount == 0 || _pos == _nbytes)
+ *aNumRead = 0;
+ else
+ {
+ NS_ASSERTION (_buffer != NULL, "Stream buffer has been released - there's an ownership problem somewhere!");
+ if (_buffer == NULL)
+ *aNumRead = 0;
+ else
+ if (aCount > _nbytes - _pos)
+ {
+ memcpy (aBuffer, &_buffer[_pos], *aNumRead = _nbytes - _pos);
+ _pos = _nbytes;
+ }
+ else
+ {
+ memcpy (aBuffer, &_buffer[_pos], *aNumRead = aCount);
+ _pos += aCount;
+ }
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsByteArrayInputStream::ReadSegments(nsWriteSegmentFun writer, void * aClosure, PRUint32 aCount, PRUint32 *aNumRead)
+{
+ if (aNumRead == NULL)
+ return NS_ERROR_NULL_POINTER;
+
+ if (_nbytes == 0)
+ return NS_ERROR_FAILURE;
+
+ if (aCount == 0 || _pos == _nbytes)
+ *aNumRead = 0;
+ else {
+ NS_ASSERTION (_buffer != NULL, "Stream buffer has been released - there's an ownership problem somewhere!");
+ PRUint32 readCount = PR_MIN(aCount, (_nbytes - _pos));
+ if (_buffer == NULL)
+ *aNumRead = 0;
+ else {
+ nsresult rv = writer (this, aClosure, &_buffer[_pos],
+ _pos, readCount, aNumRead);
+ if (NS_SUCCEEDED(rv))
+ _pos += *aNumRead;
+ }
+ }
+
+ // do not propogate errors returned from writer!
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsByteArrayInputStream::IsNonBlocking(PRBool *aNonBlocking)
+{
+ *aNonBlocking = PR_TRUE;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsByteArrayInputStream::Close ()
+{
+ if (_buffer != NULL)
+ {
+ nsMemory::Free (_buffer);
+ _buffer = NULL;
+ _nbytes = 0;
+ }
+ else
+ return NS_ERROR_FAILURE;
+
+ return NS_OK;
+}
+
+NS_COM nsresult
+NS_NewByteArrayInputStream (nsIByteArrayInputStream* *aResult, char * buffer, unsigned long bytes)
+{
+ if (aResult == NULL)
+ return NS_ERROR_NULL_POINTER;
+
+ nsIByteArrayInputStream * stream = new nsByteArrayInputStream (buffer, bytes);
+
+ if (!stream)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ NS_ADDREF (stream);
+ *aResult = stream;
+ return NS_OK;
+}
diff --git a/src/libs/xpcom18a4/xpcom/io/nsByteArrayInputStream.h b/src/libs/xpcom18a4/xpcom/io/nsByteArrayInputStream.h
new file mode 100644
index 00000000..2999eb5f
--- /dev/null
+++ b/src/libs/xpcom18a4/xpcom/io/nsByteArrayInputStream.h
@@ -0,0 +1,59 @@
+/* -*- 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, released
+ * March 31, 1998.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1998-1999
+ * 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 ***** */
+
+#include "nsIByteArrayInputStream.h"
+#include "nsCOMPtr.h"
+
+class nsByteArrayInputStream : public nsIByteArrayInputStream
+{
+ // nsISupports methods
+ NS_DECL_ISUPPORTS
+
+ // nsIInputStream methods
+ NS_DECL_NSIINPUTSTREAM
+
+public:
+ nsByteArrayInputStream (char *buffer, PRUint32 nbytes);
+
+private:
+ ~nsByteArrayInputStream ();
+
+ char *_buffer;
+ PRUint32 _nbytes;
+ PRUint32 _pos;
+};
diff --git a/src/libs/xpcom18a4/xpcom/io/nsDirectoryService.cpp b/src/libs/xpcom18a4/xpcom/io/nsDirectoryService.cpp
new file mode 100644
index 00000000..60b16eda
--- /dev/null
+++ b/src/libs/xpcom18a4/xpcom/io/nsDirectoryService.cpp
@@ -0,0 +1,1241 @@
+/* -*- 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.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):
+ * IBM Corp.
+ *
+ * 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 "nsCOMPtr.h"
+#include "nsDirectoryService.h"
+#include "nsDirectoryServiceDefs.h"
+#include "nsLocalFile.h"
+#include "nsDebug.h"
+#include "nsStaticAtom.h"
+
+#if defined(XP_MAC)
+#include <Folders.h>
+#include <Files.h>
+#include <Memory.h>
+#include <Processes.h>
+#include <Gestalt.h>
+#elif defined(XP_WIN)
+#include <windows.h>
+#include <shlobj.h>
+#include <stdlib.h>
+#include <stdio.h>
+#elif defined(XP_UNIX) || defined(XP_MACOSX)
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/param.h>
+#include <dlfcn.h>
+#include "prenv.h"
+#ifdef XP_MACOSX
+#include <CoreServices/CoreServices.h>
+#include <Folders.h>
+#include <Files.h>
+# ifndef VBOX_WITH_NEWER_OSX_SDK
+# include <Memory.h>
+# endif
+#include <Processes.h>
+#include <Gestalt.h>
+#include <CFURL.h>
+#include <InternetConfig.h>
+#endif
+#elif defined(XP_OS2)
+#define MAX_PATH _MAX_PATH
+#elif defined(XP_BEOS)
+#include <FindDirectory.h>
+#include <Path.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/param.h>
+#include <OS.h>
+#include <image.h>
+#include "prenv.h"
+#endif
+
+#include "SpecialSystemDirectory.h"
+#include "nsAppFileLocationProvider.h"
+
+#if defined(XP_MAC)
+#define COMPONENT_REGISTRY_NAME NS_LITERAL_CSTRING("Component Registry")
+#define COMPONENT_DIRECTORY NS_LITERAL_CSTRING("Components")
+#else
+#define COMPONENT_REGISTRY_NAME NS_LITERAL_CSTRING("compreg.dat")
+#define COMPONENT_DIRECTORY NS_LITERAL_CSTRING("components")
+#endif
+
+#define XPTI_REGISTRY_NAME NS_LITERAL_CSTRING("xpti.dat")
+
+// define home directory
+// For Windows platform, We are choosing Appdata folder as HOME
+#if defined (XP_WIN)
+#define HOME_DIR NS_WIN_APPDATA_DIR
+#elif defined (XP_MAC) || defined (XP_MACOSX)
+#define HOME_DIR NS_OSX_HOME_DIR
+#elif defined (XP_UNIX)
+#define HOME_DIR NS_UNIX_HOME_DIR
+#elif defined (XP_OS2)
+#define HOME_DIR NS_OS2_HOME_DIR
+#elif defined (XP_BEOS)
+#define HOME_DIR NS_BEOS_HOME_DIR
+#endif
+
+//----------------------------------------------------------------------------------------
+nsresult
+nsDirectoryService::GetCurrentProcessDirectory(nsILocalFile** aFile)
+//----------------------------------------------------------------------------------------
+{
+ NS_ENSURE_ARG_POINTER(aFile);
+ *aFile = nsnull;
+
+ // Set the component registry location:
+ if (!mService)
+ return NS_ERROR_FAILURE;
+
+ nsresult rv;
+
+ nsCOMPtr<nsIProperties> dirService;
+ rv = nsDirectoryService::Create(nsnull,
+ NS_GET_IID(nsIProperties),
+ getter_AddRefs(dirService)); // needs to be around for life of product
+
+ if (dirService)
+ {
+ nsCOMPtr <nsILocalFile> aLocalFile;
+ dirService->Get(NS_XPCOM_INIT_CURRENT_PROCESS_DIR, NS_GET_IID(nsILocalFile), getter_AddRefs(aLocalFile));
+ if (aLocalFile)
+ {
+ *aFile = aLocalFile;
+ NS_ADDREF(*aFile);
+ return NS_OK;
+ }
+ }
+
+ nsLocalFile* localFile = new nsLocalFile;
+
+ if (localFile == nsnull)
+ return NS_ERROR_OUT_OF_MEMORY;
+ NS_ADDREF(localFile);
+
+
+
+#ifdef XP_WIN
+ char buf[MAX_PATH];
+ if ( ::GetModuleFileName(0, buf, sizeof(buf)) ) {
+ // chop of the executable name by finding the rightmost backslash
+ char* lastSlash = PL_strrchr(buf, '\\');
+ if (lastSlash)
+ *(lastSlash + 1) = '\0';
+
+ localFile->InitWithNativePath(nsDependentCString(buf));
+ *aFile = localFile;
+ return NS_OK;
+ }
+
+#elif defined(XP_MAC)
+ // get info for the the current process to determine the directory
+ // its located in
+ OSErr err;
+ ProcessSerialNumber psn = {kNoProcess, kCurrentProcess};
+ ProcessInfoRec pInfo;
+ FSSpec tempSpec;
+
+ // initialize ProcessInfoRec before calling
+ // GetProcessInformation() or die horribly.
+ pInfo.processName = nil;
+ pInfo.processAppSpec = &tempSpec;
+ pInfo.processInfoLength = sizeof(ProcessInfoRec);
+
+ err = GetProcessInformation(&psn, &pInfo);
+ if (!err)
+ {
+ // create an FSSpec from the volume and dirid of the app.
+ FSSpec appFSSpec;
+ ::FSMakeFSSpec(pInfo.processAppSpec->vRefNum, pInfo.processAppSpec->parID, 0, &appFSSpec);
+
+ nsCOMPtr<nsILocalFileMac> localFileMac = do_QueryInterface((nsIFile*)localFile);
+ if (localFileMac)
+ {
+ localFileMac->InitWithFSSpec(&appFSSpec);
+ *aFile = localFile;
+ return NS_OK;
+ }
+ }
+#elif defined(XP_MACOSX)
+# ifdef MOZ_DEFAULT_VBOX_XPCOM_HOME
+ rv = localFile->InitWithNativePath(nsDependentCString(MOZ_DEFAULT_VBOX_XPCOM_HOME));
+ if (NS_SUCCEEDED(rv))
+ *aFile = localFile;
+# else
+ // 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)
+ {
+ // Pass PR_TRUE for the "resolveAgainstBase" arg to CFURLGetFileSystemRepresentation.
+ // This will resolve the relative portion of the CFURL against it base, giving a full
+ // path, which CFURLCopyFileSystemPath doesn't do.
+ char buffer[PATH_MAX];
+ if (CFURLGetFileSystemRepresentation(parentURL, PR_TRUE, (UInt8 *)buffer, sizeof(buffer)))
+ {
+#ifdef DEBUG_conrad
+ printf("nsDirectoryService - CurrentProcessDir is: %s\n", buffer);
+#endif
+ rv = localFile->InitWithNativePath(nsDependentCString(buffer));
+ if (NS_SUCCEEDED(rv))
+ *aFile = localFile;
+ }
+ CFRelease(parentURL);
+ }
+ CFRelease(bundleURL);
+ }
+ }
+#endif
+
+ NS_ASSERTION(*aFile, "nsDirectoryService - Could not determine CurrentProcessDir.\n");
+ if (*aFile)
+ return NS_OK;
+
+#elif defined(XP_UNIX)
+
+ // In the absence of a good way to get the executable directory let
+ // us try this for unix:
+ // - if VBOX_XPCOM_HOME is defined, that is it
+ // - else give the current directory
+ char buf[MAXPATHLEN];
+
+#if 0 /* we need .so location. */
+ // Actually we have a way on linux.
+ static volatile bool fPathSet = false;
+ static char szPath[MAXPATHLEN];
+ if (!fPathSet)
+ {
+ char buf2[MAXPATHLEN + 3];
+ buf2[0] = '\0';
+
+ /*
+ * Env.var. VBOX_XPCOM_HOME first.
+ */
+ char *psz = PR_GetEnv("VBOX_XPCOM_HOME");
+ if (psz)
+ {
+ if (strlen(psz) < MAXPATHLEN)
+ {
+ if (!realpath(psz, buf2))
+ strcpy(buf2, psz);
+ strcat(buf2, "/x"); /* for the filename stripping */
+ }
+ }
+
+ /*
+ * The dynamic loader.
+ */
+ if (!buf2[0])
+ {
+ Dl_info DlInfo = {0};
+ if ( !dladdr((void *)nsDirectoryService::mService, &DlInfo)
+ && DlInfo.dli_fname)
+ {
+ if (!realpath(DlInfo.dli_fname, buf2))
+ buf2[0] = '\0';
+ }
+ }
+
+ /*
+ * Executable location.
+ */
+ if (!buf2[0])
+ {
+ char buf[MAXPATHLEN];
+ int cchLink = readlink("/proc/self/exe", buf, sizeof(buf) - 1);
+ if (cchLink > 0 || cchLink != sizeof(buf) - 1)
+ {
+ buf[cchLink] = '\0';
+ if (!realpath(buf, buf2))
+ buf2[0] = '\0';
+ }
+ }
+
+ /*
+ * Copy to static buffer on success.
+ */
+ if (buf2[0])
+ {
+ char *p = strrchr(buf2, '/');
+ if (p)
+ {
+ p[p == buf2] = '\0';
+ #ifdef DEBUG
+ printf("debug: (1) VBOX_XPCOM_HOME=%s\n", buf2);
+ #endif
+ strcpy(szPath, buf2);
+ fPathSet = true;
+ }
+ }
+ }
+ if (fPathSet)
+ {
+ localFile->InitWithNativePath(nsDependentCString(szPath));
+ *aFile = localFile;
+ return NS_OK;
+ }
+
+#endif
+
+
+ // The MOZ_DEFAULT_VBOX_XPCOM_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 VBOX_XPCOM_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_VBOX_XPCOM_HOME
+ if (PR_GetEnv("VBOX_XPCOM_HOME") == nsnull)
+ PR_SetEnv("VBOX_XPCOM_HOME=" MOZ_DEFAULT_VBOX_XPCOM_HOME);
+#endif
+
+ char *moz5 = PR_GetEnv("VBOX_XPCOM_HOME");
+
+ if (moz5)
+ {
+ if (realpath(moz5, buf)) {
+ localFile->InitWithNativePath(nsDependentCString(buf));
+ *aFile = localFile;
+ return NS_OK;
+ }
+ }
+#if defined(DEBUG)
+ static PRBool firstWarning = PR_TRUE;
+
+ if(!moz5 && firstWarning) {
+ // Warn that VBOX_XPCOM_HOME not set, once.
+ printf("Warning: VBOX_XPCOM_HOME not set.\n");
+ firstWarning = PR_FALSE;
+ }
+#endif /* DEBUG */
+
+ // Fall back to current directory.
+ if (getcwd(buf, sizeof(buf)))
+ {
+ localFile->InitWithNativePath(nsDependentCString(buf));
+ *aFile = localFile;
+ return NS_OK;
+ }
+
+#elif defined(XP_OS2)
+ PPIB ppib;
+ PTIB ptib;
+ char buffer[CCHMAXPATH];
+ DosGetInfoBlocks( &ptib, &ppib);
+ DosQueryModuleName( ppib->pib_hmte, CCHMAXPATH, buffer);
+ *strrchr( buffer, '\\') = '\0'; // XXX DBCS misery
+ localFile->InitWithNativePath(nsDependentCString(buffer));
+ *aFile = localFile;
+ return NS_OK;
+
+#elif defined(XP_BEOS)
+
+ char *moz5 = getenv("VBOX_XPCOM_HOME");
+ if (moz5)
+ {
+ localFile->InitWithNativePath(nsDependentCString(moz5));
+ localFile->Normalize();
+ *aFile = localFile;
+ return NS_OK;
+ }
+ else
+ {
+ static char buf[MAXPATHLEN];
+ int32 cookie = 0;
+ image_info info;
+ char *p;
+ *buf = 0;
+ if(get_next_image_info(0, &cookie, &info) == B_OK)
+ {
+ strcpy(buf, info.name);
+ if((p = strrchr(buf, '/')) != 0)
+ {
+ *p = 0;
+ localFile->InitWithNativePath(nsDependentCString(buf));
+ *aFile = localFile;
+ return NS_OK;
+ }
+ }
+ }
+
+#endif
+
+ NS_RELEASE(localFile);
+
+ NS_ERROR("unable to get current process directory");
+ return NS_ERROR_FAILURE;
+} // GetCurrentProcessDirectory()
+
+
+nsIAtom* nsDirectoryService::sCurrentProcess = nsnull;
+nsIAtom* nsDirectoryService::sComponentRegistry = nsnull;
+nsIAtom* nsDirectoryService::sXPTIRegistry = nsnull;
+nsIAtom* nsDirectoryService::sComponentDirectory = nsnull;
+nsIAtom* nsDirectoryService::sGRE_Directory = nsnull;
+nsIAtom* nsDirectoryService::sGRE_ComponentDirectory = nsnull;
+nsIAtom* nsDirectoryService::sOS_DriveDirectory = nsnull;
+nsIAtom* nsDirectoryService::sOS_TemporaryDirectory = nsnull;
+nsIAtom* nsDirectoryService::sOS_CurrentProcessDirectory = nsnull;
+nsIAtom* nsDirectoryService::sOS_CurrentWorkingDirectory = nsnull;
+#if defined (XP_MACOSX)
+nsIAtom* nsDirectoryService::sDirectory = nsnull;
+nsIAtom* nsDirectoryService::sDesktopDirectory = nsnull;
+nsIAtom* nsDirectoryService::sTrashDirectory = nsnull;
+nsIAtom* nsDirectoryService::sStartupDirectory = nsnull;
+nsIAtom* nsDirectoryService::sShutdownDirectory = nsnull;
+nsIAtom* nsDirectoryService::sAppleMenuDirectory = nsnull;
+nsIAtom* nsDirectoryService::sControlPanelDirectory = nsnull;
+nsIAtom* nsDirectoryService::sExtensionDirectory = nsnull;
+nsIAtom* nsDirectoryService::sFontsDirectory = nsnull;
+nsIAtom* nsDirectoryService::sPreferencesDirectory = nsnull;
+nsIAtom* nsDirectoryService::sDocumentsDirectory = nsnull;
+nsIAtom* nsDirectoryService::sInternetSearchDirectory = nsnull;
+nsIAtom* nsDirectoryService::sUserLibDirectory = nsnull;
+nsIAtom* nsDirectoryService::sHomeDirectory = nsnull;
+nsIAtom* nsDirectoryService::sDefaultDownloadDirectory = nsnull;
+nsIAtom* nsDirectoryService::sUserDesktopDirectory = nsnull;
+nsIAtom* nsDirectoryService::sLocalDesktopDirectory = nsnull;
+nsIAtom* nsDirectoryService::sUserApplicationsDirectory = nsnull;
+nsIAtom* nsDirectoryService::sLocalApplicationsDirectory = nsnull;
+nsIAtom* nsDirectoryService::sUserDocumentsDirectory = nsnull;
+nsIAtom* nsDirectoryService::sLocalDocumentsDirectory = nsnull;
+nsIAtom* nsDirectoryService::sUserInternetPlugInDirectory = nsnull;
+nsIAtom* nsDirectoryService::sLocalInternetPlugInDirectory = nsnull;
+nsIAtom* nsDirectoryService::sUserFrameworksDirectory = nsnull;
+nsIAtom* nsDirectoryService::sLocalFrameworksDirectory = nsnull;
+nsIAtom* nsDirectoryService::sUserPreferencesDirectory = nsnull;
+nsIAtom* nsDirectoryService::sLocalPreferencesDirectory = nsnull;
+nsIAtom* nsDirectoryService::sPictureDocumentsDirectory = nsnull;
+nsIAtom* nsDirectoryService::sMovieDocumentsDirectory = nsnull;
+nsIAtom* nsDirectoryService::sMusicDocumentsDirectory = nsnull;
+nsIAtom* nsDirectoryService::sInternetSitesDirectory = nsnull;
+#elif defined (XP_WIN)
+nsIAtom* nsDirectoryService::sSystemDirectory = nsnull;
+nsIAtom* nsDirectoryService::sWindowsDirectory = nsnull;
+nsIAtom* nsDirectoryService::sHomeDirectory = nsnull;
+nsIAtom* nsDirectoryService::sDesktop = nsnull;
+nsIAtom* nsDirectoryService::sPrograms = nsnull;
+nsIAtom* nsDirectoryService::sControls = nsnull;
+nsIAtom* nsDirectoryService::sPrinters = nsnull;
+nsIAtom* nsDirectoryService::sPersonal = nsnull;
+nsIAtom* nsDirectoryService::sFavorites = nsnull;
+nsIAtom* nsDirectoryService::sStartup = nsnull;
+nsIAtom* nsDirectoryService::sRecent = nsnull;
+nsIAtom* nsDirectoryService::sSendto = nsnull;
+nsIAtom* nsDirectoryService::sBitbucket = nsnull;
+nsIAtom* nsDirectoryService::sStartmenu = nsnull;
+nsIAtom* nsDirectoryService::sDesktopdirectory = nsnull;
+nsIAtom* nsDirectoryService::sDrives = nsnull;
+nsIAtom* nsDirectoryService::sNetwork = nsnull;
+nsIAtom* nsDirectoryService::sNethood = nsnull;
+nsIAtom* nsDirectoryService::sFonts = nsnull;
+nsIAtom* nsDirectoryService::sTemplates = nsnull;
+nsIAtom* nsDirectoryService::sCommon_Startmenu = nsnull;
+nsIAtom* nsDirectoryService::sCommon_Programs = nsnull;
+nsIAtom* nsDirectoryService::sCommon_Startup = nsnull;
+nsIAtom* nsDirectoryService::sCommon_Desktopdirectory = nsnull;
+nsIAtom* nsDirectoryService::sAppdata = nsnull;
+nsIAtom* nsDirectoryService::sPrinthood = nsnull;
+nsIAtom* nsDirectoryService::sWinCookiesDirectory = nsnull;
+#elif defined (XP_UNIX)
+nsIAtom* nsDirectoryService::sLocalDirectory = nsnull;
+nsIAtom* nsDirectoryService::sLibDirectory = nsnull;
+nsIAtom* nsDirectoryService::sHomeDirectory = nsnull;
+#elif defined (XP_OS2)
+nsIAtom* nsDirectoryService::sSystemDirectory = nsnull;
+nsIAtom* nsDirectoryService::sOS2Directory = nsnull;
+nsIAtom* nsDirectoryService::sHomeDirectory = nsnull;
+nsIAtom* nsDirectoryService::sDesktopDirectory = nsnull;
+#elif defined (XP_BEOS)
+nsIAtom* nsDirectoryService::sSettingsDirectory = nsnull;
+nsIAtom* nsDirectoryService::sHomeDirectory = nsnull;
+nsIAtom* nsDirectoryService::sDesktopDirectory = nsnull;
+nsIAtom* nsDirectoryService::sSystemDirectory = nsnull;
+#endif
+
+
+nsDirectoryService* nsDirectoryService::mService = nsnull;
+
+nsDirectoryService::nsDirectoryService() :
+ mHashtable(256, PR_TRUE)
+{
+}
+
+NS_METHOD
+nsDirectoryService::Create(nsISupports *outer, REFNSIID aIID, void **aResult)
+{
+ NS_ENSURE_ARG_POINTER(aResult);
+ if (!mService)
+ {
+ mService = new nsDirectoryService();
+ if (!mService)
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ return mService->QueryInterface(aIID, aResult);
+}
+
+static const nsStaticAtom directory_atoms[] = {
+ { NS_XPCOM_CURRENT_PROCESS_DIR, &nsDirectoryService::sCurrentProcess },
+ { NS_XPCOM_COMPONENT_REGISTRY_FILE, &nsDirectoryService::sComponentRegistry },
+ { NS_XPCOM_COMPONENT_DIR, &nsDirectoryService::sComponentDirectory },
+ { NS_XPCOM_XPTI_REGISTRY_FILE, &nsDirectoryService::sXPTIRegistry },
+ { NS_GRE_DIR, &nsDirectoryService::sGRE_Directory },
+ { NS_GRE_COMPONENT_DIR, &nsDirectoryService::sGRE_ComponentDirectory },
+ { NS_OS_DRIVE_DIR, &nsDirectoryService::sOS_DriveDirectory },
+ { NS_OS_TEMP_DIR, &nsDirectoryService::sOS_TemporaryDirectory },
+ { NS_OS_CURRENT_PROCESS_DIR, &nsDirectoryService::sOS_CurrentProcessDirectory },
+ { NS_OS_CURRENT_WORKING_DIR, &nsDirectoryService::sOS_CurrentWorkingDirectory },
+ { NS_XPCOM_INIT_CURRENT_PROCESS_DIR, nsnull },
+#if defined (XP_MACOSX)
+ { NS_OS_SYSTEM_DIR, &nsDirectoryService::sDirectory },
+ { NS_MAC_DESKTOP_DIR, &nsDirectoryService::sDesktopDirectory },
+ { NS_MAC_TRASH_DIR, &nsDirectoryService::sTrashDirectory },
+ { NS_MAC_STARTUP_DIR, &nsDirectoryService::sStartupDirectory },
+ { NS_MAC_SHUTDOWN_DIR, &nsDirectoryService::sShutdownDirectory },
+ { NS_MAC_APPLE_MENU_DIR, &nsDirectoryService::sAppleMenuDirectory },
+ { NS_MAC_CONTROL_PANELS_DIR, &nsDirectoryService::sControlPanelDirectory },
+ { NS_MAC_EXTENSIONS_DIR, &nsDirectoryService::sExtensionDirectory },
+ { NS_MAC_FONTS_DIR, &nsDirectoryService::sFontsDirectory },
+ { NS_MAC_PREFS_DIR, &nsDirectoryService::sPreferencesDirectory },
+ { NS_MAC_DOCUMENTS_DIR, &nsDirectoryService::sDocumentsDirectory },
+ { NS_MAC_INTERNET_SEARCH_DIR, &nsDirectoryService::sInternetSearchDirectory },
+ { NS_MAC_USER_LIB_DIR, &nsDirectoryService::sUserLibDirectory },
+ { NS_OSX_HOME_DIR, &nsDirectoryService::sHomeDirectory },
+ { NS_OSX_DEFAULT_DOWNLOAD_DIR, &nsDirectoryService::sDefaultDownloadDirectory },
+ { NS_OSX_USER_DESKTOP_DIR, &nsDirectoryService::sUserDesktopDirectory },
+ { NS_OSX_LOCAL_DESKTOP_DIR, &nsDirectoryService::sLocalDesktopDirectory },
+ { NS_OSX_USER_APPLICATIONS_DIR, &nsDirectoryService::sUserApplicationsDirectory },
+ { NS_OSX_LOCAL_APPLICATIONS_DIR, &nsDirectoryService::sLocalApplicationsDirectory },
+ { NS_OSX_USER_DOCUMENTS_DIR, &nsDirectoryService::sUserDocumentsDirectory },
+ { NS_OSX_LOCAL_DOCUMENTS_DIR, &nsDirectoryService::sLocalDocumentsDirectory },
+ { NS_OSX_USER_INTERNET_PLUGIN_DIR, &nsDirectoryService::sUserInternetPlugInDirectory },
+ { NS_OSX_LOCAL_INTERNET_PLUGIN_DIR, &nsDirectoryService::sLocalInternetPlugInDirectory },
+ { NS_OSX_USER_FRAMEWORKS_DIR, &nsDirectoryService::sUserFrameworksDirectory },
+ { NS_OSX_LOCAL_FRAMEWORKS_DIR, &nsDirectoryService::sLocalFrameworksDirectory },
+ { NS_OSX_USER_PREFERENCES_DIR, &nsDirectoryService::sUserPreferencesDirectory },
+ { NS_OSX_LOCAL_PREFERENCES_DIR, &nsDirectoryService::sLocalPreferencesDirectory },
+ { NS_OSX_PICTURE_DOCUMENTS_DIR, &nsDirectoryService::sPictureDocumentsDirectory },
+ { NS_OSX_MOVIE_DOCUMENTS_DIR, &nsDirectoryService::sMovieDocumentsDirectory },
+ { NS_OSX_MUSIC_DOCUMENTS_DIR, &nsDirectoryService::sMusicDocumentsDirectory },
+ { NS_OSX_INTERNET_SITES_DIR, &nsDirectoryService::sInternetSitesDirectory },
+#elif defined (XP_WIN)
+ { NS_OS_SYSTEM_DIR, &nsDirectoryService::sSystemDirectory },
+ { NS_WIN_WINDOWS_DIR, &nsDirectoryService::sWindowsDirectory },
+ { NS_WIN_HOME_DIR, &nsDirectoryService::sHomeDirectory },
+ { NS_WIN_DESKTOP_DIR, &nsDirectoryService::sDesktop },
+ { NS_WIN_PROGRAMS_DIR, &nsDirectoryService::sPrograms },
+ { NS_WIN_CONTROLS_DIR, &nsDirectoryService::sControls },
+ { NS_WIN_PRINTERS_DIR, &nsDirectoryService::sPrinters },
+ { NS_WIN_PERSONAL_DIR, &nsDirectoryService::sPersonal },
+ { NS_WIN_FAVORITES_DIR, &nsDirectoryService::sFavorites },
+ { NS_WIN_STARTUP_DIR, &nsDirectoryService::sStartup },
+ { NS_WIN_RECENT_DIR, &nsDirectoryService::sRecent },
+ { NS_WIN_SEND_TO_DIR, &nsDirectoryService::sSendto },
+ { NS_WIN_BITBUCKET_DIR, &nsDirectoryService::sBitbucket },
+ { NS_WIN_STARTMENU_DIR, &nsDirectoryService::sStartmenu },
+ { NS_WIN_DESKTOP_DIRECTORY, &nsDirectoryService::sDesktopdirectory },
+ { NS_WIN_DRIVES_DIR, &nsDirectoryService::sDrives },
+ { NS_WIN_NETWORK_DIR, &nsDirectoryService::sNetwork },
+ { NS_WIN_NETHOOD_DIR, &nsDirectoryService::sNethood },
+ { NS_WIN_FONTS_DIR, &nsDirectoryService::sFonts },
+ { NS_WIN_TEMPLATES_DIR, &nsDirectoryService::sTemplates },
+ { NS_WIN_COMMON_STARTMENU_DIR, &nsDirectoryService::sCommon_Startmenu },
+ { NS_WIN_COMMON_PROGRAMS_DIR, &nsDirectoryService::sCommon_Programs },
+ { NS_WIN_COMMON_STARTUP_DIR, &nsDirectoryService::sCommon_Startup },
+ { NS_WIN_COMMON_DESKTOP_DIRECTORY, &nsDirectoryService::sCommon_Desktopdirectory },
+ { NS_WIN_APPDATA_DIR, &nsDirectoryService::sAppdata },
+ { NS_WIN_PRINTHOOD, &nsDirectoryService::sPrinthood },
+ { NS_WIN_COOKIES_DIR, &nsDirectoryService::sWinCookiesDirectory },
+#elif defined (XP_UNIX)
+ { NS_UNIX_LOCAL_DIR, &nsDirectoryService::sLocalDirectory },
+ { NS_UNIX_LIB_DIR, &nsDirectoryService::sLibDirectory },
+ { NS_UNIX_HOME_DIR, &nsDirectoryService::sHomeDirectory },
+#elif defined (XP_OS2)
+ { NS_OS_SYSTEM_DIR, &nsDirectoryService::sSystemDirectory },
+ { NS_OS2_DIR, &nsDirectoryService::sOS2Directory },
+ { NS_OS2_HOME_DIR, &nsDirectoryService::sHomeDirectory },
+ { NS_OS2_DESKTOP_DIR, &nsDirectoryService::sDesktopDirectory },
+#elif defined (XP_BEOS)
+ { NS_OS_SYSTEM_DIR, &nsDirectoryService::sSystemDirectory },
+ { NS_BEOS_SETTINGS_DIR, &nsDirectoryService::sSettingsDirectory },
+ { NS_BEOS_HOME_DIR, &nsDirectoryService::sHomeDirectory },
+ { NS_BEOS_DESKTOP_DIR, &nsDirectoryService::sDesktopDirectory },
+#endif
+};
+
+nsresult
+nsDirectoryService::Init()
+{
+ nsresult rv;
+
+ rv = NS_NewISupportsArray(getter_AddRefs(mProviders));
+ if (NS_FAILED(rv)) return rv;
+
+ NS_RegisterStaticAtoms(directory_atoms, NS_ARRAY_LENGTH(directory_atoms));
+
+ // Let the list hold the only reference to the provider.
+ nsAppFileLocationProvider *defaultProvider = new nsAppFileLocationProvider;
+ if (!defaultProvider)
+ return NS_ERROR_OUT_OF_MEMORY;
+ // AppendElement returns PR_TRUE for success.
+ rv = mProviders->AppendElement(defaultProvider) ? NS_OK : NS_ERROR_FAILURE;
+
+ return rv;
+}
+
+PRBool
+nsDirectoryService::ReleaseValues(nsHashKey* key, void* data, void* closure)
+{
+ nsISupports* value = (nsISupports*)data;
+ NS_IF_RELEASE(value);
+ return PR_TRUE;
+}
+
+nsDirectoryService::~nsDirectoryService()
+{
+ // clear the global
+ mService = nsnull;
+
+}
+
+NS_IMPL_THREADSAFE_ISUPPORTS4(nsDirectoryService, nsIProperties, nsIDirectoryService, nsIDirectoryServiceProvider, nsIDirectoryServiceProvider2)
+
+
+NS_IMETHODIMP
+nsDirectoryService::Undefine(const char* prop)
+{
+ nsCStringKey key(prop);
+ if (!mHashtable.Exists(&key))
+ return NS_ERROR_FAILURE;
+
+ mHashtable.Remove (&key);
+ return NS_OK;
+ }
+
+NS_IMETHODIMP
+nsDirectoryService::GetKeys(PRUint32 *count, char ***keys)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+struct FileData
+{
+ FileData(const char* aProperty,
+ const nsIID& aUUID) :
+ property(aProperty),
+ data(nsnull),
+ persistent(PR_TRUE),
+ uuid(aUUID) {}
+
+ const char* property;
+ nsISupports* data;
+ PRBool persistent;
+ const nsIID& uuid;
+};
+
+static PRBool FindProviderFile(nsISupports* aElement, void *aData)
+{
+ nsresult rv;
+ FileData* fileData = (FileData*)aData;
+ if (fileData->uuid.Equals(NS_GET_IID(nsISimpleEnumerator)))
+ {
+ // Not all providers implement this iface
+ nsCOMPtr<nsIDirectoryServiceProvider2> prov2 = do_QueryInterface(aElement);
+ if (prov2)
+ {
+ rv = prov2->GetFiles(fileData->property, (nsISimpleEnumerator **)&fileData->data);
+ if (NS_SUCCEEDED(rv) && fileData->data) {
+ fileData->persistent = PR_FALSE; // Enumerators can never be peristent
+ return PR_FALSE;
+ }
+ }
+ }
+ else
+ {
+ nsCOMPtr<nsIDirectoryServiceProvider> prov = do_QueryInterface(aElement);
+ if (!prov)
+ return PR_FALSE;
+ rv = prov->GetFile(fileData->property, &fileData->persistent, (nsIFile **)&fileData->data);
+ if (NS_SUCCEEDED(rv) && fileData->data)
+ return PR_FALSE;
+ }
+
+ return PR_TRUE;
+}
+
+NS_IMETHODIMP
+nsDirectoryService::Get(const char* prop, const nsIID & uuid, void* *result)
+{
+ nsCStringKey key(prop);
+
+ nsCOMPtr<nsISupports> value = dont_AddRef(mHashtable.Get(&key));
+
+ if (value)
+ {
+ nsCOMPtr<nsIFile> cloneFile;
+ nsCOMPtr<nsIFile> cachedFile = do_QueryInterface(value);
+ NS_ASSERTION(cachedFile, "nsIFile expected");
+
+ cachedFile->Clone(getter_AddRefs(cloneFile));
+ return cloneFile->QueryInterface(uuid, result);
+ }
+
+ // it is not one of our defaults, lets check any providers
+ FileData fileData(prop, uuid);
+
+ mProviders->EnumerateBackwards(FindProviderFile, &fileData);
+ if (fileData.data)
+ {
+ if (fileData.persistent)
+ {
+ Set(prop, NS_STATIC_CAST(nsIFile*, fileData.data));
+ }
+ nsresult rv = (fileData.data)->QueryInterface(uuid, result);
+ NS_RELEASE(fileData.data); // addref occurs in FindProviderFile()
+ return rv;
+ }
+
+ FindProviderFile(NS_STATIC_CAST(nsIDirectoryServiceProvider*, this), &fileData);
+ if (fileData.data)
+ {
+ if (fileData.persistent)
+ {
+ Set(prop, NS_STATIC_CAST(nsIFile*, fileData.data));
+ }
+ nsresult rv = (fileData.data)->QueryInterface(uuid, result);
+ NS_RELEASE(fileData.data); // addref occurs in FindProviderFile()
+ return rv;
+ }
+
+ return NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+nsDirectoryService::Set(const char* prop, nsISupports* value)
+{
+ nsCStringKey key(prop);
+ if (mHashtable.Exists(&key) || value == nsnull)
+ return NS_ERROR_FAILURE;
+
+ nsCOMPtr<nsIFile> ourFile;
+ value->QueryInterface(NS_GET_IID(nsIFile), getter_AddRefs(ourFile));
+ if (ourFile)
+ {
+ nsCOMPtr<nsIFile> cloneFile;
+ ourFile->Clone (getter_AddRefs (cloneFile));
+ mHashtable.Put(&key, cloneFile);
+
+ return NS_OK;
+ }
+
+ return NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+nsDirectoryService::Has(const char *prop, PRBool *_retval)
+{
+ *_retval = PR_FALSE;
+ nsCOMPtr<nsIFile> value;
+ nsresult rv = Get(prop, NS_GET_IID(nsIFile), getter_AddRefs(value));
+ if (NS_FAILED(rv))
+ return rv;
+
+ if (value)
+ {
+ *_retval = PR_TRUE;
+ }
+
+ return rv;
+}
+
+NS_IMETHODIMP
+nsDirectoryService::RegisterProvider(nsIDirectoryServiceProvider *prov)
+{
+ nsresult rv;
+ if (!prov)
+ return NS_ERROR_FAILURE;
+ if (!mProviders)
+ return NS_ERROR_NOT_INITIALIZED;
+
+ nsCOMPtr<nsISupports> supports = do_QueryInterface(prov, &rv);
+ if (NS_FAILED(rv)) return rv;
+
+ // AppendElement returns PR_TRUE for success.
+ return mProviders->AppendElement(supports) ? NS_OK : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+nsDirectoryService::UnregisterProvider(nsIDirectoryServiceProvider *prov)
+{
+ nsresult rv;
+ if (!prov)
+ return NS_ERROR_FAILURE;
+ if (!mProviders)
+ return NS_ERROR_NOT_INITIALIZED;
+
+ nsCOMPtr<nsISupports> supports = do_QueryInterface(prov, &rv);
+ if (NS_FAILED(rv)) return rv;
+
+ // RemoveElement returns PR_TRUE for success.
+ return mProviders->RemoveElement(supports) ? NS_OK : NS_ERROR_FAILURE;
+}
+
+// DO NOT ADD ANY LOCATIONS TO THIS FUNCTION UNTIL YOU TALK TO: dougt@netscape.com.
+// This is meant to be a place of xpcom or system specific file locations, not
+// application specific locations. If you need the later, register a callback for
+// your application.
+
+NS_IMETHODIMP
+nsDirectoryService::GetFile(const char *prop, PRBool *persistent, nsIFile **_retval)
+{
+ nsCOMPtr<nsILocalFile> localFile;
+ nsresult rv = NS_ERROR_FAILURE;
+
+ *_retval = nsnull;
+ *persistent = PR_TRUE;
+
+ nsIAtom* inAtom = NS_NewAtom(prop);
+
+ // check to see if it is one of our defaults
+
+ if (inAtom == nsDirectoryService::sCurrentProcess ||
+ inAtom == nsDirectoryService::sOS_CurrentProcessDirectory )
+ {
+ rv = GetCurrentProcessDirectory(getter_AddRefs(localFile));
+ }
+ else if (inAtom == nsDirectoryService::sComponentRegistry)
+ {
+ rv = GetCurrentProcessDirectory(getter_AddRefs(localFile));
+ if (!localFile)
+ return NS_ERROR_FAILURE;
+
+ localFile->AppendNative(COMPONENT_DIRECTORY);
+ localFile->AppendNative(COMPONENT_REGISTRY_NAME);
+ }
+ else if (inAtom == nsDirectoryService::sXPTIRegistry)
+ {
+ rv = GetCurrentProcessDirectory(getter_AddRefs(localFile));
+ if (!localFile)
+ return NS_ERROR_FAILURE;
+
+ localFile->AppendNative(COMPONENT_DIRECTORY);
+ localFile->AppendNative(XPTI_REGISTRY_NAME);
+ }
+
+ // Unless otherwise set, the core pieces of the GRE exist
+ // in the current process directory.
+ else if (inAtom == nsDirectoryService::sGRE_Directory)
+ {
+ rv = GetCurrentProcessDirectory(getter_AddRefs(localFile));
+ }
+ // the GRE components directory is relative to the GRE directory
+ // by default; applications may override this behavior in special
+ // cases
+ else if (inAtom == nsDirectoryService::sGRE_ComponentDirectory)
+ {
+ rv = Get(NS_GRE_DIR, nsILocalFile::GetIID(), getter_AddRefs(localFile));
+ if (localFile)
+ localFile->AppendNative(COMPONENT_DIRECTORY);
+ }
+ else if (inAtom == nsDirectoryService::sComponentDirectory)
+ {
+ rv = GetCurrentProcessDirectory(getter_AddRefs(localFile));
+ if (localFile)
+ localFile->AppendNative(COMPONENT_DIRECTORY);
+ }
+ else if (inAtom == nsDirectoryService::sOS_DriveDirectory)
+ {
+ rv = GetSpecialSystemDirectory(OS_DriveDirectory, getter_AddRefs(localFile));
+ }
+ else if (inAtom == nsDirectoryService::sOS_TemporaryDirectory)
+ {
+ rv = GetSpecialSystemDirectory(OS_TemporaryDirectory, getter_AddRefs(localFile));
+ }
+ else if (inAtom == nsDirectoryService::sOS_CurrentProcessDirectory)
+ {
+ rv = GetSpecialSystemDirectory(OS_CurrentProcessDirectory, getter_AddRefs(localFile));
+ }
+ else if (inAtom == nsDirectoryService::sOS_CurrentWorkingDirectory)
+ {
+ rv = GetSpecialSystemDirectory(OS_CurrentWorkingDirectory, getter_AddRefs(localFile));
+ }
+
+#if defined(XP_MACOSX)
+ else if (inAtom == nsDirectoryService::sDirectory)
+ {
+ rv = GetOSXFolderType(kClassicDomain, kSystemFolderType, getter_AddRefs(localFile));
+ }
+ else if (inAtom == nsDirectoryService::sDesktopDirectory)
+ {
+ rv = GetOSXFolderType(kClassicDomain, kDesktopFolderType, getter_AddRefs(localFile));
+ }
+ else if (inAtom == nsDirectoryService::sTrashDirectory)
+ {
+ rv = GetOSXFolderType(kClassicDomain, kTrashFolderType, getter_AddRefs(localFile));
+ }
+ else if (inAtom == nsDirectoryService::sStartupDirectory)
+ {
+ rv = GetOSXFolderType(kClassicDomain, kStartupFolderType, getter_AddRefs(localFile));
+ }
+ else if (inAtom == nsDirectoryService::sShutdownDirectory)
+ {
+ rv = GetOSXFolderType(kClassicDomain, kShutdownFolderType, getter_AddRefs(localFile));
+ }
+ else if (inAtom == nsDirectoryService::sAppleMenuDirectory)
+ {
+ rv = GetOSXFolderType(kClassicDomain, kAppleMenuFolderType, getter_AddRefs(localFile));
+ }
+ else if (inAtom == nsDirectoryService::sControlPanelDirectory)
+ {
+ rv = GetOSXFolderType(kClassicDomain, kControlPanelFolderType, getter_AddRefs(localFile));
+ }
+ else if (inAtom == nsDirectoryService::sExtensionDirectory)
+ {
+ rv = GetOSXFolderType(kClassicDomain, kExtensionFolderType, getter_AddRefs(localFile));
+ }
+ else if (inAtom == nsDirectoryService::sFontsDirectory)
+ {
+ rv = GetOSXFolderType(kClassicDomain, kFontsFolderType, getter_AddRefs(localFile));
+ }
+ else if (inAtom == nsDirectoryService::sPreferencesDirectory)
+ {
+ rv = GetOSXFolderType(kClassicDomain, kPreferencesFolderType, getter_AddRefs(localFile));
+ }
+ else if (inAtom == nsDirectoryService::sDocumentsDirectory)
+ {
+ rv = GetOSXFolderType(kClassicDomain, kDocumentsFolderType, getter_AddRefs(localFile));
+ }
+ else if (inAtom == nsDirectoryService::sInternetSearchDirectory)
+ {
+ rv = GetOSXFolderType(kClassicDomain, kInternetSearchSitesFolderType, getter_AddRefs(localFile));
+ }
+ else if (inAtom == nsDirectoryService::sUserLibDirectory)
+ {
+ rv = GetOSXFolderType(kUserDomain, kDomainLibraryFolderType, getter_AddRefs(localFile));
+ }
+ else if (inAtom == nsDirectoryService::sHomeDirectory)
+ {
+ rv = GetOSXFolderType(kUserDomain, kDomainTopLevelFolderType, getter_AddRefs(localFile));
+ }
+ else if (inAtom == nsDirectoryService::sDefaultDownloadDirectory)
+ {
+ NS_NewLocalFile(EmptyString(), PR_TRUE, getter_AddRefs(localFile));
+ nsCOMPtr<nsILocalFileMac> localMacFile(do_QueryInterface(localFile));
+
+ if (localMacFile)
+ {
+ OSErr err;
+ ICInstance icInstance;
+
+ err = ::ICStart(&icInstance, 'XPCM');
+ if (err == noErr)
+ {
+ ICAttr attrs;
+ ICFileSpec icFileSpec;
+ long size = kICFileSpecHeaderSize;
+ err = ::ICGetPref(icInstance, kICDownloadFolder, &attrs, &icFileSpec, &size);
+ if (err == noErr || (err == icTruncatedErr && size >= kICFileSpecHeaderSize))
+ {
+ rv = localMacFile->InitWithFSSpec(&icFileSpec.fss);
+ }
+ ::ICStop(icInstance);
+ }
+
+ if (NS_FAILED(rv))
+ {
+ // We got an error getting the DL folder from IC so try finding the user's Desktop folder
+ rv = GetOSXFolderType(kUserDomain, kDesktopFolderType, getter_AddRefs(localFile));
+ }
+ }
+
+ // Don't cache the DL directory as the user may change it while we're running.
+ // Negligible perf hit as this directory is only requested for downloads
+ *persistent = PR_FALSE;
+ }
+ else if (inAtom == nsDirectoryService::sUserDesktopDirectory)
+ {
+ rv = GetOSXFolderType(kUserDomain, kDesktopFolderType, getter_AddRefs(localFile));
+ }
+ else if (inAtom == nsDirectoryService::sLocalDesktopDirectory)
+ {
+ rv = GetOSXFolderType(kLocalDomain, kDesktopFolderType, getter_AddRefs(localFile));
+ }
+ else if (inAtom == nsDirectoryService::sUserApplicationsDirectory)
+ {
+ rv = GetOSXFolderType(kUserDomain, kApplicationsFolderType, getter_AddRefs(localFile));
+ }
+ else if (inAtom == nsDirectoryService::sLocalApplicationsDirectory)
+ {
+ rv = GetOSXFolderType(kLocalDomain, kApplicationsFolderType, getter_AddRefs(localFile));
+ }
+ else if (inAtom == nsDirectoryService::sUserDocumentsDirectory)
+ {
+ rv = GetOSXFolderType(kUserDomain, kDocumentsFolderType, getter_AddRefs(localFile));
+ }
+ else if (inAtom == nsDirectoryService::sLocalDocumentsDirectory)
+ {
+ rv = GetOSXFolderType(kLocalDomain, kDocumentsFolderType, getter_AddRefs(localFile));
+ }
+ else if (inAtom == nsDirectoryService::sUserInternetPlugInDirectory)
+ {
+ rv = GetOSXFolderType(kUserDomain, kInternetPlugInFolderType, getter_AddRefs(localFile));
+ }
+ else if (inAtom == nsDirectoryService::sLocalInternetPlugInDirectory)
+ {
+ rv = GetOSXFolderType(kLocalDomain, kInternetPlugInFolderType, getter_AddRefs(localFile));
+ }
+ else if (inAtom == nsDirectoryService::sUserFrameworksDirectory)
+ {
+ rv = GetOSXFolderType(kUserDomain, kFrameworksFolderType, getter_AddRefs(localFile));
+ }
+ else if (inAtom == nsDirectoryService::sLocalFrameworksDirectory)
+ {
+ rv = GetOSXFolderType(kLocalDomain, kFrameworksFolderType, getter_AddRefs(localFile));
+ }
+ else if (inAtom == nsDirectoryService::sUserPreferencesDirectory)
+ {
+ rv = GetOSXFolderType(kUserDomain, kPreferencesFolderType, getter_AddRefs(localFile));
+ }
+ else if (inAtom == nsDirectoryService::sLocalPreferencesDirectory)
+ {
+ rv = GetOSXFolderType(kLocalDomain, kPreferencesFolderType, getter_AddRefs(localFile));
+ }
+ else if (inAtom == nsDirectoryService::sPictureDocumentsDirectory)
+ {
+ rv = GetOSXFolderType(kUserDomain, kPictureDocumentsFolderType, getter_AddRefs(localFile));
+ }
+ else if (inAtom == nsDirectoryService::sMovieDocumentsDirectory)
+ {
+ rv = GetOSXFolderType(kUserDomain, kMovieDocumentsFolderType, getter_AddRefs(localFile));
+ }
+ else if (inAtom == nsDirectoryService::sMusicDocumentsDirectory)
+ {
+ rv = GetOSXFolderType(kUserDomain, kMusicDocumentsFolderType, getter_AddRefs(localFile));
+ }
+ else if (inAtom == nsDirectoryService::sInternetSitesDirectory)
+ {
+ rv = GetOSXFolderType(kUserDomain, kInternetSitesFolderType, getter_AddRefs(localFile));
+ }
+#elif defined (XP_WIN)
+ else if (inAtom == nsDirectoryService::sSystemDirectory)
+ {
+ rv = GetSpecialSystemDirectory(Win_SystemDirectory, getter_AddRefs(localFile));
+ }
+ else if (inAtom == nsDirectoryService::sWindowsDirectory)
+ {
+ rv = GetSpecialSystemDirectory(Win_WindowsDirectory, getter_AddRefs(localFile));
+ }
+ else if (inAtom == nsDirectoryService::sHomeDirectory)
+ {
+ rv = GetSpecialSystemDirectory(Win_HomeDirectory, getter_AddRefs(localFile));
+ }
+ else if (inAtom == nsDirectoryService::sDesktop)
+ {
+ rv = GetSpecialSystemDirectory(Win_Desktop, getter_AddRefs(localFile));
+ }
+ else if (inAtom == nsDirectoryService::sPrograms)
+ {
+ rv = GetSpecialSystemDirectory(Win_Programs, getter_AddRefs(localFile));
+ }
+ else if (inAtom == nsDirectoryService::sControls)
+ {
+ rv = GetSpecialSystemDirectory(Win_Controls, getter_AddRefs(localFile));
+ }
+ else if (inAtom == nsDirectoryService::sPrinters)
+ {
+ rv = GetSpecialSystemDirectory(Win_Printers, getter_AddRefs(localFile));
+ }
+ else if (inAtom == nsDirectoryService::sPersonal)
+ {
+ rv = GetSpecialSystemDirectory(Win_Personal, getter_AddRefs(localFile));
+ }
+ else if (inAtom == nsDirectoryService::sFavorites)
+ {
+ rv = GetSpecialSystemDirectory(Win_Favorites, getter_AddRefs(localFile));
+ }
+ else if (inAtom == nsDirectoryService::sStartup)
+ {
+ rv = GetSpecialSystemDirectory(Win_Startup, getter_AddRefs(localFile));
+ }
+ else if (inAtom == nsDirectoryService::sRecent)
+ {
+ rv = GetSpecialSystemDirectory(Win_Recent, getter_AddRefs(localFile));
+ }
+ else if (inAtom == nsDirectoryService::sSendto)
+ {
+ rv = GetSpecialSystemDirectory(Win_Sendto, getter_AddRefs(localFile));
+ }
+ else if (inAtom == nsDirectoryService::sBitbucket)
+ {
+ rv = GetSpecialSystemDirectory(Win_Bitbucket, getter_AddRefs(localFile));
+ }
+ else if (inAtom == nsDirectoryService::sStartmenu)
+ {
+ rv = GetSpecialSystemDirectory(Win_Startmenu, getter_AddRefs(localFile));
+ }
+ else if (inAtom == nsDirectoryService::sDesktopdirectory)
+ {
+ rv = GetSpecialSystemDirectory(Win_Desktopdirectory, getter_AddRefs(localFile));
+ }
+ else if (inAtom == nsDirectoryService::sDrives)
+ {
+ rv = GetSpecialSystemDirectory(Win_Drives, getter_AddRefs(localFile));
+ }
+ else if (inAtom == nsDirectoryService::sNetwork)
+ {
+ rv = GetSpecialSystemDirectory(Win_Network, getter_AddRefs(localFile));
+ }
+ else if (inAtom == nsDirectoryService::sNethood)
+ {
+ rv = GetSpecialSystemDirectory(Win_Nethood, getter_AddRefs(localFile));
+ }
+ else if (inAtom == nsDirectoryService::sFonts)
+ {
+ rv = GetSpecialSystemDirectory(Win_Fonts, getter_AddRefs(localFile));
+ }
+ else if (inAtom == nsDirectoryService::sTemplates)
+ {
+ rv = GetSpecialSystemDirectory(Win_Templates, getter_AddRefs(localFile));
+ }
+ else if (inAtom == nsDirectoryService::sCommon_Startmenu)
+ {
+ rv = GetSpecialSystemDirectory(Win_Common_Startmenu, getter_AddRefs(localFile));
+ }
+ else if (inAtom == nsDirectoryService::sCommon_Programs)
+ {
+ rv = GetSpecialSystemDirectory(Win_Common_Programs, getter_AddRefs(localFile));
+ }
+ else if (inAtom == nsDirectoryService::sCommon_Startup)
+ {
+ rv = GetSpecialSystemDirectory(Win_Common_Startup, getter_AddRefs(localFile));
+ }
+ else if (inAtom == nsDirectoryService::sCommon_Desktopdirectory)
+ {
+ rv = GetSpecialSystemDirectory(Win_Common_Desktopdirectory, getter_AddRefs(localFile));
+ }
+ else if (inAtom == nsDirectoryService::sAppdata)
+ {
+ rv = GetSpecialSystemDirectory(Win_Appdata, getter_AddRefs(localFile));
+ }
+ else if (inAtom == nsDirectoryService::sPrinthood)
+ {
+ rv = GetSpecialSystemDirectory(Win_Printhood, getter_AddRefs(localFile));
+ }
+ else if (inAtom == nsDirectoryService::sWinCookiesDirectory)
+ {
+ rv = GetSpecialSystemDirectory(Win_Cookies, getter_AddRefs(localFile));
+ }
+#elif defined (XP_UNIX)
+
+ else if (inAtom == nsDirectoryService::sLocalDirectory)
+ {
+ rv = GetSpecialSystemDirectory(Unix_LocalDirectory, getter_AddRefs(localFile));
+ }
+ else if (inAtom == nsDirectoryService::sLibDirectory)
+ {
+ rv = GetSpecialSystemDirectory(Unix_LibDirectory, getter_AddRefs(localFile));
+ }
+ else if (inAtom == nsDirectoryService::sHomeDirectory)
+ {
+ rv = GetSpecialSystemDirectory(Unix_HomeDirectory, getter_AddRefs(localFile));
+ }
+#elif defined (XP_OS2)
+ else if (inAtom == nsDirectoryService::sSystemDirectory)
+ {
+ rv = GetSpecialSystemDirectory(OS2_SystemDirectory, getter_AddRefs(localFile));
+ }
+ else if (inAtom == nsDirectoryService::sOS2Directory)
+ {
+ rv = GetSpecialSystemDirectory(OS2_OS2Directory, getter_AddRefs(localFile));
+ }
+ else if (inAtom == nsDirectoryService::sHomeDirectory)
+ {
+ rv = GetSpecialSystemDirectory(OS2_HomeDirectory, getter_AddRefs(localFile));
+ }
+ else if (inAtom == nsDirectoryService::sDesktopDirectory)
+ {
+ rv = GetSpecialSystemDirectory(OS2_DesktopDirectory, getter_AddRefs(localFile));
+ }
+#elif defined (XP_BEOS)
+ else if (inAtom == nsDirectoryService::sSettingsDirectory)
+ {
+ rv = GetSpecialSystemDirectory(BeOS_SettingsDirectory, getter_AddRefs(localFile));
+ }
+ else if (inAtom == nsDirectoryService::sHomeDirectory)
+ {
+ rv = GetSpecialSystemDirectory(BeOS_HomeDirectory, getter_AddRefs(localFile));
+ }
+ else if (inAtom == nsDirectoryService::sDesktopDirectory)
+ {
+ rv = GetSpecialSystemDirectory(BeOS_DesktopDirectory, getter_AddRefs(localFile));
+ }
+ else if (inAtom == nsDirectoryService::sSystemDirectory)
+ {
+ rv = GetSpecialSystemDirectory(BeOS_SystemDirectory, getter_AddRefs(localFile));
+ }
+#endif
+
+
+ NS_RELEASE(inAtom);
+
+ if (localFile && NS_SUCCEEDED(rv))
+ return localFile->QueryInterface(NS_GET_IID(nsIFile), (void**)_retval);
+#ifdef DEBUG_dougt
+ printf("Failed to find directory for key: %s\n", prop);
+#endif
+ return rv;
+}
+
+NS_IMETHODIMP
+nsDirectoryService::GetFiles(const char *prop, nsISimpleEnumerator **_retval)
+{
+ NS_ENSURE_ARG_POINTER(_retval);
+ *_retval = nsnull;
+
+ return NS_ERROR_FAILURE;
+}
diff --git a/src/libs/xpcom18a4/xpcom/io/nsDirectoryService.h b/src/libs/xpcom18a4/xpcom/io/nsDirectoryService.h
new file mode 100644
index 00000000..67e6e371
--- /dev/null
+++ b/src/libs/xpcom18a4/xpcom/io/nsDirectoryService.h
@@ -0,0 +1,177 @@
+/* -*- Mode: C++; tab-width: 2; 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):
+ * IBM Corp.
+ *
+ * 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 ***** */
+
+#ifndef nsDirectoryService_h___
+#define nsDirectoryService_h___
+
+#include "nsIDirectoryService.h"
+#include "nsHashtable.h"
+#include "nsILocalFile.h"
+#include "nsISupportsArray.h"
+#include "nsIAtom.h"
+
+#define NS_XPCOM_INIT_CURRENT_PROCESS_DIR "MozBinD" // Can be used to set NS_XPCOM_CURRENT_PROCESS_DIR
+ // CANNOT be used to GET a location
+
+class nsDirectoryService : public nsIDirectoryService,
+ public nsIProperties,
+ public nsIDirectoryServiceProvider2
+{
+ public:
+
+ NS_DEFINE_STATIC_CID_ACCESSOR(NS_DIRECTORY_SERVICE_CID);
+
+ // nsISupports interface
+ NS_DECL_ISUPPORTS
+
+ NS_DECL_NSIPROPERTIES
+
+ NS_DECL_NSIDIRECTORYSERVICE
+
+ NS_DECL_NSIDIRECTORYSERVICEPROVIDER
+
+ NS_DECL_NSIDIRECTORYSERVICEPROVIDER2
+
+ nsDirectoryService();
+
+ static NS_METHOD
+ Create(nsISupports *aOuter, REFNSIID aIID, void **aResult);
+
+private:
+ ~nsDirectoryService();
+
+ nsresult GetCurrentProcessDirectory(nsILocalFile** aFile);
+
+ static nsDirectoryService* mService;
+ static PRBool PR_CALLBACK ReleaseValues(nsHashKey* key, void* data, void* closure);
+ nsSupportsHashtable mHashtable;
+ nsCOMPtr<nsISupportsArray> mProviders;
+
+public:
+ static nsIAtom *sCurrentProcess;
+ static nsIAtom *sComponentRegistry;
+ static nsIAtom *sComponentDirectory;
+ static nsIAtom *sXPTIRegistry;
+ static nsIAtom *sGRE_Directory;
+ static nsIAtom *sGRE_ComponentDirectory;
+ static nsIAtom *sOS_DriveDirectory;
+ static nsIAtom *sOS_TemporaryDirectory;
+ static nsIAtom *sOS_CurrentProcessDirectory;
+ static nsIAtom *sOS_CurrentWorkingDirectory;
+#if defined (XP_MACOSX)
+ static nsIAtom *sDirectory;
+ static nsIAtom *sDesktopDirectory;
+ static nsIAtom *sTrashDirectory;
+ static nsIAtom *sStartupDirectory;
+ static nsIAtom *sShutdownDirectory;
+ static nsIAtom *sAppleMenuDirectory;
+ static nsIAtom *sControlPanelDirectory;
+ static nsIAtom *sExtensionDirectory;
+ static nsIAtom *sFontsDirectory;
+ static nsIAtom *sPreferencesDirectory;
+ static nsIAtom *sDocumentsDirectory;
+ static nsIAtom *sInternetSearchDirectory;
+ static nsIAtom *sUserLibDirectory;
+ static nsIAtom *sHomeDirectory;
+ static nsIAtom *sDefaultDownloadDirectory;
+ static nsIAtom *sUserDesktopDirectory;
+ static nsIAtom *sLocalDesktopDirectory;
+ static nsIAtom *sUserApplicationsDirectory;
+ static nsIAtom *sLocalApplicationsDirectory;
+ static nsIAtom *sUserDocumentsDirectory;
+ static nsIAtom *sLocalDocumentsDirectory;
+ static nsIAtom *sUserInternetPlugInDirectory;
+ static nsIAtom *sLocalInternetPlugInDirectory;
+ static nsIAtom *sUserFrameworksDirectory;
+ static nsIAtom *sLocalFrameworksDirectory;
+ static nsIAtom *sUserPreferencesDirectory;
+ static nsIAtom *sLocalPreferencesDirectory;
+ static nsIAtom *sPictureDocumentsDirectory;
+ static nsIAtom *sMovieDocumentsDirectory;
+ static nsIAtom *sMusicDocumentsDirectory;
+ static nsIAtom *sInternetSitesDirectory;
+#elif defined (XP_WIN)
+ static nsIAtom *sSystemDirectory;
+ static nsIAtom *sWindowsDirectory;
+ static nsIAtom *sHomeDirectory;
+ static nsIAtom *sDesktop;
+ static nsIAtom *sPrograms;
+ static nsIAtom *sControls;
+ static nsIAtom *sPrinters;
+ static nsIAtom *sPersonal;
+ static nsIAtom *sFavorites;
+ static nsIAtom *sStartup;
+ static nsIAtom *sRecent;
+ static nsIAtom *sSendto;
+ static nsIAtom *sBitbucket;
+ static nsIAtom *sStartmenu;
+ static nsIAtom *sDesktopdirectory;
+ static nsIAtom *sDrives;
+ static nsIAtom *sNetwork;
+ static nsIAtom *sNethood;
+ static nsIAtom *sFonts;
+ static nsIAtom *sTemplates;
+ static nsIAtom *sCommon_Startmenu;
+ static nsIAtom *sCommon_Programs;
+ static nsIAtom *sCommon_Startup;
+ static nsIAtom *sCommon_Desktopdirectory;
+ static nsIAtom *sAppdata;
+ static nsIAtom *sPrinthood;
+ static nsIAtom *sWinCookiesDirectory;
+#elif defined (XP_UNIX)
+ static nsIAtom *sLocalDirectory;
+ static nsIAtom *sLibDirectory;
+ static nsIAtom *sHomeDirectory;
+#elif defined (XP_OS2)
+ static nsIAtom *sSystemDirectory;
+ static nsIAtom *sOS2Directory;
+ static nsIAtom *sHomeDirectory;
+ static nsIAtom *sDesktopDirectory;
+#elif defined (XP_BEOS)
+ static nsIAtom *sSettingsDirectory;
+ static nsIAtom *sHomeDirectory;
+ static nsIAtom *sDesktopDirectory;
+ static nsIAtom *sSystemDirectory;
+#endif
+
+
+};
+
+
+#endif
+
diff --git a/src/libs/xpcom18a4/xpcom/io/nsDirectoryServiceDefs.h b/src/libs/xpcom18a4/xpcom/io/nsDirectoryServiceDefs.h
new file mode 100644
index 00000000..6b3c8ba4
--- /dev/null
+++ b/src/libs/xpcom18a4/xpcom/io/nsDirectoryServiceDefs.h
@@ -0,0 +1,197 @@
+/* -*- 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) 2000
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Conrad Carlen conrad@ingress.com
+ *
+ * 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 ***** */
+
+/**
+ * Defines the property names for directories available from
+ * nsIDirectoryService. These dirs are always available even if no
+ * nsIDirectoryServiceProviders have been registered with the service.
+ * Application level keys are defined in nsAppDirectoryServiceDefs.h.
+ *
+ * Keys whose definition ends in "DIR" or "FILE" return a single nsIFile (or
+ * subclass). Keys whose definition ends in "LIST" return an nsISimpleEnumerator
+ * which enumerates a list of file objects.
+ *
+ * Defines listed in this file are FROZEN. This list may grow.
+ */
+
+#ifndef nsDirectoryServiceDefs_h___
+#define nsDirectoryServiceDefs_h___
+
+/* General OS specific locations */
+
+#define NS_OS_HOME_DIR "Home"
+#define NS_OS_TEMP_DIR "TmpD"
+#define NS_OS_CURRENT_WORKING_DIR "CurWorkD"
+
+/* Property returns the directory in which the procces was started from.
+ * On Unix this will be the path in the VBOX_XPCOM_HOME env var and if
+ * unset will be the current working directory.
+ */
+#define NS_OS_CURRENT_PROCESS_DIR "CurProcD"
+
+/* This location is similar to NS_OS_CURRENT_PROCESS_DIR, however,
+ * NS_XPCOM_CURRENT_PROCESS_DIR can be overriden by passing a "bin
+ * directory" to NS_InitXPCOM2().
+ */
+#define NS_XPCOM_CURRENT_PROCESS_DIR "XCurProcD"
+
+/* Property will return the location of the application components
+ * directory. By default, this directory will be contained in the
+ * NS_XPCOM_CURRENT_PROCESS_DIR.
+ */
+#define NS_XPCOM_COMPONENT_DIR "ComsD"
+
+/* Property will return a list of components directories that will
+ * will be registered after the application components directory.
+ */
+#define NS_XPCOM_COMPONENT_DIR_LIST "ComsDL"
+
+/* Property will return the location of the application components
+ * registry file.
+ */
+#define NS_XPCOM_COMPONENT_REGISTRY_FILE "ComRegF"
+
+/* Property will return the location of the application XPTI
+ * registry file.
+ */
+#define NS_XPCOM_XPTI_REGISTRY_FILE "XptiRegF"
+
+/* Property will return the location of the the XPCOM Shared Library.
+ */
+#define NS_XPCOM_LIBRARY_FILE "XpcomLib"
+
+/* Property will return the current location of the the GRE directory.
+ * If no GRE is used, this propery will behave like
+ * NS_XPCOM_CURRENT_PROCESS_DIR.
+ */
+#define NS_GRE_DIR "GreD"
+
+/* Property will return the current location of the the GRE component
+ * directory. If no GRE is used, this propery will behave like
+ * NS_XPCOM_COMPONENT_DIR.
+ */
+#define NS_GRE_COMPONENT_DIR "GreComsD"
+
+
+/* Platform Specific Locations */
+
+#if !defined (XP_UNIX) || defined(XP_MACOSX)
+ #define NS_OS_SYSTEM_DIR "SysD"
+#endif
+
+#if defined (XP_MACOSX)
+ #define NS_MAC_DESKTOP_DIR "Desk"
+ #define NS_MAC_TRASH_DIR "Trsh"
+ #define NS_MAC_STARTUP_DIR "Strt"
+ #define NS_MAC_SHUTDOWN_DIR "Shdwn"
+ #define NS_MAC_APPLE_MENU_DIR "ApplMenu"
+ #define NS_MAC_CONTROL_PANELS_DIR "CntlPnl"
+ #define NS_MAC_EXTENSIONS_DIR "Exts"
+ #define NS_MAC_FONTS_DIR "Fnts"
+ #define NS_MAC_PREFS_DIR "Prfs"
+ #define NS_MAC_DOCUMENTS_DIR "Docs"
+ #define NS_MAC_INTERNET_SEARCH_DIR "ISrch"
+ #define NS_OSX_HOME_DIR "Home"
+ #define NS_MAC_HOME_DIR NS_OSX_HOME_DIR
+ #define NS_MAC_DEFAULT_DOWNLOAD_DIR "DfltDwnld"
+ #define NS_MAC_USER_LIB_DIR "ULibDir" // Only available under OS X
+ #define NS_OSX_DEFAULT_DOWNLOAD_DIR NS_MAC_DEFAULT_DOWNLOAD_DIR
+ #define NS_OSX_USER_DESKTOP_DIR "UsrDsk"
+ #define NS_OSX_LOCAL_DESKTOP_DIR "LocDsk"
+ #define NS_OSX_USER_APPLICATIONS_DIR "UsrApp"
+ #define NS_OSX_LOCAL_APPLICATIONS_DIR "LocApp"
+ #define NS_OSX_USER_DOCUMENTS_DIR "UsrDocs"
+ #define NS_OSX_LOCAL_DOCUMENTS_DIR "LocDocs"
+ #define NS_OSX_USER_INTERNET_PLUGIN_DIR "UsrIntrntPlgn"
+ #define NS_OSX_LOCAL_INTERNET_PLUGIN_DIR "LoclIntrntPlgn"
+ #define NS_OSX_USER_FRAMEWORKS_DIR "UsrFrmwrks"
+ #define NS_OSX_LOCAL_FRAMEWORKS_DIR "LocFrmwrks"
+ #define NS_OSX_USER_PREFERENCES_DIR "UsrPrfs"
+ #define NS_OSX_LOCAL_PREFERENCES_DIR "LocPrfs"
+ #define NS_OSX_PICTURE_DOCUMENTS_DIR "Pct"
+ #define NS_OSX_MOVIE_DOCUMENTS_DIR "Mov"
+ #define NS_OSX_MUSIC_DOCUMENTS_DIR "Music"
+ #define NS_OSX_INTERNET_SITES_DIR "IntrntSts"
+#elif defined (XP_WIN)
+ #define NS_WIN_WINDOWS_DIR "WinD"
+ #define NS_WIN_HOME_DIR "Home"
+ #define NS_WIN_DESKTOP_DIR "DeskV" // virtual folder at the root of the namespace
+ #define NS_WIN_PROGRAMS_DIR "Progs"
+ #define NS_WIN_CONTROLS_DIR "Cntls"
+ #define NS_WIN_PRINTERS_DIR "Prnts"
+ #define NS_WIN_PERSONAL_DIR "Pers"
+ #define NS_WIN_FAVORITES_DIR "Favs"
+ #define NS_WIN_STARTUP_DIR "Strt"
+ #define NS_WIN_RECENT_DIR "Rcnt"
+ #define NS_WIN_SEND_TO_DIR "SndTo"
+ #define NS_WIN_BITBUCKET_DIR "Buckt"
+ #define NS_WIN_STARTMENU_DIR "Strt"
+ #define NS_WIN_DESKTOP_DIRECTORY "DeskP" // file sys dir which physically stores objects on desktop
+ #define NS_WIN_DRIVES_DIR "Drivs"
+ #define NS_WIN_NETWORK_DIR "NetW"
+ #define NS_WIN_NETHOOD_DIR "netH"
+ #define NS_WIN_FONTS_DIR "Fnts"
+ #define NS_WIN_TEMPLATES_DIR "Tmpls"
+ #define NS_WIN_COMMON_STARTMENU_DIR "CmStrt"
+ #define NS_WIN_COMMON_PROGRAMS_DIR "CmPrgs"
+ #define NS_WIN_COMMON_STARTUP_DIR "CmStrt"
+ #define NS_WIN_COMMON_DESKTOP_DIRECTORY "CmDeskP"
+ #define NS_WIN_APPDATA_DIR "AppData"
+ #define NS_WIN_PRINTHOOD "PrntHd"
+ #define NS_WIN_COOKIES_DIR "CookD"
+#elif defined (XP_UNIX)
+ #define NS_UNIX_LOCAL_DIR "Locl"
+ #define NS_UNIX_LIB_DIR "LibD"
+ #define NS_UNIX_HOME_DIR "Home"
+#elif defined (XP_OS2)
+ #define NS_OS2_DIR "OS2Dir"
+ #define NS_OS2_HOME_DIR "Home"
+ #define NS_OS2_DESKTOP_DIR "Desk"
+#elif defined (XP_BEOS)
+ #define NS_BEOS_SETTINGS_DIR "Setngs"
+ #define NS_BEOS_HOME_DIR "Home"
+ #define NS_BEOS_DESKTOP_DIR "Desk"
+#endif
+
+/* Deprecated */
+
+#define NS_OS_DRIVE_DIR "DrvD"
+
+
+
+#endif
diff --git a/src/libs/xpcom18a4/xpcom/io/nsDirectoryServiceUtils.h b/src/libs/xpcom18a4/xpcom/io/nsDirectoryServiceUtils.h
new file mode 100644
index 00000000..f2482c76
--- /dev/null
+++ b/src/libs/xpcom18a4/xpcom/io/nsDirectoryServiceUtils.h
@@ -0,0 +1,61 @@
+/* -*- 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, released
+ * March 31, 1998.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1998-1999
+ * 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 ***** */
+
+#ifndef nsDirectoryServiceUtils_h___
+#define nsDirectoryServiceUtils_h___
+
+#include "nsIServiceManager.h"
+#include "nsIProperties.h"
+#include "nsCOMPtr.h"
+
+#define NS_DIRECTORY_SERVICE_CID {0xf00152d0,0xb40b,0x11d3,{0x8c, 0x9c, 0x00, 0x00, 0x64, 0x65, 0x73, 0x74}}
+
+inline nsresult
+NS_GetSpecialDirectory(const char* specialDirName, nsIFile* *result)
+{
+ nsresult rv;
+ static NS_DEFINE_CID(kDirectoryServiceCID, NS_DIRECTORY_SERVICE_CID);
+ nsCOMPtr<nsIProperties> serv(do_GetService(kDirectoryServiceCID, &rv));
+ if (NS_FAILED(rv))
+ return rv;
+
+ return serv->Get(specialDirName, NS_GET_IID(nsIFile),
+ NS_REINTERPRET_CAST(void**, result));
+}
+
+#endif
diff --git a/src/libs/xpcom18a4/xpcom/io/nsEscape.cpp b/src/libs/xpcom18a4/xpcom/io/nsEscape.cpp
new file mode 100644
index 00000000..758339e4
--- /dev/null
+++ b/src/libs/xpcom18a4/xpcom/io/nsEscape.cpp
@@ -0,0 +1,491 @@
+/* -*- 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 ***** */
+
+// First checked in on 98/12/03 by John R. McMullen, derived from net.h/mkparse.c.
+
+#include "nsEscape.h"
+#include "nsMemory.h"
+#include "nsCRT.h"
+#include "nsReadableUtils.h"
+
+const int netCharType[256] =
+/* Bit 0 xalpha -- the alphas
+** Bit 1 xpalpha -- as xalpha but
+** converts spaces to plus and plus to %2B
+** Bit 3 ... path -- as xalphas but doesn't escape '/'
+*/
+ /* 0 1 2 3 4 5 6 7 8 9 A B C D E F */
+ { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x */
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 1x */
+ 0,0,0,0,0,0,0,0,0,0,7,4,0,7,7,4, /* 2x !"#$%&'()*+,-./ */
+ 7,7,7,7,7,7,7,7,7,7,0,0,0,0,0,0, /* 3x 0123456789:;<=>? */
+ 0,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, /* 4x @ABCDEFGHIJKLMNO */
+ /* bits for '@' changed from 7 to 0 so '@' can be escaped */
+ /* in usernames and passwords in publishing. */
+ 7,7,7,7,7,7,7,7,7,7,7,0,0,0,0,7, /* 5X PQRSTUVWXYZ[\]^_ */
+ 0,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, /* 6x `abcdefghijklmno */
+ 7,7,7,7,7,7,7,7,7,7,7,0,0,0,0,0, /* 7X pqrstuvwxyz{\}~ DEL */
+ 0, };
+
+/* decode % escaped hex codes into character values
+ */
+#define UNHEX(C) \
+ ((C >= '0' && C <= '9') ? C - '0' : \
+ ((C >= 'A' && C <= 'F') ? C - 'A' + 10 : \
+ ((C >= 'a' && C <= 'f') ? C - 'a' + 10 : 0)))
+
+
+#define IS_OK(C) (netCharType[((unsigned int) (C))] & (mask))
+#define HEX_ESCAPE '%'
+
+//----------------------------------------------------------------------------------------
+static char* nsEscapeCount(
+ const char * str,
+ PRInt32 len,
+ nsEscapeMask mask,
+ PRInt32* out_len)
+//----------------------------------------------------------------------------------------
+{
+ if (!str)
+ return 0;
+
+ int i, extra = 0;
+ static const char hexChars[] = "0123456789ABCDEF";
+
+ register const unsigned char* src = (const unsigned char *) str;
+ for (i = 0; i < len; i++)
+ {
+ if (!IS_OK(*src++))
+ extra += 2; /* the escape, plus an extra byte for each nibble */
+ }
+
+ char* result = (char *)nsMemory::Alloc(len + extra + 1);
+ if (!result)
+ return 0;
+
+ register unsigned char* dst = (unsigned char *) result;
+ src = (const unsigned char *) str;
+ if (mask == url_XPAlphas)
+ {
+ for (i = 0; i < len; i++)
+ {
+ unsigned char c = *src++;
+ if (IS_OK(c))
+ *dst++ = c;
+ else if (c == ' ')
+ *dst++ = '+'; /* convert spaces to pluses */
+ else
+ {
+ *dst++ = HEX_ESCAPE;
+ *dst++ = hexChars[c >> 4]; /* high nibble */
+ *dst++ = hexChars[c & 0x0f]; /* low nibble */
+ }
+ }
+ }
+ else
+ {
+ for (i = 0; i < len; i++)
+ {
+ unsigned char c = *src++;
+ if (IS_OK(c))
+ *dst++ = c;
+ else
+ {
+ *dst++ = HEX_ESCAPE;
+ *dst++ = hexChars[c >> 4]; /* high nibble */
+ *dst++ = hexChars[c & 0x0f]; /* low nibble */
+ }
+ }
+ }
+
+ *dst = '\0'; /* tack on eos */
+ if(out_len)
+ *out_len = dst - (unsigned char *) result;
+ return result;
+}
+
+//----------------------------------------------------------------------------------------
+NS_COM char* nsEscape(const char * str, nsEscapeMask mask)
+//----------------------------------------------------------------------------------------
+{
+ if(!str)
+ return NULL;
+ return nsEscapeCount(str, (PRInt32)strlen(str), mask, NULL);
+}
+
+//----------------------------------------------------------------------------------------
+NS_COM char* nsUnescape(char * str)
+//----------------------------------------------------------------------------------------
+{
+ nsUnescapeCount(str);
+ return str;
+}
+
+//----------------------------------------------------------------------------------------
+NS_COM PRInt32 nsUnescapeCount(char * str)
+//----------------------------------------------------------------------------------------
+{
+ register char *src = str;
+ register char *dst = str;
+ static const char hexChars[] = "0123456789ABCDEFabcdef";
+
+ char c1[] = " ";
+ char c2[] = " ";
+ char* const pc1 = c1;
+ char* const pc2 = c2;
+
+ while (*src)
+ {
+ c1[0] = *(src+1);
+ if (*(src+1) == '\0')
+ c2[0] = '\0';
+ else
+ c2[0] = *(src+2);
+
+ if (*src != HEX_ESCAPE || PL_strpbrk(pc1, hexChars) == 0 ||
+ PL_strpbrk(pc2, hexChars) == 0 )
+ *dst++ = *src++;
+ else
+ {
+ src++; /* walk over escape */
+ if (*src)
+ {
+ *dst = UNHEX(*src) << 4;
+ src++;
+ }
+ if (*src)
+ {
+ *dst = (*dst + UNHEX(*src));
+ src++;
+ }
+ dst++;
+ }
+ }
+
+ *dst = 0;
+ return (int)(dst - str);
+
+} /* NET_UnEscapeCnt */
+
+
+NS_COM char *
+nsEscapeHTML(const char * string)
+{
+ /* XXX Hardcoded max entity len. The +1 is for the trailing null. */
+ char *rv = (char *) nsMemory::Alloc(strlen(string) * 6 + 1);
+ char *ptr = rv;
+
+ if(rv)
+ {
+ for(; *string != '\0'; string++)
+ {
+ if(*string == '<')
+ {
+ *ptr++ = '&';
+ *ptr++ = 'l';
+ *ptr++ = 't';
+ *ptr++ = ';';
+ }
+ else if(*string == '>')
+ {
+ *ptr++ = '&';
+ *ptr++ = 'g';
+ *ptr++ = 't';
+ *ptr++ = ';';
+ }
+ else if(*string == '&')
+ {
+ *ptr++ = '&';
+ *ptr++ = 'a';
+ *ptr++ = 'm';
+ *ptr++ = 'p';
+ *ptr++ = ';';
+ }
+ else if (*string == '"')
+ {
+ *ptr++ = '&';
+ *ptr++ = 'q';
+ *ptr++ = 'u';
+ *ptr++ = 'o';
+ *ptr++ = 't';
+ *ptr++ = ';';
+ }
+ else if (*string == '\'')
+ {
+ *ptr++ = '&';
+ *ptr++ = '#';
+ *ptr++ = '3';
+ *ptr++ = '9';
+ *ptr++ = ';';
+ }
+ else
+ {
+ *ptr++ = *string;
+ }
+ }
+ *ptr = '\0';
+ }
+
+ return(rv);
+}
+
+NS_COM PRUnichar *
+nsEscapeHTML2(const PRUnichar *aSourceBuffer, PRInt32 aSourceBufferLen)
+{
+ // if the caller didn't calculate the length
+ if (aSourceBufferLen == -1) {
+ aSourceBufferLen = nsCRT::strlen(aSourceBuffer); // ...then I will
+ }
+
+ /* XXX Hardcoded max entity len. */
+ PRUnichar *resultBuffer = (PRUnichar *)nsMemory::Alloc(aSourceBufferLen *
+ 6 * sizeof(PRUnichar) + sizeof(PRUnichar('\0')));
+ PRUnichar *ptr = resultBuffer;
+
+ if (resultBuffer) {
+ PRInt32 i;
+
+ for(i = 0; i < aSourceBufferLen; i++) {
+ if(aSourceBuffer[i] == '<') {
+ *ptr++ = '&';
+ *ptr++ = 'l';
+ *ptr++ = 't';
+ *ptr++ = ';';
+ } else if(aSourceBuffer[i] == '>') {
+ *ptr++ = '&';
+ *ptr++ = 'g';
+ *ptr++ = 't';
+ *ptr++ = ';';
+ } else if(aSourceBuffer[i] == '&') {
+ *ptr++ = '&';
+ *ptr++ = 'a';
+ *ptr++ = 'm';
+ *ptr++ = 'p';
+ *ptr++ = ';';
+ } else if (aSourceBuffer[i] == '"') {
+ *ptr++ = '&';
+ *ptr++ = 'q';
+ *ptr++ = 'u';
+ *ptr++ = 'o';
+ *ptr++ = 't';
+ *ptr++ = ';';
+ } else if (aSourceBuffer[i] == '\'') {
+ *ptr++ = '&';
+ *ptr++ = '#';
+ *ptr++ = '3';
+ *ptr++ = '9';
+ *ptr++ = ';';
+ } else {
+ *ptr++ = aSourceBuffer[i];
+ }
+ }
+ *ptr = 0;
+ }
+
+ return resultBuffer;
+}
+
+//----------------------------------------------------------------------------------------
+
+const int EscapeChars[256] =
+/* 0 1 2 3 4 5 6 7 8 9 A B C D E F */
+{
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1x */
+ 0,1023, 0, 512,1023, 0,1023,1023,1023,1023,1023,1023,1023,1023, 953, 784, /* 2x !"#$%&'()*+,-./ */
+ 1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1008, 912, 0,1008, 0, 768, /* 3x 0123456789:;<=>? */
+ 1008,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023, /* 4x @ABCDEFGHIJKLMNO */
+ 1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023, 896, 896, 896, 896,1023, /* 5x PQRSTUVWXYZ[\]^_ */
+ 0,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023, /* 6x `abcdefghijklmno */
+ 1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023, 896,1012, 896,1023, 0, /* 7x pqrstuvwxyz{|}~ */
+ 0 /* 8x DEL */
+};
+
+#define NO_NEED_ESC(C) (EscapeChars[((unsigned int) (C))] & (mask))
+
+//----------------------------------------------------------------------------------------
+
+/* returns an escaped string */
+
+/* use the following masks to specify which
+ part of an URL you want to escape:
+
+ esc_Scheme = 1
+ esc_Username = 2
+ esc_Password = 4
+ esc_Host = 8
+ esc_Directory = 16
+ esc_FileBaseName = 32
+ esc_FileExtension = 64
+ esc_Param = 128
+ esc_Query = 256
+ esc_Ref = 512
+*/
+
+/* by default this function will not escape parts of a string
+ that already look escaped, which means it already includes
+ a valid hexcode. This is done to avoid multiple escapes of
+ a string. Use the following mask to force escaping of a
+ string:
+
+ esc_Forced = 1024
+*/
+
+NS_COM PRBool NS_EscapeURL(const char *part,
+ PRInt32 partLen,
+ PRInt16 mask,
+ nsACString &result)
+{
+ if (!part) {
+ NS_NOTREACHED("null pointer");
+ return PR_FALSE;
+ }
+
+ int i = 0;
+ static const char hexChars[] = "0123456789ABCDEF";
+ if (partLen < 0)
+ partLen = strlen(part);
+ PRBool forced = (mask & esc_Forced);
+ PRBool ignoreNonAscii = (mask & esc_OnlyASCII);
+ PRBool ignoreAscii = (mask & esc_OnlyNonASCII);
+ PRBool writing = (mask & esc_AlwaysCopy);
+ PRBool colon = (mask & esc_Colon);
+
+ register const unsigned char* src = (const unsigned char *) part;
+
+ char tempBuffer[100];
+ unsigned int tempBufferPos = 0;
+
+ for (i = 0; i < partLen; i++)
+ {
+ unsigned char c = *src++;
+
+ // if the char has not to be escaped or whatever follows % is
+ // a valid escaped string, just copy the char.
+ //
+ // Also the % will not be escaped until forced
+ // See bugzilla bug 61269 for details why we changed this
+ //
+ // And, we will not escape non-ascii characters if requested.
+ // On special request we will also escape the colon even when
+ // not covered by the matrix.
+ // ignoreAscii is not honored for control characters (C0 and DEL)
+ if ((NO_NEED_ESC(c) || (c == HEX_ESCAPE && !forced)
+ || (c > 0x7f && ignoreNonAscii)
+ || (c > 0x1f && c < 0x7f && ignoreAscii))
+ && !(c == ':' && colon))
+ {
+ if (writing)
+ tempBuffer[tempBufferPos++] = c;
+ }
+ else /* do the escape magic */
+ {
+ if (!writing)
+ {
+ result.Append(part, i);
+ writing = PR_TRUE;
+ }
+ tempBuffer[tempBufferPos++] = HEX_ESCAPE;
+ tempBuffer[tempBufferPos++] = hexChars[c >> 4]; /* high nibble */
+ tempBuffer[tempBufferPos++] = hexChars[c & 0x0f]; /* low nibble */
+ }
+
+ if (tempBufferPos >= sizeof(tempBuffer) - 4)
+ {
+ NS_ASSERTION(writing, "should be writing");
+ tempBuffer[tempBufferPos] = '\0';
+ result += tempBuffer;
+ tempBufferPos = 0;
+ }
+ }
+ if (writing) {
+ tempBuffer[tempBufferPos] = '\0';
+ result += tempBuffer;
+ }
+ return writing;
+}
+
+#define ISHEX(c) memchr(hexChars, c, sizeof(hexChars)-1)
+
+NS_COM PRBool NS_UnescapeURL(const char *str, PRInt32 len, PRInt16 flags, nsACString &result)
+{
+ if (!str) {
+ NS_NOTREACHED("null pointer");
+ return PR_FALSE;
+ }
+
+ if (len < 0)
+ len = strlen(str);
+
+ PRBool ignoreNonAscii = (flags & esc_OnlyASCII);
+ PRBool writing = (flags & esc_AlwaysCopy);
+ PRBool skipControl = (flags & esc_SkipControl);
+
+ static const char hexChars[] = "0123456789ABCDEFabcdef";
+
+ const char *last = str;
+ const char *p = str;
+
+ for (int i=0; i<len; ++i, ++p) {
+ //printf("%c [i=%d of len=%d]\n", *p, i, len);
+ if (*p == HEX_ESCAPE && i < len-2) {
+ unsigned char *p1 = ((unsigned char *) p) + 1;
+ unsigned char *p2 = ((unsigned char *) p) + 2;
+ if (ISHEX(*p1) && ISHEX(*p2) && !(ignoreNonAscii && *p1 >= '8') &&
+ !(skipControl &&
+ (*p1 < '2' || (*p1 == '7' && (*p2 == 'f' || *p2 == 'F'))))) {
+ //printf("- p1=%c p2=%c\n", *p1, *p2);
+ writing = PR_TRUE;
+ if (p > last) {
+ //printf("- p=%p, last=%p\n", p, last);
+ result.Append(last, p - last);
+ last = p;
+ }
+ char u = (UNHEX(*p1) << 4) + UNHEX(*p2);
+ //printf("- u=%c\n", u);
+ result.Append(u);
+ i += 2;
+ p += 2;
+ last += 3;
+ }
+ }
+ }
+ if (writing && last < str + len)
+ result.Append(last, str + len - last);
+
+ return writing;
+}
diff --git a/src/libs/xpcom18a4/xpcom/io/nsEscape.h b/src/libs/xpcom18a4/xpcom/io/nsEscape.h
new file mode 100644
index 00000000..aaf56738
--- /dev/null
+++ b/src/libs/xpcom18a4/xpcom/io/nsEscape.h
@@ -0,0 +1,196 @@
+/* -*- Mode: C++; tab-width: 2; 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 ***** */
+
+/* First checked in on 98/12/03 by John R. McMullen, derived from net.h/mkparse.c. */
+
+#ifndef _ESCAPE_H_
+#define _ESCAPE_H_
+
+#include "prtypes.h"
+#include "nscore.h"
+#include "nsError.h"
+#include "nsString.h"
+
+#ifdef VBOX_WITH_XPCOM_NAMESPACE_CLEANUP
+#define nsEscape VBoxNsxpnsEscape
+#define nsUnescape VBoxNsxpnsUnescape
+#define nsUnescapeCount VBoxNsxpnsUnescapeCount
+#define nsEscapeHTML VBoxNsxpnsEscapeHTML
+#define nsEscapeHTML2 VBoxNsxpnsEscapeHTML2
+#endif /* VBOX_WITH_XPCOM_NAMESPACE_CLEANUP */
+
+/**
+ * Valid mask values for nsEscape
+ */
+typedef enum {
+ url_XAlphas = PR_BIT(0) /**< Normal escape - leave alphas intact, escape the rest */
+, url_XPAlphas = PR_BIT(1) /**< As url_XAlphas, but convert spaces (0x20) to '+' and plus to %2B */
+, url_Path = PR_BIT(2) /**< As url_XAlphas, but don't escape slash ('/') */
+} nsEscapeMask;
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Escape the given string according to mask
+ * @param str The string to escape
+ * @param mask How to escape the string
+ * @return A newly allocated escaped string that must be free'd with
+ * nsCRT::free, or null on failure
+ */
+NS_COM char * nsEscape(const char * str, nsEscapeMask mask);
+
+NS_COM char * nsUnescape(char * str);
+ /* decode % escaped hex codes into character values,
+ * modifies the parameter, returns the same buffer
+ */
+
+NS_COM PRInt32 nsUnescapeCount (char * str);
+ /* decode % escaped hex codes into character values,
+ * modifies the parameter buffer, returns the length of the result
+ * (result may contain \0's).
+ */
+
+NS_COM char *
+nsEscapeHTML(const char * string);
+
+NS_COM PRUnichar *
+nsEscapeHTML2(const PRUnichar *aSourceBuffer,
+ PRInt32 aSourceBufferLen = -1);
+ /*
+ * Escape problem char's for HTML display
+ */
+
+
+#ifdef __cplusplus
+}
+#endif
+
+
+/**
+ * NS_EscapeURL/NS_UnescapeURL constants for |flags| parameter:
+ */
+enum EscapeMask {
+ /** url components **/
+ esc_Scheme = PR_BIT(0),
+ esc_Username = PR_BIT(1),
+ esc_Password = PR_BIT(2),
+ esc_Host = PR_BIT(3),
+ esc_Directory = PR_BIT(4),
+ esc_FileBaseName = PR_BIT(5),
+ esc_FileExtension = PR_BIT(6),
+ esc_FilePath = esc_Directory | esc_FileBaseName | esc_FileExtension,
+ esc_Param = PR_BIT(7),
+ esc_Query = PR_BIT(8),
+ esc_Ref = PR_BIT(9),
+ /** special flags **/
+ esc_Minimal = esc_Scheme | esc_Username | esc_Password | esc_Host | esc_FilePath | esc_Param | esc_Query | esc_Ref,
+ esc_Forced = PR_BIT(10), /* forces escaping of existing escape sequences */
+ esc_OnlyASCII = PR_BIT(11), /* causes non-ascii octets to be skipped */
+ esc_OnlyNonASCII = PR_BIT(12), /* causes _graphic_ ascii octets (0x20-0x7E)
+ * to be skipped when escaping. causes all
+ * ascii octets to be skipped when unescaping */
+ esc_AlwaysCopy = PR_BIT(13), /* copy input to result buf even if escaping is unnecessary */
+ esc_Colon = PR_BIT(14), /* forces escape of colon */
+ esc_SkipControl = PR_BIT(15) /* skips C0 and DEL from unescaping */
+};
+
+/**
+ * NS_EscapeURL
+ *
+ * Escapes invalid char's in an URL segment. Has no side-effect if the URL
+ * segment is already escaped. Otherwise, the escaped URL segment is appended
+ * to |result|.
+ *
+ * @param str url segment string
+ * @param len url segment string length (-1 if unknown)
+ * @param flags url segment type flag
+ * @param result result buffer, untouched if part is already escaped
+ *
+ * @return TRUE if escaping was performed, FALSE otherwise.
+ */
+NS_COM PRBool NS_EscapeURL(const char *str,
+ PRInt32 len,
+ PRInt16 flags,
+ nsACString &result);
+
+/**
+ * Expands URL escape sequences... beware embedded null bytes!
+ *
+ * @param str url string to unescape
+ * @param len length of |str|
+ * @param flags only esc_OnlyNonASCII, esc_SkipControl and esc_AlwaysCopy
+ * are recognized
+ * @param result result buffer, untouched if |str| is already unescaped
+ *
+ * @return TRUE if unescaping was performed, FALSE otherwise.
+ */
+NS_COM PRBool NS_UnescapeURL(const char *str,
+ PRInt32 len,
+ PRInt16 flags,
+ nsACString &result);
+
+/** returns resultant string length **/
+inline PRInt32 NS_UnescapeURL(char *str) { return nsUnescapeCount(str); }
+
+/**
+ * string friendly versions...
+ */
+inline const nsACString &
+NS_EscapeURL(const nsASingleFragmentCString &part, PRInt16 partType, nsACString &result) {
+ const char *temp;
+ if (NS_EscapeURL(part.BeginReading(temp), part.Length(), partType, result))
+ return result;
+ return part;
+}
+inline const nsACString &
+NS_UnescapeURL(const nsASingleFragmentCString &str, PRInt16 flags, nsACString &result) {
+ const char *temp;
+ if (NS_UnescapeURL(str.BeginReading(temp), str.Length(), flags, result))
+ return result;
+ return str;
+}
+// inline unescape
+inline nsAFlatCString &
+NS_UnescapeURL(nsAFlatCString &str)
+{
+ str.SetLength(nsUnescapeCount(str.BeginWriting()));
+ return str;
+}
+
+#endif // _ESCAPE_H_
diff --git a/src/libs/xpcom18a4/xpcom/io/nsFastLoadFile.cpp b/src/libs/xpcom18a4/xpcom/io/nsFastLoadFile.cpp
new file mode 100644
index 00000000..56b09d22
--- /dev/null
+++ b/src/libs/xpcom18a4/xpcom/io/nsFastLoadFile.cpp
@@ -0,0 +1,2581 @@
+/* -*- 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 FastLoad code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2001
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Brendan Eich <brendan@mozilla.org> (original author)
+ *
+ * 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 <string.h>
+#include "prtypes.h"
+#include "nscore.h"
+#include "nsDebug.h"
+#include "nsEnumeratorUtils.h"
+#include "nsMemory.h"
+#include "nsXPIDLString.h"
+#include "nsString.h"
+#include "nsReadableUtils.h"
+
+#include "nsIComponentManager.h"
+#include "nsIFile.h"
+#include "nsILocalFile.h"
+#include "nsISeekableStream.h"
+#include "nsISerializable.h"
+#include "nsIStreamBufferAccess.h"
+
+#include "nsBinaryStream.h"
+#include "nsFastLoadFile.h"
+#include "nsInt64.h"
+
+#ifdef DEBUG_brendan
+# define METERING
+# define DEBUG_MUX
+#endif
+
+#ifdef METERING
+# define METER(x) x
+#else
+# define METER(x) /* nothing */
+#endif
+
+#ifdef DEBUG_MUX
+# include <stdio.h>
+# include <stdarg.h>
+
+static void trace_mux(char mode, const char *format, ...)
+{
+ va_list ap;
+ static FILE *tfp;
+ if (!tfp) {
+ char tfn[16];
+ sprintf(tfn, "/tmp/mux.%ctrace", mode);
+ tfp = fopen(tfn, "w");
+ if (!tfp)
+ return;
+ setvbuf(tfp, NULL, _IOLBF, 0);
+ }
+ va_start(ap, format);
+ vfprintf(tfp, format, ap);
+ va_end(ap);
+}
+
+# define TRACE_MUX(args) trace_mux args
+#else
+# define TRACE_MUX(args) /* nothing */
+#endif
+
+/*
+ * Fletcher's 16-bit checksum, using 32-bit two's-complement arithmetic.
+ */
+#define FOLD_ONES_COMPLEMENT_CARRY(X) ((X) = ((X) & 0xffff) + ((X) >> 16))
+#define ONES_COMPLEMENT_ACCUMULATE(X,Y) (X) += (Y); if ((X) & 0x80000000) \
+ FOLD_ONES_COMPLEMENT_CARRY(X)
+#define FLETCHER_ACCUMULATE(A,B,U) ONES_COMPLEMENT_ACCUMULATE(A, U); \
+ ONES_COMPLEMENT_ACCUMULATE(B, A)
+
+PR_IMPLEMENT(PRUint32)
+NS_AccumulateFastLoadChecksum(PRUint32 *aChecksum,
+ const PRUint8* aBuffer,
+ PRUint32 aLength,
+ PRBool aLastBuffer)
+{
+ PRUint32 C = *aChecksum;
+ PRUint32 A = C & 0xffff;
+ PRUint32 B = C >> 16;
+
+ PRUint16 U = 0;
+ if (aLength >= 4) {
+ PRBool odd = PRWord(aBuffer) & 1;
+ switch (PRWord(aBuffer) & 3) {
+ case 3:
+ U = (aBuffer[0] << 8) | aBuffer[1];
+ FLETCHER_ACCUMULATE(A, B, U);
+ U = aBuffer[2];
+ aBuffer += 3;
+ aLength -= 3;
+ break;
+
+ case 2:
+ U = (aBuffer[0] << 8) | aBuffer[1];
+ FLETCHER_ACCUMULATE(A, B, U);
+ U = 0;
+ aBuffer += 2;
+ aLength -= 2;
+ break;
+
+ case 1:
+ U = *aBuffer++;
+ aLength--;
+ break;
+ }
+
+ PRUint32 W;
+ if (odd) {
+ while (aLength > 3) {
+ W = *NS_REINTERPRET_CAST(const PRUint32*, aBuffer);
+ U <<= 8;
+#ifdef IS_BIG_ENDIAN
+ U |= W >> 24;
+ FLETCHER_ACCUMULATE(A, B, U);
+ U = PRUint16(W >> 8);
+ FLETCHER_ACCUMULATE(A, B, U);
+ U = W & 0xff;
+#else
+ U |= W & 0xff;
+ FLETCHER_ACCUMULATE(A, B, U);
+ U = PRUint16(W >> 8);
+ U = NS_SWAP16(U);
+ FLETCHER_ACCUMULATE(A, B, U);
+ U = W >> 24;
+#endif
+ aBuffer += 4;
+ aLength -= 4;
+ }
+ aBuffer--; // we're odd, we didn't checksum the last byte
+ aLength++;
+ } else {
+ while (aLength > 3) {
+ W = *NS_REINTERPRET_CAST(const PRUint32*, aBuffer);
+#ifdef IS_BIG_ENDIAN
+ U = W >> 16;
+ FLETCHER_ACCUMULATE(A, B, U);
+ U = PRUint16(W);
+ FLETCHER_ACCUMULATE(A, B, U);
+#else
+ U = NS_SWAP16(W);
+ FLETCHER_ACCUMULATE(A, B, U);
+ U = W >> 16;
+ U = NS_SWAP16(W);
+ FLETCHER_ACCUMULATE(A, B, U);
+#endif
+ aBuffer += 4;
+ aLength -= 4;
+ }
+ }
+ }
+
+ if (aLastBuffer) {
+ NS_ASSERTION(aLength <= 4, "aLength botch");
+ switch (aLength) {
+ case 4:
+ U = (aBuffer[0] << 8) | aBuffer[1];
+ FLETCHER_ACCUMULATE(A, B, U);
+ U = (aBuffer[2] << 8) | aBuffer[3];
+ FLETCHER_ACCUMULATE(A, B, U);
+ break;
+
+ case 3:
+ U = (aBuffer[0] << 8) | aBuffer[1];
+ FLETCHER_ACCUMULATE(A, B, U);
+ U = aBuffer[2];
+ FLETCHER_ACCUMULATE(A, B, U);
+ break;
+
+ case 2:
+ U = (aBuffer[0] << 8) | aBuffer[1];
+ FLETCHER_ACCUMULATE(A, B, U);
+ break;
+
+ case 1:
+ U = aBuffer[0];
+ FLETCHER_ACCUMULATE(A, B, U);
+ break;
+ }
+
+ aLength = 0;
+ }
+
+ while (A >> 16)
+ FOLD_ONES_COMPLEMENT_CARRY(A);
+ while (B >> 16)
+ FOLD_ONES_COMPLEMENT_CARRY(B);
+
+ *aChecksum = (B << 16) | A;
+ return aLength;
+}
+
+PR_IMPLEMENT(PRUint32)
+NS_AddFastLoadChecksums(PRUint32 sum1, PRUint32 sum2, PRUint32 sum2ByteCount)
+{
+ PRUint32 A1 = sum1 & 0xffff;
+ PRUint32 B1 = sum1 >> 16;
+
+ PRUint32 A2 = sum2 & 0xffff;
+ PRUint32 B2 = sum2 >> 16;
+
+ PRUint32 A = A1 + A2;
+ while (A >> 16)
+ FOLD_ONES_COMPLEMENT_CARRY(A);
+
+ PRUint32 B = B2;
+ for (PRUint32 n = (sum2ByteCount + 1) / 2; n != 0; n--)
+ ONES_COMPLEMENT_ACCUMULATE(B, B1);
+ while (B >> 16)
+ FOLD_ONES_COMPLEMENT_CARRY(B);
+
+ return (B << 16) | A;
+}
+
+#undef FOLD_ONES_COMPLEMENT_CARRY
+#undef ONES_COMPLEMENT_ACCUMULATE
+#undef FLETCHER_ACCUMULATE
+
+static const char magic[] = MFL_FILE_MAGIC;
+
+// -------------------------- nsFastLoadFileReader --------------------------
+
+nsID nsFastLoadFileReader::nsFastLoadFooter::gDummyID;
+nsFastLoadFileReader::nsObjectMapEntry
+ nsFastLoadFileReader::nsFastLoadFooter::gDummySharpObjectEntry;
+
+NS_IMPL_ISUPPORTS_INHERITED5(nsFastLoadFileReader,
+ nsBinaryInputStream,
+ nsIObjectInputStream,
+ nsIFastLoadFileControl,
+ nsIFastLoadReadControl,
+ nsISeekableStream,
+ nsIFastLoadFileReader)
+
+MOZ_DECL_CTOR_COUNTER(nsFastLoadFileReader)
+
+nsresult
+nsFastLoadFileReader::ReadHeader(nsFastLoadHeader *aHeader)
+{
+ nsresult rv;
+ PRUint32 bytesRead;
+
+ rv = Read(NS_REINTERPRET_CAST(char*, aHeader), sizeof *aHeader, &bytesRead);
+ if (NS_FAILED(rv))
+ return rv;
+
+ if (bytesRead != sizeof *aHeader ||
+ memcmp(aHeader->mMagic, magic, MFL_FILE_MAGIC_SIZE)) {
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ aHeader->mChecksum = NS_SWAP32(aHeader->mChecksum);
+ aHeader->mVersion = NS_SWAP32(aHeader->mVersion);
+ aHeader->mFooterOffset = NS_SWAP32(aHeader->mFooterOffset);
+ aHeader->mFileSize = NS_SWAP32(aHeader->mFileSize);
+
+ return NS_OK;
+}
+
+// nsIFastLoadFileControl methods:
+
+NS_IMETHODIMP
+nsFastLoadFileReader::GetChecksum(PRUint32 *aChecksum)
+{
+ *aChecksum = mHeader.mChecksum;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsFastLoadFileReader::SetChecksum(PRUint32 aChecksum)
+{
+ mHeader.mChecksum = aChecksum;
+ return NS_OK;
+}
+
+struct nsStringMapEntry : public PLDHashEntryHdr {
+ const char* mString; // key, must come first
+ nsISupports* mURI; // for SelectMuxedDocument return value
+};
+
+struct nsDocumentMapEntry : public nsStringMapEntry {
+ PRUint32 mInitialSegmentOffset; // offset of URI's first segment in file
+};
+
+struct nsDocumentMapReadEntry : public nsDocumentMapEntry {
+ PRUint32 mNextSegmentOffset; // offset of URI's next segment to read
+ PRUint32 mBytesLeft : 31, // bytes remaining in current segment
+ mNeedToSeek : 1; // flag to defer Seek from Select to
+ // Read, in case there is no Read before
+ // another entry is Selected (to improve
+ // input stream buffer utilization)
+ PRInt64 mSaveOffset; // in case demux schedule differs from
+ // mux schedule
+};
+
+PR_STATIC_CALLBACK(void)
+strmap_ClearEntry(PLDHashTable *aTable, PLDHashEntryHdr *aHdr)
+{
+ nsStringMapEntry* entry = NS_STATIC_CAST(nsStringMapEntry*, aHdr);
+
+ if (entry->mString)
+ nsMemory::Free((void*) entry->mString);
+ NS_IF_RELEASE(entry->mURI);
+ PL_DHashClearEntryStub(aTable, aHdr);
+}
+
+static const PLDHashTableOps strmap_DHashTableOps = {
+ PL_DHashAllocTable,
+ PL_DHashFreeTable,
+ PL_DHashGetKeyStub,
+ PL_DHashStringKey,
+ PL_DHashMatchStringKey,
+ PL_DHashMoveEntryStub,
+ strmap_ClearEntry,
+ PL_DHashFinalizeStub,
+ NULL
+};
+
+// An nsObjectMapEntry holds a strong reference to an XPCOM object, unless the
+// mObject member, when cast to NSFastLoadOID, has its MFL_OBJECT_DEF_TAG bit
+// set. NB: we rely on the fact that an nsISupports* is never an odd pointer.
+struct nsObjectMapEntry : public PLDHashEntryHdr {
+ nsISupports* mObject; // key, must come first
+};
+
+// Fast mapping from URI object pointer back to spec-indexed document info.
+struct nsURIMapReadEntry : public nsObjectMapEntry {
+ nsDocumentMapReadEntry* mDocMapEntry;
+};
+
+PR_STATIC_CALLBACK(void)
+objmap_ClearEntry(PLDHashTable *aTable, PLDHashEntryHdr *aHdr)
+{
+ nsObjectMapEntry* entry = NS_STATIC_CAST(nsObjectMapEntry*, aHdr);
+
+ // Ignore tagged object ids stored as object pointer keys (the updater
+ // code does this).
+ if ((NS_PTR_TO_INT32(entry->mObject) & MFL_OBJECT_DEF_TAG) == 0)
+ NS_IF_RELEASE(entry->mObject);
+ PL_DHashClearEntryStub(aTable, aHdr);
+}
+
+static const PLDHashTableOps objmap_DHashTableOps = {
+ PL_DHashAllocTable,
+ PL_DHashFreeTable,
+ PL_DHashGetKeyStub,
+ PL_DHashVoidPtrKeyStub,
+ PL_DHashMatchEntryStub,
+ PL_DHashMoveEntryStub,
+ objmap_ClearEntry,
+ PL_DHashFinalizeStub,
+ NULL
+};
+
+NS_IMETHODIMP
+nsFastLoadFileReader::HasMuxedDocument(const char* aURISpec, PRBool *aResult)
+{
+ nsDocumentMapReadEntry* docMapEntry =
+ NS_STATIC_CAST(nsDocumentMapReadEntry*,
+ PL_DHashTableOperate(&mFooter.mDocumentMap, aURISpec,
+ PL_DHASH_LOOKUP));
+
+ *aResult = PL_DHASH_ENTRY_IS_BUSY(docMapEntry);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsFastLoadFileReader::StartMuxedDocument(nsISupports* aURI, const char* aURISpec)
+{
+ nsDocumentMapReadEntry* docMapEntry =
+ NS_STATIC_CAST(nsDocumentMapReadEntry*,
+ PL_DHashTableOperate(&mFooter.mDocumentMap, aURISpec,
+ PL_DHASH_LOOKUP));
+
+ // If the spec isn't in the map, return NS_ERROR_NOT_AVAILABLE so the
+ // FastLoad service can try for a file update.
+ if (PL_DHASH_ENTRY_IS_FREE(docMapEntry))
+ return NS_ERROR_NOT_AVAILABLE;
+
+ nsCOMPtr<nsISupports> key(do_QueryInterface(aURI));
+ nsURIMapReadEntry* uriMapEntry =
+ NS_STATIC_CAST(nsURIMapReadEntry*,
+ PL_DHashTableOperate(&mFooter.mURIMap, key,
+ PL_DHASH_ADD));
+ if (!uriMapEntry)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ NS_ASSERTION(uriMapEntry->mDocMapEntry == nsnull,
+ "URI mapped to two different specs?");
+ if (uriMapEntry->mDocMapEntry)
+ return NS_ERROR_UNEXPECTED;
+
+ docMapEntry->mURI = aURI;
+ NS_ADDREF(docMapEntry->mURI);
+ uriMapEntry->mObject = key;
+ NS_ADDREF(uriMapEntry->mObject);
+ uriMapEntry->mDocMapEntry = docMapEntry;
+ TRACE_MUX(('r', "start %p (%p) %s\n", aURI, key.get(), aURISpec));
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsFastLoadFileReader::SelectMuxedDocument(nsISupports* aURI,
+ nsISupports** aResult)
+{
+ nsresult rv;
+
+ // Find the given URI's entry and select it for more reading.
+ nsCOMPtr<nsISupports> key(do_QueryInterface(aURI));
+ nsURIMapReadEntry* uriMapEntry =
+ NS_STATIC_CAST(nsURIMapReadEntry*,
+ PL_DHashTableOperate(&mFooter.mURIMap, key,
+ PL_DHASH_LOOKUP));
+
+ // If the URI isn't in the map, return NS_ERROR_NOT_AVAILABLE so the
+ // FastLoad service can try selecting the file updater.
+ if (PL_DHASH_ENTRY_IS_FREE(uriMapEntry))
+ return NS_ERROR_NOT_AVAILABLE;
+
+ // If we're interrupting another document's segment, save its offset so
+ // we can seek back when it's reselected. If prevDocMapEntry->mNeedToSeek
+ // is set, that means the stream is not positioned for prevDocMapEntry, to
+ // avoid buffer thrashing. See below in this function for more.
+ nsDocumentMapReadEntry* prevDocMapEntry = mCurrentDocumentMapEntry;
+ if (prevDocMapEntry &&
+ prevDocMapEntry->mBytesLeft &&
+ !prevDocMapEntry->mNeedToSeek) {
+ rv = Tell(&prevDocMapEntry->mSaveOffset);
+ if (NS_FAILED(rv))
+ return rv;
+ }
+
+ // It turns out we get a fair amount of redundant select calls, thanks to
+ // non-blocking hunks of data from the parser that are devoid of scripts.
+ // As more data gets FastLoaded, the number of these useless selects will
+ // decline.
+ nsDocumentMapReadEntry* docMapEntry = uriMapEntry->mDocMapEntry;
+ if (docMapEntry == prevDocMapEntry) {
+ TRACE_MUX(('r', "select prev %s same as current!\n",
+ docMapEntry->mString));
+ }
+
+ // Invariant: docMapEntry->mBytesLeft implies docMapEntry->mSaveOffset has
+ // been set non-zero by the Tell call above.
+ else if (docMapEntry->mBytesLeft) {
+ NS_ASSERTION(docMapEntry->mSaveOffset != 0,
+ "reselecting from multiplex at unsaved offset?");
+
+ // Defer Seek till Read, in case of "ping-pong" Selects without any
+ // intervening Reads, to avoid dumping the underlying mInputStream's
+ // input buffer for cases where alternate "pongs" fall in the same
+ // buffer.
+ docMapEntry->mNeedToSeek = PR_TRUE;
+ }
+
+ *aResult = prevDocMapEntry ? prevDocMapEntry->mURI : nsnull;
+ NS_IF_ADDREF(*aResult);
+
+ mCurrentDocumentMapEntry = docMapEntry;
+#ifdef DEBUG_MUX
+ PRInt64 currentSegmentOffset;
+ Tell(&currentSegmentOffset);
+ trace_mux('r', "select %p (%p) offset %ld\n",
+ aURI, key.get(), (long) currentSegmentOffset);
+#endif
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsFastLoadFileReader::EndMuxedDocument(nsISupports* aURI)
+{
+ nsCOMPtr<nsISupports> key(do_QueryInterface(aURI));
+ nsURIMapReadEntry* uriMapEntry =
+ NS_STATIC_CAST(nsURIMapReadEntry*,
+ PL_DHashTableOperate(&mFooter.mURIMap, key,
+ PL_DHASH_LOOKUP));
+
+ // If the URI isn't in the map, return NS_ERROR_NOT_AVAILABLE so the
+ // FastLoad service can try to end a select on its file updater.
+ if (PL_DHASH_ENTRY_IS_FREE(uriMapEntry))
+ return NS_ERROR_NOT_AVAILABLE;
+
+ // Drop our ref to the URI object that was passed to StartMuxedDocument,
+ // we no longer need it, and we do not want to extend its lifetime.
+ if (uriMapEntry->mDocMapEntry)
+ NS_RELEASE(uriMapEntry->mDocMapEntry->mURI);
+
+ // Shrink the table if half the entries are removed sentinels.
+ PRUint32 size = PL_DHASH_TABLE_SIZE(&mFooter.mURIMap);
+ if (mFooter.mURIMap.removedCount >= (size >> 2))
+ PL_DHashTableOperate(&mFooter.mURIMap, key, PL_DHASH_REMOVE);
+ else
+ PL_DHashTableRawRemove(&mFooter.mURIMap, uriMapEntry);
+
+ TRACE_MUX(('r', "end %p (%p)\n", aURI, key.get()));
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsFastLoadFileReader::Read(char* aBuffer, PRUint32 aCount, PRUint32 *aBytesRead)
+{
+ nsresult rv;
+
+ nsDocumentMapReadEntry* entry = mCurrentDocumentMapEntry;
+ if (entry) {
+ // Don't call our Seek wrapper, as it clears mCurrentDocumentMapEntry.
+ nsCOMPtr<nsISeekableStream> seekable(do_QueryInterface(mInputStream));
+ if (entry->mNeedToSeek) {
+ rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET,
+ entry->mSaveOffset);
+ if (NS_FAILED(rv))
+ return rv;
+
+ entry->mNeedToSeek = PR_FALSE;
+ }
+
+ // Loop to handle empty segments, which may be generated by the
+ // writer, given Start A; Start B; Select A; Select B; write B data;
+ // multiplexing schedules, which do tend to occur given non-blocking
+ // i/o with LIFO scheduling. XXXbe investigate LIFO issues
+ while (entry->mBytesLeft == 0) {
+ // Check for unexpected end of multiplexed stream.
+ NS_ASSERTION(entry->mNextSegmentOffset != 0,
+ "document demuxed from FastLoad file more than once?");
+ if (entry->mNextSegmentOffset == 0)
+ return NS_ERROR_UNEXPECTED;
+
+ rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET,
+ entry->mNextSegmentOffset);
+ if (NS_FAILED(rv))
+ return rv;
+
+ // Clear mCurrentDocumentMapEntry temporarily to avoid recursion.
+ mCurrentDocumentMapEntry = nsnull;
+
+ rv = Read32(&entry->mNextSegmentOffset);
+ if (NS_SUCCEEDED(rv)) {
+ PRUint32 bytesLeft = 0;
+ rv = Read32(&bytesLeft);
+ entry->mBytesLeft = bytesLeft;
+ }
+
+ mCurrentDocumentMapEntry = entry;
+ if (NS_FAILED(rv))
+ return rv;
+
+ NS_ASSERTION(entry->mBytesLeft >= 8, "demux segment length botch!");
+ entry->mBytesLeft -= 8;
+ }
+ }
+
+ rv = mInputStream->Read(aBuffer, aCount, aBytesRead);
+
+ if (NS_SUCCEEDED(rv) && entry) {
+ NS_ASSERTION(entry->mBytesLeft >= *aBytesRead, "demux Read underflow!");
+ entry->mBytesLeft -= *aBytesRead;
+
+#ifdef NS_DEBUG
+ // Invariant: !entry->mBytesLeft implies entry->mSaveOffset == 0.
+ if (entry->mBytesLeft == 0)
+ entry->mSaveOffset = 0;
+#endif
+ }
+ return rv;
+}
+
+NS_IMETHODIMP
+nsFastLoadFileReader::ReadSegments(nsWriteSegmentFun aWriter, void* aClosure,
+ PRUint32 aCount, PRUint32 *aResult)
+{
+ nsDocumentMapReadEntry* entry = mCurrentDocumentMapEntry;
+
+ NS_ASSERTION(!entry || (!entry->mNeedToSeek && entry->mBytesLeft != 0),
+ "ReadSegments called from above nsFastLoadFileReader layer?!");
+
+ nsresult rv = nsBinaryInputStream::ReadSegments(aWriter, aClosure, aCount,
+ aResult);
+ if (NS_SUCCEEDED(rv) && entry) {
+ NS_ASSERTION(entry->mBytesLeft >= *aResult,
+ "demux ReadSegments underflow!");
+ entry->mBytesLeft -= *aResult;
+
+#ifdef NS_DEBUG
+ // Invariant: !entry->mBytesLeft implies entry->mSaveOffset == 0.
+ if (entry->mBytesLeft == 0)
+ entry->mSaveOffset = 0;
+#endif
+ }
+ return rv;
+}
+
+/**
+ * XXX tuneme
+ */
+#define MFL_CHECKSUM_BUFSIZE 8192
+
+NS_IMETHODIMP
+nsFastLoadFileReader::ComputeChecksum(PRUint32 *aResult)
+{
+ nsCOMPtr<nsIInputStream> stream = mInputStream;
+
+ nsCOMPtr<nsISeekableStream> seekable(do_QueryInterface(stream));
+ PRInt64 saveOffset;
+ nsresult rv = seekable->Tell(&saveOffset);
+ if (NS_FAILED(rv))
+ return rv;
+
+ nsCOMPtr<nsIStreamBufferAccess> bufferAccess(do_QueryInterface(stream));
+ if (bufferAccess) {
+ rv = bufferAccess->GetUnbufferedStream(getter_AddRefs(stream));
+ if (NS_FAILED(rv))
+ return rv;
+
+ seekable = do_QueryInterface(stream);
+ if (!seekable)
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET, 0);
+ if (NS_FAILED(rv))
+ return rv;
+
+ char buf[MFL_CHECKSUM_BUFSIZE];
+ PRUint32 len, rem;
+
+ rem = offsetof(nsFastLoadHeader, mChecksum);
+ rv = stream->Read(buf, rem, &len);
+ if (NS_FAILED(rv))
+ return rv;
+ if (len != rem)
+ return NS_ERROR_UNEXPECTED;
+
+ rv = seekable->Seek(nsISeekableStream::NS_SEEK_CUR, 4);
+ if (NS_FAILED(rv))
+ return rv;
+ memset(buf + rem, 0, 4);
+ rem += 4;
+
+ PRUint32 checksum = 0;
+ while (NS_SUCCEEDED(rv = stream->Read(buf + rem, sizeof buf - rem, &len)) &&
+ len) {
+ len += rem;
+ rem = NS_AccumulateFastLoadChecksum(&checksum,
+ NS_REINTERPRET_CAST(PRUint8*, buf),
+ len,
+ PR_FALSE);
+ if (rem)
+ memcpy(buf, buf + len - rem, rem);
+ }
+ if (NS_FAILED(rv))
+ return rv;
+
+ if (rem) {
+ NS_AccumulateFastLoadChecksum(&checksum,
+ NS_REINTERPRET_CAST(PRUint8*, buf),
+ rem,
+ PR_TRUE);
+ }
+
+ rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET, saveOffset);
+ if (NS_FAILED(rv))
+ return rv;
+
+ *aResult = checksum;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsFastLoadFileReader::GetDependencies(nsISimpleEnumerator* *aDependencies)
+{
+ return NS_NewArrayEnumerator(aDependencies, mFooter.mDependencies);
+}
+
+nsresult
+nsFastLoadFileReader::ReadFooter(nsFastLoadFooter *aFooter)
+{
+ nsresult rv;
+
+ rv = ReadFooterPrefix(aFooter);
+ if (NS_FAILED(rv))
+ return rv;
+
+ aFooter->mIDMap = new nsID[aFooter->mNumIDs];
+ if (!aFooter->mIDMap)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ PRUint32 i, n;
+ for (i = 0, n = aFooter->mNumIDs; i < n; i++) {
+ rv = ReadSlowID(&aFooter->mIDMap[i]);
+ if (NS_FAILED(rv))
+ return rv;
+ }
+
+ aFooter->mObjectMap = new nsObjectMapEntry[aFooter->mNumSharpObjects];
+ if (!aFooter->mObjectMap)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ for (i = 0, n = aFooter->mNumSharpObjects; i < n; i++) {
+ nsObjectMapEntry* entry = &aFooter->mObjectMap[i];
+
+ rv = ReadSharpObjectInfo(entry);
+ if (NS_FAILED(rv))
+ return rv;
+
+ entry->mReadObject = nsnull;
+ entry->mSkipOffset = 0;
+ entry->mSaveStrongRefCnt = entry->mStrongRefCnt;
+ entry->mSaveWeakRefCnt = entry->mWeakRefCnt;
+ }
+
+ if (!PL_DHashTableInit(&aFooter->mDocumentMap, &strmap_DHashTableOps,
+ (void *)this, sizeof(nsDocumentMapReadEntry),
+ aFooter->mNumMuxedDocuments)) {
+ aFooter->mDocumentMap.ops = nsnull;
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ if (!PL_DHashTableInit(&aFooter->mURIMap, &objmap_DHashTableOps,
+ (void *)this, sizeof(nsURIMapReadEntry),
+ aFooter->mNumMuxedDocuments)) {
+ aFooter->mURIMap.ops = nsnull;
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ for (i = 0, n = aFooter->mNumMuxedDocuments; i < n; i++) {
+ nsFastLoadMuxedDocumentInfo info;
+
+ rv = ReadMuxedDocumentInfo(&info);
+ if (NS_FAILED(rv))
+ return rv;
+
+ nsDocumentMapReadEntry* entry =
+ NS_STATIC_CAST(nsDocumentMapReadEntry*,
+ PL_DHashTableOperate(&aFooter->mDocumentMap,
+ info.mURISpec,
+ PL_DHASH_ADD));
+ if (!entry) {
+ nsMemory::Free((void*) info.mURISpec);
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ NS_ASSERTION(!entry->mString, "duplicate URISpec in MuxedDocumentMap");
+ entry->mString = info.mURISpec;
+ entry->mURI = nsnull;
+ entry->mInitialSegmentOffset = info.mInitialSegmentOffset;
+ entry->mNextSegmentOffset = info.mInitialSegmentOffset;
+ entry->mBytesLeft = 0;
+ entry->mNeedToSeek = PR_FALSE;
+ entry->mSaveOffset = 0;
+ }
+
+ nsCOMPtr<nsISupportsArray> readDeps;
+ rv = NS_NewISupportsArray(getter_AddRefs(readDeps));
+ if (NS_FAILED(rv))
+ return rv;
+
+ nsCAutoString filename;
+ for (i = 0, n = aFooter->mNumDependencies; i < n; i++) {
+ rv = ReadCString(filename);
+ if (NS_FAILED(rv))
+ return rv;
+
+ PRInt64 fastLoadMtime;
+ rv = Read64(NS_REINTERPRET_CAST(PRUint64*, &fastLoadMtime));
+ if (NS_FAILED(rv))
+ return rv;
+
+ nsCOMPtr<nsILocalFile> file;
+ rv = NS_NewNativeLocalFile(filename, PR_TRUE, getter_AddRefs(file));
+ if (NS_FAILED(rv))
+ return rv;
+
+ PRInt64 currentMtime;
+ rv = file->GetLastModifiedTime(&currentMtime);
+ if (NS_FAILED(rv))
+ return rv;
+
+ if (LL_NE(fastLoadMtime, currentMtime)) {
+#ifdef DEBUG
+ nsCAutoString path;
+ file->GetNativePath(path);
+ printf("%s mtime changed, invalidating FastLoad file\n",
+ path.get());
+#endif
+ return NS_ERROR_FAILURE;
+ }
+
+ rv = readDeps->AppendElement(file);
+ if (NS_FAILED(rv))
+ return rv;
+ }
+
+ aFooter->mDependencies = readDeps;
+ return NS_OK;
+}
+
+nsresult
+nsFastLoadFileReader::ReadFooterPrefix(nsFastLoadFooterPrefix *aFooterPrefix)
+{
+ nsresult rv;
+
+ rv = Read32(&aFooterPrefix->mNumIDs);
+ if (NS_FAILED(rv))
+ return rv;
+
+ rv = Read32(&aFooterPrefix->mNumSharpObjects);
+ if (NS_FAILED(rv))
+ return rv;
+
+ rv = Read32(&aFooterPrefix->mNumMuxedDocuments);
+ if (NS_FAILED(rv))
+ return rv;
+
+ rv = Read32(&aFooterPrefix->mNumDependencies);
+ if (NS_FAILED(rv))
+ return rv;
+
+ return NS_OK;
+}
+
+nsresult
+nsFastLoadFileReader::ReadSlowID(nsID *aID)
+{
+ nsresult rv;
+
+ rv = Read32(&aID->m0);
+ if (NS_FAILED(rv))
+ return rv;
+
+ rv = Read16(&aID->m1);
+ if (NS_FAILED(rv))
+ return rv;
+
+ rv = Read16(&aID->m2);
+ if (NS_FAILED(rv))
+ return rv;
+
+ PRUint32 bytesRead;
+ rv = Read(NS_REINTERPRET_CAST(char*, aID->m3), sizeof aID->m3, &bytesRead);
+ if (NS_FAILED(rv))
+ return rv;
+
+ if (bytesRead != sizeof aID->m3)
+ return NS_ERROR_FAILURE;
+ return NS_OK;
+}
+
+nsresult
+nsFastLoadFileReader::ReadFastID(NSFastLoadID *aID)
+{
+ nsresult rv = Read32(aID);
+ if (NS_SUCCEEDED(rv))
+ *aID ^= MFL_ID_XOR_KEY;
+ return rv;
+}
+
+nsresult
+nsFastLoadFileReader::ReadSharpObjectInfo(nsFastLoadSharpObjectInfo *aInfo)
+{
+ nsresult rv;
+
+ rv = Read32(&aInfo->mCIDOffset);
+ if (NS_FAILED(rv))
+ return rv;
+
+ NS_ASSERTION(aInfo->mCIDOffset != 0,
+ "fastload reader: mCIDOffset cannot be zero!");
+
+ rv = Read16(&aInfo->mStrongRefCnt);
+ if (NS_FAILED(rv))
+ return rv;
+
+ rv = Read16(&aInfo->mWeakRefCnt);
+ if (NS_FAILED(rv))
+ return rv;
+
+ return NS_OK;
+}
+
+nsresult
+nsFastLoadFileReader::ReadMuxedDocumentInfo(nsFastLoadMuxedDocumentInfo *aInfo)
+{
+ nsresult rv;
+
+ nsCAutoString spec;
+ rv = ReadCString(spec);
+ if (NS_FAILED(rv))
+ return rv;
+
+ rv = Read32(&aInfo->mInitialSegmentOffset);
+ if (NS_FAILED(rv))
+ return rv;
+
+ aInfo->mURISpec = ToNewCString(spec);
+ return NS_OK;
+}
+
+nsresult
+nsFastLoadFileReader::Open()
+{
+ nsCOMPtr<nsISeekableStream> seekable(do_QueryInterface(mInputStream));
+ if (!seekable)
+ return NS_ERROR_UNEXPECTED;
+
+ nsresult rv;
+
+ // Don't bother buffering the header, as we immediately seek to EOF.
+ nsCOMPtr<nsIStreamBufferAccess>
+ bufferAccess(do_QueryInterface(mInputStream));
+ if (bufferAccess)
+ bufferAccess->DisableBuffering();
+
+ rv = ReadHeader(&mHeader);
+
+ if (bufferAccess)
+ bufferAccess->EnableBuffering();
+ if (NS_FAILED(rv))
+ return rv;
+
+ if (mHeader.mVersion != MFL_FILE_VERSION)
+ return NS_ERROR_UNEXPECTED;
+ if (mHeader.mFooterOffset == 0)
+ return NS_ERROR_UNEXPECTED;
+
+ rv = seekable->Seek(nsISeekableStream::NS_SEEK_END, 0);
+ if (NS_FAILED(rv))
+ return rv;
+
+ PRInt64 fileSize;
+ rv = seekable->Tell(&fileSize);
+ if (NS_FAILED(rv))
+ return rv;
+
+ nsInt64 fileSize64 = fileSize;
+ const nsInt64 maxUint32 = PR_UINT32_MAX;
+ NS_ASSERTION(fileSize64 <= maxUint32, "fileSize must fit in 32 bits");
+ if ((PRUint32) fileSize64 != mHeader.mFileSize)
+ return NS_ERROR_UNEXPECTED;
+
+ rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET,
+ PRInt32(mHeader.mFooterOffset));
+ if (NS_FAILED(rv))
+ return rv;
+
+ rv = ReadFooter(&mFooter);
+ if (NS_FAILED(rv))
+ return rv;
+
+ return seekable->Seek(nsISeekableStream::NS_SEEK_SET,
+ sizeof(nsFastLoadHeader));
+}
+
+NS_IMETHODIMP
+nsFastLoadFileReader::Close()
+{
+ // Give up our strong "keepalive" references, in case not all objects that
+ // were deserialized were fully re-connected.
+ //
+ // This happens for sure when an nsFastLoadFileUpdater is created and wraps
+ // an nsFastLoadFileReader whose data was already deserialized by an earlier
+ // FastLoad episode. The reader is useful in the second such episode during
+ // a session not so much for reading objects as for its footer information,
+ // which primes the updater's tables so that after the update completes, the
+ // FastLoad file has a superset footer.
+
+ for (PRUint32 i = 0, n = mFooter.mNumSharpObjects; i < n; i++) {
+ nsObjectMapEntry* entry = &mFooter.mObjectMap[i];
+ entry->mReadObject = nsnull;
+ }
+
+ return mInputStream->Close();
+}
+
+nsresult
+nsFastLoadFileReader::DeserializeObject(nsISupports* *aObject)
+{
+ nsresult rv;
+ NSFastLoadID fastCID;
+
+ rv = ReadFastID(&fastCID);
+ if (NS_FAILED(rv))
+ return rv;
+
+ const nsID& slowCID = mFooter.GetID(fastCID);
+ nsCOMPtr<nsISupports> object(do_CreateInstance(slowCID, &rv));
+ if (NS_FAILED(rv))
+ return rv;
+
+ nsCOMPtr<nsISerializable> serializable(do_QueryInterface(object));
+ if (!serializable)
+ return NS_ERROR_FAILURE;
+
+ rv = serializable->Read(this);
+ if (NS_FAILED(rv))
+ return rv;
+
+ *aObject = object;
+ NS_ADDREF(*aObject);
+ return NS_OK;
+}
+
+nsresult
+nsFastLoadFileReader::ReadObject(PRBool aIsStrongRef, nsISupports* *aObject)
+{
+ nsresult rv;
+ NSFastLoadOID oid;
+
+ rv = Read32(&oid);
+ if (NS_FAILED(rv))
+ return rv;
+ oid ^= MFL_OID_XOR_KEY;
+
+ nsCOMPtr<nsISupports> object;
+
+ if (oid == MFL_DULL_OBJECT_OID) {
+ // A very dull object, defined at point of single (strong) reference.
+ NS_ASSERTION(aIsStrongRef, "dull object read via weak ref!");
+
+ rv = DeserializeObject(getter_AddRefs(object));
+ if (NS_FAILED(rv))
+ return rv;
+ } else {
+ NS_ASSERTION((oid & MFL_WEAK_REF_TAG) ==
+ (aIsStrongRef ? 0 : MFL_WEAK_REF_TAG),
+ "strong vs. weak ref deserialization mismatch!");
+
+ nsObjectMapEntry* entry = &mFooter.GetSharpObjectEntry(oid);
+
+ // Check whether we've already deserialized the object for this OID.
+ object = entry->mReadObject;
+ if (!object) {
+ nsCOMPtr<nsISeekableStream> seekable(do_QueryInterface(mInputStream));
+ PRInt64 saveOffset;
+ nsDocumentMapReadEntry* saveDocMapEntry = nsnull;
+
+ rv = seekable->Tell(&saveOffset);
+ if (NS_FAILED(rv))
+ return rv;
+
+ PRUint32 saveOffset32 = saveOffset;
+ if (entry->mCIDOffset != saveOffset32) {
+ // We skipped deserialization of this object from its position
+ // earlier in the input stream, presumably due to the reference
+ // there being an nsFastLoadPtr, or (more likely) because the
+ // object was muxed in another document, and deserialization
+ // order does not match serialization order. So we must seek
+ // back and read it now.
+ NS_ASSERTION(entry->mCIDOffset < saveOffset32,
+ "out of order object?!");
+
+ // Ape our Seek wrapper by clearing mCurrentDocumentMapEntry.
+ // This allows for a skipped object to be referenced from two
+ // or more multiplexed documents in the FastLoad file.
+ saveDocMapEntry = mCurrentDocumentMapEntry;
+ mCurrentDocumentMapEntry = nsnull;
+ rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET,
+ entry->mCIDOffset);
+ if (NS_FAILED(rv))
+ return rv;
+ }
+
+ rv = DeserializeObject(getter_AddRefs(object));
+ if (NS_FAILED(rv))
+ return rv;
+
+ if (entry->mCIDOffset != saveOffset32) {
+ // Save the "skip offset" in case we need to skip this object
+ // definition when reading forward, later on.
+ rv = seekable->Tell(&entry->mSkipOffset);
+ if (NS_FAILED(rv))
+ return rv;
+
+ // Restore stream offset and mCurrentDocumentMapEntry in case
+ // we're still reading forward through a part of the multiplex
+ // to get object definitions eagerly.
+ rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET, saveOffset);
+ if (NS_FAILED(rv))
+ return rv;
+ mCurrentDocumentMapEntry = saveDocMapEntry;
+ }
+
+ // Save object until all refs have been deserialized.
+ entry->mReadObject = object;
+ } else {
+ // What if we are at a definition that's already been read? This
+ // case arises when a sharp object's def is serialized before its
+ // refs, while a non-defining ref is deserialized before the def.
+ // We must skip over the object definition.
+ if (oid & MFL_OBJECT_DEF_TAG) {
+ NS_ASSERTION(entry->mSkipOffset != 0, "impossible! see above");
+ nsCOMPtr<nsISeekableStream>
+ seekable(do_QueryInterface(mInputStream));
+ rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET,
+ entry->mSkipOffset);
+ if (NS_FAILED(rv))
+ return rv;
+ }
+ }
+
+ if (aIsStrongRef) {
+ NS_ASSERTION(entry->mStrongRefCnt != 0,
+ "mStrongRefCnt underflow!");
+ --entry->mStrongRefCnt;
+ } else {
+ NS_ASSERTION(MFL_GET_WEAK_REFCNT(entry) != 0,
+ "mWeakRefCnt underflow!");
+ MFL_DROP_WEAK_REFCNT(entry);
+ }
+
+ if (entry->mStrongRefCnt == 0 && MFL_GET_WEAK_REFCNT(entry) == 0)
+ entry->mReadObject = nsnull;
+ }
+
+ if (oid & MFL_QUERY_INTERFACE_TAG) {
+ NSFastLoadID iid;
+ rv = ReadFastID(&iid);
+ if (NS_FAILED(rv))
+ return rv;
+
+ rv = object->QueryInterface(mFooter.GetID(iid),
+ NS_REINTERPRET_CAST(void**, aObject));
+ if (NS_FAILED(rv))
+ return rv;
+ } else {
+ *aObject = object;
+ NS_ADDREF(*aObject);
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsFastLoadFileReader::ReadID(nsID *aResult)
+{
+ nsresult rv;
+ NSFastLoadID fastID;
+
+ rv = ReadFastID(&fastID);
+ if (NS_FAILED(rv))
+ return rv;
+
+ *aResult = mFooter.GetID(fastID);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsFastLoadFileReader::Seek(PRInt32 aWhence, PRInt64 aOffset)
+{
+ mCurrentDocumentMapEntry = nsnull;
+ nsCOMPtr<nsISeekableStream> seekable(do_QueryInterface(mInputStream));
+ return seekable->Seek(aWhence, aOffset);
+}
+
+NS_IMETHODIMP
+nsFastLoadFileReader::Tell(PRInt64 *aResult)
+{
+ nsCOMPtr<nsISeekableStream> seekable(do_QueryInterface(mInputStream));
+ return seekable->Tell(aResult);
+}
+
+NS_IMETHODIMP
+nsFastLoadFileReader::SetEOF()
+{
+ nsCOMPtr<nsISeekableStream> seekable(do_QueryInterface(mInputStream));
+ return seekable->SetEOF();
+}
+
+NS_COM nsresult
+NS_NewFastLoadFileReader(nsIObjectInputStream* *aResult,
+ nsIInputStream* aSrcStream)
+{
+ nsFastLoadFileReader* reader = new nsFastLoadFileReader(aSrcStream);
+ if (!reader)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ // Stabilize reader's refcnt.
+ nsCOMPtr<nsIObjectInputStream> stream(reader);
+
+ nsresult rv = reader->Open();
+ if (NS_FAILED(rv))
+ return rv;
+
+ *aResult = stream;
+ NS_ADDREF(*aResult);
+ return NS_OK;
+}
+
+// -------------------------- nsFastLoadFileWriter --------------------------
+
+NS_IMPL_ISUPPORTS_INHERITED4(nsFastLoadFileWriter,
+ nsBinaryOutputStream,
+ nsIObjectOutputStream,
+ nsIFastLoadFileControl,
+ nsIFastLoadWriteControl,
+ nsISeekableStream)
+
+MOZ_DECL_CTOR_COUNTER(nsFastLoadFileWriter)
+
+struct nsIDMapEntry : public PLDHashEntryHdr {
+ NSFastLoadID mFastID; // 1 + nsFastLoadFooter::mIDMap index
+ nsID mSlowID; // key, used by PLDHashTableOps below
+};
+
+PR_STATIC_CALLBACK(const void *)
+idmap_GetKey(PLDHashTable *aTable, PLDHashEntryHdr *aHdr)
+{
+ nsIDMapEntry* entry = NS_STATIC_CAST(nsIDMapEntry*, aHdr);
+
+ return &entry->mSlowID;
+}
+
+PR_STATIC_CALLBACK(PLDHashNumber)
+idmap_HashKey(PLDHashTable *aTable, const void *aKey)
+{
+ const nsID *idp = NS_REINTERPRET_CAST(const nsID*, aKey);
+
+ return idp->m0;
+}
+
+PR_STATIC_CALLBACK(PRBool)
+idmap_MatchEntry(PLDHashTable *aTable,
+ const PLDHashEntryHdr *aHdr,
+ const void *aKey)
+{
+ const nsIDMapEntry* entry = NS_STATIC_CAST(const nsIDMapEntry*, aHdr);
+ const nsID *idp = NS_REINTERPRET_CAST(const nsID*, aKey);
+
+ return memcmp(&entry->mSlowID, idp, sizeof(nsID)) == 0;
+}
+
+static const PLDHashTableOps idmap_DHashTableOps = {
+ PL_DHashAllocTable,
+ PL_DHashFreeTable,
+ idmap_GetKey,
+ idmap_HashKey,
+ idmap_MatchEntry,
+ PL_DHashMoveEntryStub,
+ PL_DHashClearEntryStub,
+ PL_DHashFinalizeStub,
+ NULL
+};
+
+nsresult
+nsFastLoadFileWriter::MapID(const nsID& aSlowID, NSFastLoadID *aResult)
+{
+ nsIDMapEntry* entry =
+ NS_STATIC_CAST(nsIDMapEntry*,
+ PL_DHashTableOperate(&mIDMap, &aSlowID, PL_DHASH_ADD));
+ if (!entry)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ if (entry->mFastID == 0) {
+ entry->mFastID = mIDMap.entryCount;
+ entry->mSlowID = aSlowID;
+ }
+
+ *aResult = entry->mFastID;
+ return NS_OK;
+}
+
+nsresult
+nsFastLoadFileWriter::WriteHeader(nsFastLoadHeader *aHeader)
+{
+ nsresult rv;
+ PRUint32 bytesWritten;
+
+ rv = Write(aHeader->mMagic, MFL_FILE_MAGIC_SIZE, &bytesWritten);
+ if (NS_FAILED(rv))
+ return rv;
+
+ if (bytesWritten != MFL_FILE_MAGIC_SIZE)
+ return NS_ERROR_FAILURE;
+
+ rv = Write32(aHeader->mChecksum);
+ if (NS_FAILED(rv))
+ return rv;
+
+ rv = Write32(aHeader->mVersion);
+ if (NS_FAILED(rv))
+ return rv;
+
+ rv = Write32(aHeader->mFooterOffset);
+ if (NS_FAILED(rv))
+ return rv;
+
+ rv = Write32(aHeader->mFileSize);
+ if (NS_FAILED(rv))
+ return rv;
+
+ return NS_OK;
+}
+
+// nsIFastLoadFileControl methods:
+
+NS_IMETHODIMP
+nsFastLoadFileWriter::GetChecksum(PRUint32 *aChecksum)
+{
+ if (mHeader.mChecksum == 0)
+ return NS_ERROR_NOT_AVAILABLE;
+ *aChecksum = mHeader.mChecksum;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsFastLoadFileWriter::SetChecksum(PRUint32 aChecksum)
+{
+ mHeader.mChecksum = aChecksum;
+ return NS_OK;
+}
+
+struct nsDocumentMapWriteEntry : public nsDocumentMapEntry {
+ PRUint32 mCurrentSegmentOffset; // last written segment's offset
+};
+
+// Fast mapping from URI object pointer back to spec-indexed document info.
+// We also may need the slow mapping from mURISpec to nsDocumentMapWriteEntry,
+// because the writer's mDocumentMap double hash table may grow "behind the
+// back of" each mURIMap entry's mDocMapEntry member.
+struct nsURIMapWriteEntry : public nsObjectMapEntry {
+ nsDocumentMapWriteEntry* mDocMapEntry;
+ PRUint32 mGeneration;
+ const char* mURISpec;
+};
+
+NS_IMETHODIMP
+nsFastLoadFileWriter::HasMuxedDocument(const char* aURISpec, PRBool *aResult)
+{
+ nsDocumentMapWriteEntry* docMapEntry =
+ NS_STATIC_CAST(nsDocumentMapWriteEntry*,
+ PL_DHashTableOperate(&mDocumentMap, aURISpec,
+ PL_DHASH_LOOKUP));
+
+ *aResult = PL_DHASH_ENTRY_IS_BUSY(docMapEntry);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsFastLoadFileWriter::StartMuxedDocument(nsISupports* aURI,
+ const char* aURISpec)
+{
+ // Save mDocumentMap table generation and mCurrentDocumentMapEntry key in
+ // case the hash table grows during the PL_DHASH_ADD operation.
+ PRUint32 saveGeneration = mDocumentMap.generation;
+ const char* saveURISpec = mCurrentDocumentMapEntry
+ ? mCurrentDocumentMapEntry->mString
+ : nsnull;
+
+ nsDocumentMapWriteEntry* docMapEntry =
+ NS_STATIC_CAST(nsDocumentMapWriteEntry*,
+ PL_DHashTableOperate(&mDocumentMap, aURISpec,
+ PL_DHASH_ADD));
+ if (!docMapEntry)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ // If the generation number changed, refresh mCurrentDocumentMapEntry.
+ if (mCurrentDocumentMapEntry && mDocumentMap.generation != saveGeneration) {
+ mCurrentDocumentMapEntry =
+ NS_STATIC_CAST(nsDocumentMapWriteEntry*,
+ PL_DHashTableOperate(&mDocumentMap, saveURISpec,
+ PL_DHASH_LOOKUP));
+ NS_ASSERTION(PL_DHASH_ENTRY_IS_BUSY(mCurrentDocumentMapEntry),
+ "mCurrentDocumentMapEntry lost during table growth?!");
+
+ // Refresh saveGeneration for use below when initializing uriMapEntry.
+ saveGeneration = mDocumentMap.generation;
+ }
+
+ NS_ASSERTION(docMapEntry->mString == nsnull,
+ "redundant multiplexed document?");
+ if (docMapEntry->mString)
+ return NS_ERROR_UNEXPECTED;
+
+ void* spec = nsMemory::Clone(aURISpec, strlen(aURISpec) + 1);
+ if (!spec)
+ return NS_ERROR_OUT_OF_MEMORY;
+ docMapEntry->mString = NS_REINTERPRET_CAST(const char*, spec);
+ docMapEntry->mURI = aURI;
+ NS_ADDREF(docMapEntry->mURI);
+
+ nsCOMPtr<nsISupports> key(do_QueryInterface(aURI));
+ nsURIMapWriteEntry* uriMapEntry =
+ NS_STATIC_CAST(nsURIMapWriteEntry*,
+ PL_DHashTableOperate(&mURIMap, key, PL_DHASH_ADD));
+ if (!uriMapEntry)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ NS_ASSERTION(uriMapEntry->mDocMapEntry == nsnull,
+ "URI mapped to two different specs?");
+ if (uriMapEntry->mDocMapEntry)
+ return NS_ERROR_UNEXPECTED;
+
+ uriMapEntry->mObject = key;
+ NS_ADDREF(uriMapEntry->mObject);
+ uriMapEntry->mDocMapEntry = docMapEntry;
+ uriMapEntry->mGeneration = saveGeneration;
+ uriMapEntry->mURISpec = NS_REINTERPRET_CAST(const char*, spec);
+ TRACE_MUX(('w', "start %p (%p) %s\n", aURI, key.get(), aURISpec));
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsFastLoadFileWriter::SelectMuxedDocument(nsISupports* aURI,
+ nsISupports** aResult)
+{
+ // Avoid repeatedly QI'ing to nsISeekableStream as we tell and seek.
+ nsCOMPtr<nsISeekableStream> seekable(do_QueryInterface(mOutputStream));
+
+ // Capture the current file offset (XXXbe maintain our own via Write?)
+ nsresult rv;
+ PRInt64 currentSegmentOffset;
+ rv = seekable->Tell(&currentSegmentOffset);
+ if (NS_FAILED(rv))
+ return rv;
+
+ PRUint32 currentSegmentOffset32 = currentSegmentOffset;
+ // Look for an existing entry keyed by aURI, added by StartMuxedDocument.
+ nsCOMPtr<nsISupports> key(do_QueryInterface(aURI));
+ nsURIMapWriteEntry* uriMapEntry =
+ NS_STATIC_CAST(nsURIMapWriteEntry*,
+ PL_DHashTableOperate(&mURIMap, key, PL_DHASH_LOOKUP));
+ NS_ASSERTION(PL_DHASH_ENTRY_IS_BUSY(uriMapEntry),
+ "SelectMuxedDocument without prior StartMuxedDocument?");
+ if (PL_DHASH_ENTRY_IS_FREE(uriMapEntry))
+ return NS_ERROR_UNEXPECTED;
+
+ // Beware that uriMapEntry->mDocMapEntry may be stale, if an mDocumentMap
+ // addition caused that table to grow. We save the mDocumentMap generation
+ // in each uriMapEntry and compare it to the current generation, rehashing
+ // uriMapEntry->mURISpec if necessary.
+
+ nsDocumentMapWriteEntry* docMapEntry = uriMapEntry->mDocMapEntry;
+ if (uriMapEntry->mGeneration != mDocumentMap.generation) {
+ docMapEntry =
+ NS_STATIC_CAST(nsDocumentMapWriteEntry*,
+ PL_DHashTableOperate(&mDocumentMap,
+ uriMapEntry->mURISpec,
+ PL_DHASH_LOOKUP));
+ NS_ASSERTION(PL_DHASH_ENTRY_IS_BUSY(docMapEntry), "lost mDocMapEntry!?");
+ uriMapEntry->mDocMapEntry = docMapEntry;
+ uriMapEntry->mGeneration = mDocumentMap.generation;
+ }
+ docMapEntry = uriMapEntry->mDocMapEntry;
+
+ // If there is a muxed document segment open, close it now by setting its
+ // length, stored in the second PRUint32 of the segment.
+ nsDocumentMapWriteEntry* prevDocMapEntry = mCurrentDocumentMapEntry;
+ if (prevDocMapEntry) {
+ if (prevDocMapEntry == docMapEntry) {
+ TRACE_MUX(('w', "select prev %s same as current!\n",
+ prevDocMapEntry->mString));
+ *aResult = docMapEntry->mURI;
+ NS_ADDREF(*aResult);
+ return NS_OK;
+ }
+
+ PRUint32 prevSegmentOffset = prevDocMapEntry->mCurrentSegmentOffset;
+ TRACE_MUX(('w', "select prev %s offset %lu\n",
+ prevDocMapEntry->mString, prevSegmentOffset));
+
+ rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET,
+ prevSegmentOffset + 4);
+ if (NS_FAILED(rv))
+ return rv;
+
+ // The length counts all bytes in the segment, including the header
+ // that contains [nextSegmentOffset, length].
+ rv = Write32(currentSegmentOffset32 - prevSegmentOffset);
+ if (NS_FAILED(rv))
+ return rv;
+
+ // Seek back to the current offset only if we are not going to seek
+ // back to *this* entry's last "current" segment offset and write its
+ // next segment offset at the first PRUint32 of the segment.
+ if (!docMapEntry->mInitialSegmentOffset) {
+ rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET,
+ currentSegmentOffset);
+ if (NS_FAILED(rv))
+ return rv;
+ }
+ }
+
+ // If this entry was newly added, set its key and initial segment offset.
+ // Otherwise, seek back to write the next segment offset of the previous
+ // segment for this document in the multiplex.
+ if (!docMapEntry->mInitialSegmentOffset) {
+ docMapEntry->mInitialSegmentOffset = currentSegmentOffset32;
+ } else {
+ rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET,
+ docMapEntry->mCurrentSegmentOffset);
+ if (NS_FAILED(rv))
+ return rv;
+
+ rv = Write32(currentSegmentOffset32);
+ if (NS_FAILED(rv))
+ return rv;
+
+ rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET,
+ currentSegmentOffset);
+ if (NS_FAILED(rv))
+ return rv;
+ }
+
+ // Update this document's current segment offset so we can later fix its
+ // next segment offset (unless it is last, in which case we leave the zero
+ // placeholder as a terminator).
+ docMapEntry->mCurrentSegmentOffset = currentSegmentOffset32;
+
+ rv = Write32(0); // nextSegmentOffset placeholder
+ if (NS_FAILED(rv))
+ return rv;
+
+ rv = Write32(0); // length placeholder
+ if (NS_FAILED(rv))
+ return rv;
+
+ *aResult = prevDocMapEntry ? prevDocMapEntry->mURI : nsnull;
+ NS_IF_ADDREF(*aResult);
+
+ mCurrentDocumentMapEntry = docMapEntry;
+ TRACE_MUX(('w', "select %p (%p) offset %lu\n",
+ aURI, key.get(), currentSegmentOffset));
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsFastLoadFileWriter::EndMuxedDocument(nsISupports* aURI)
+{
+ nsCOMPtr<nsISupports> key(do_QueryInterface(aURI));
+ nsURIMapWriteEntry* uriMapEntry =
+ NS_STATIC_CAST(nsURIMapWriteEntry*,
+ PL_DHashTableOperate(&mURIMap, key, PL_DHASH_LOOKUP));
+
+ // If the URI isn't in the map, nsFastLoadFileWriter::StartMuxedDocument
+ // must have been called with a redundant URI, *and* its caller must have
+ // ignored the NS_ERROR_UNEXPECTED it returned in that case.
+ if (PL_DHASH_ENTRY_IS_FREE(uriMapEntry)) {
+ TRACE_MUX(('w', "bad end %p (%p)\n", aURI, key.get()));
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ // Drop our ref to the URI object that was passed to StartMuxedDocument,
+ // we no longer need it, and we do not want to extend its lifetime.
+ if (uriMapEntry->mDocMapEntry)
+ NS_RELEASE(uriMapEntry->mDocMapEntry->mURI);
+
+ // Shrink the table if half the entries are removed sentinels.
+ PRUint32 size = PL_DHASH_TABLE_SIZE(&mURIMap);
+ if (mURIMap.removedCount >= (size >> 2))
+ PL_DHashTableOperate(&mURIMap, key, PL_DHASH_REMOVE);
+ else
+ PL_DHashTableRawRemove(&mURIMap, uriMapEntry);
+
+ TRACE_MUX(('w', "end %p (%p)\n", aURI, key.get()));
+ return NS_OK;
+}
+
+struct nsDependencyMapEntry : public nsStringMapEntry {
+ PRInt64 mLastModified;
+};
+
+NS_IMETHODIMP
+nsFastLoadFileWriter::AddDependency(nsIFile* aFile)
+{
+ nsCAutoString path;
+ nsresult rv = aFile->GetNativePath(path);
+ if (NS_FAILED(rv))
+ return rv;
+
+ nsDependencyMapEntry* entry =
+ NS_STATIC_CAST(nsDependencyMapEntry*,
+ PL_DHashTableOperate(&mDependencyMap, path.get(),
+ PL_DHASH_ADD));
+ if (!entry)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ if (!entry->mString) {
+ const char *tmp = ToNewCString(path);
+ if (!tmp)
+ return NS_ERROR_OUT_OF_MEMORY;
+ entry->mString = tmp;
+
+ // If we can't get the last modified time from aFile, assume it does
+ // not exist, or is otherwise inaccessible to us (due to permissions),
+ // remove the dependency, and suppress the failure.
+ //
+ // Otherwise, we would end up aborting the fastload process due to a
+ // missing .js or .xul or other file on every startup.
+
+ rv = aFile->GetLastModifiedTime(&entry->mLastModified);
+ if (NS_FAILED(rv)) {
+ PL_DHashTableOperate(&mDependencyMap, path.get(), PL_DHASH_REMOVE);
+ rv = NS_OK;
+ }
+ }
+ return rv;
+}
+
+nsresult
+nsFastLoadFileWriter::WriteFooterPrefix(const nsFastLoadFooterPrefix& aFooterPrefix)
+{
+ nsresult rv;
+
+ rv = Write32(aFooterPrefix.mNumIDs);
+ if (NS_FAILED(rv))
+ return rv;
+
+ rv = Write32(aFooterPrefix.mNumSharpObjects);
+ if (NS_FAILED(rv))
+ return rv;
+
+ rv = Write32(aFooterPrefix.mNumMuxedDocuments);
+ if (NS_FAILED(rv))
+ return rv;
+
+ rv = Write32(aFooterPrefix.mNumDependencies);
+ if (NS_FAILED(rv))
+ return rv;
+
+ return NS_OK;
+}
+
+nsresult
+nsFastLoadFileWriter::WriteSlowID(const nsID& aID)
+{
+ nsresult rv;
+
+ rv = Write32(aID.m0);
+ if (NS_FAILED(rv))
+ return rv;
+
+ rv = Write16(aID.m1);
+ if (NS_FAILED(rv))
+ return rv;
+
+ rv = Write16(aID.m2);
+ if (NS_FAILED(rv))
+ return rv;
+
+ PRUint32 bytesWritten;
+ rv = Write(NS_REINTERPRET_CAST(const char*, aID.m3), sizeof aID.m3,
+ &bytesWritten);
+ if (NS_FAILED(rv))
+ return rv;
+
+ if (bytesWritten != sizeof aID.m3)
+ return NS_ERROR_FAILURE;
+ return NS_OK;
+}
+
+nsresult
+nsFastLoadFileWriter::WriteFastID(NSFastLoadID aID)
+{
+ return Write32(aID ^ MFL_ID_XOR_KEY);
+}
+
+nsresult
+nsFastLoadFileWriter::WriteSharpObjectInfo(const nsFastLoadSharpObjectInfo& aInfo)
+{
+ nsresult rv;
+
+ NS_ASSERTION(aInfo.mCIDOffset != 0,
+ "fastload writer: mCIDOffset cannot be zero!");
+
+ rv = Write32(aInfo.mCIDOffset);
+ if (NS_FAILED(rv))
+ return rv;
+
+ rv = Write16(aInfo.mStrongRefCnt);
+ if (NS_FAILED(rv))
+ return rv;
+
+ rv = Write16(aInfo.mWeakRefCnt);
+ if (NS_FAILED(rv))
+ return rv;
+
+ return NS_OK;
+}
+
+nsresult
+nsFastLoadFileWriter::WriteMuxedDocumentInfo(const nsFastLoadMuxedDocumentInfo& aInfo)
+{
+ nsresult rv;
+
+ rv = WriteStringZ(aInfo.mURISpec);
+ if (NS_FAILED(rv))
+ return rv;
+
+ rv = Write32(aInfo.mInitialSegmentOffset);
+ if (NS_FAILED(rv))
+ return rv;
+
+ return NS_OK;
+}
+
+PLDHashOperator PR_CALLBACK
+nsFastLoadFileWriter::IDMapEnumerate(PLDHashTable *aTable,
+ PLDHashEntryHdr *aHdr,
+ PRUint32 aNumber,
+ void *aData)
+{
+ nsIDMapEntry* entry = NS_STATIC_CAST(nsIDMapEntry*, aHdr);
+ PRUint32 index = entry->mFastID - 1;
+ nsID* vector = NS_REINTERPRET_CAST(nsID*, aData);
+
+ NS_ASSERTION(index < aTable->entryCount, "bad nsIDMap index!");
+ vector[index] = entry->mSlowID;
+ return PL_DHASH_NEXT;
+}
+
+struct nsSharpObjectMapEntry : public nsObjectMapEntry {
+ NSFastLoadOID mOID;
+ nsFastLoadSharpObjectInfo mInfo;
+};
+
+PLDHashOperator PR_CALLBACK
+nsFastLoadFileWriter::ObjectMapEnumerate(PLDHashTable *aTable,
+ PLDHashEntryHdr *aHdr,
+ PRUint32 aNumber,
+ void *aData)
+{
+ nsSharpObjectMapEntry* entry = NS_STATIC_CAST(nsSharpObjectMapEntry*, aHdr);
+ PRUint32 index = MFL_OID_TO_SHARP_INDEX(entry->mOID);
+ nsFastLoadSharpObjectInfo* vector =
+ NS_REINTERPRET_CAST(nsFastLoadSharpObjectInfo*, aData);
+
+ NS_ASSERTION(index < aTable->entryCount, "bad nsObjectMap index!");
+ vector[index] = entry->mInfo;
+
+ NS_ASSERTION(entry->mInfo.mStrongRefCnt, "no strong ref in serialization!");
+
+ // Ignore tagged object ids stored as object pointer keys (the updater
+ // code does this).
+ if ((NS_PTR_TO_INT32(entry->mObject) & MFL_OBJECT_DEF_TAG) == 0)
+ NS_RELEASE(entry->mObject);
+
+ return PL_DHASH_NEXT;
+}
+
+PLDHashOperator PR_CALLBACK
+nsFastLoadFileWriter::DocumentMapEnumerate(PLDHashTable *aTable,
+ PLDHashEntryHdr *aHdr,
+ PRUint32 aNumber,
+ void *aData)
+{
+ nsFastLoadFileWriter* writer =
+ NS_REINTERPRET_CAST(nsFastLoadFileWriter*, aTable->data);
+ nsDocumentMapWriteEntry* entry =
+ NS_STATIC_CAST(nsDocumentMapWriteEntry*, aHdr);
+ nsresult* rvp = NS_REINTERPRET_CAST(nsresult*, aData);
+
+ nsFastLoadMuxedDocumentInfo info;
+ info.mURISpec = entry->mString;
+ info.mInitialSegmentOffset = entry->mInitialSegmentOffset;
+ *rvp = writer->WriteMuxedDocumentInfo(info);
+
+ return NS_FAILED(*rvp) ? PL_DHASH_STOP : PL_DHASH_NEXT;
+}
+
+PLDHashOperator PR_CALLBACK
+nsFastLoadFileWriter::DependencyMapEnumerate(PLDHashTable *aTable,
+ PLDHashEntryHdr *aHdr,
+ PRUint32 aNumber,
+ void *aData)
+{
+ nsFastLoadFileWriter* writer =
+ NS_REINTERPRET_CAST(nsFastLoadFileWriter*, aTable->data);
+ nsDependencyMapEntry* entry = NS_STATIC_CAST(nsDependencyMapEntry*, aHdr);
+ nsresult* rvp = NS_REINTERPRET_CAST(nsresult*, aData);
+
+ *rvp = writer->WriteStringZ(entry->mString);
+ if (NS_SUCCEEDED(*rvp))
+ *rvp = writer->Write64(entry->mLastModified);
+
+ return NS_FAILED(*rvp) ? PL_DHASH_STOP :PL_DHASH_NEXT;
+}
+
+nsresult
+nsFastLoadFileWriter::WriteFooter()
+{
+ nsresult rv;
+ PRUint32 i, count;
+
+ nsFastLoadFooterPrefix footerPrefix;
+ footerPrefix.mNumIDs = mIDMap.entryCount;
+ footerPrefix.mNumSharpObjects = mObjectMap.entryCount;
+ footerPrefix.mNumMuxedDocuments = mDocumentMap.entryCount;
+ footerPrefix.mNumDependencies = mDependencyMap.entryCount;
+
+ rv = WriteFooterPrefix(footerPrefix);
+ if (NS_FAILED(rv))
+ return rv;
+
+ // Enumerate mIDMap into a vector indexed by mFastID and write it.
+ nsID* idvec = new nsID[footerPrefix.mNumIDs];
+ if (!idvec)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ count = PL_DHashTableEnumerate(&mIDMap, IDMapEnumerate, idvec);
+ NS_ASSERTION(count == footerPrefix.mNumIDs, "bad mIDMap enumeration!");
+ for (i = 0; i < count; i++) {
+ rv = WriteSlowID(idvec[i]);
+ if (NS_FAILED(rv)) break;
+ }
+
+ delete[] idvec;
+ if (NS_FAILED(rv))
+ return rv;
+
+ // Enumerate mObjectMap into a vector indexed by mOID and write it.
+ nsFastLoadSharpObjectInfo* objvec =
+ new nsFastLoadSharpObjectInfo[footerPrefix.mNumSharpObjects];
+ if (!objvec)
+ return NS_ERROR_OUT_OF_MEMORY;
+#ifdef NS_DEBUG
+ memset(objvec, 0, footerPrefix.mNumSharpObjects *
+ sizeof(nsFastLoadSharpObjectInfo));
+#endif
+
+ count = PL_DHashTableEnumerate(&mObjectMap, ObjectMapEnumerate, objvec);
+ NS_ASSERTION(count == footerPrefix.mNumSharpObjects,
+ "bad mObjectMap enumeration!");
+ for (i = 0; i < count; i++) {
+ rv = WriteSharpObjectInfo(objvec[i]);
+ if (NS_FAILED(rv)) break;
+ }
+
+ delete[] objvec;
+ if (NS_FAILED(rv))
+ return rv;
+
+ // Enumerate mDocumentMap, writing nsFastLoadMuxedDocumentInfo records
+ count = PL_DHashTableEnumerate(&mDocumentMap, DocumentMapEnumerate, &rv);
+ if (NS_FAILED(rv))
+ return rv;
+
+ NS_ASSERTION(count == footerPrefix.mNumMuxedDocuments,
+ "bad mDocumentMap enumeration!");
+
+ // Write out make-like file dependencies.
+ count = PL_DHashTableEnumerate(&mDependencyMap, DependencyMapEnumerate, &rv);
+ if (NS_FAILED(rv))
+ return rv;
+
+ return NS_OK;
+}
+
+nsresult
+nsFastLoadFileWriter::Init()
+{
+ if (!PL_DHashTableInit(&mIDMap, &idmap_DHashTableOps, (void *)this,
+ sizeof(nsIDMapEntry), PL_DHASH_MIN_SIZE)) {
+ mIDMap.ops = nsnull;
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ if (!PL_DHashTableInit(&mObjectMap, &objmap_DHashTableOps, (void *)this,
+ sizeof(nsSharpObjectMapEntry), PL_DHASH_MIN_SIZE)) {
+ mObjectMap.ops = nsnull;
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ if (!PL_DHashTableInit(&mDocumentMap, &strmap_DHashTableOps, (void *)this,
+ sizeof(nsDocumentMapWriteEntry),
+ PL_DHASH_MIN_SIZE)) {
+ mDocumentMap.ops = nsnull;
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ if (!PL_DHashTableInit(&mURIMap, &objmap_DHashTableOps, (void *)this,
+ sizeof(nsURIMapWriteEntry), PL_DHASH_MIN_SIZE)) {
+ mURIMap.ops = nsnull;
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ if (!PL_DHashTableInit(&mDependencyMap, &strmap_DHashTableOps, (void *)this,
+ sizeof(nsDependencyMapEntry), PL_DHASH_MIN_SIZE)) {
+ mDependencyMap.ops = nsnull;
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ return NS_OK;
+}
+
+nsresult
+nsFastLoadFileWriter::Open()
+{
+ nsCOMPtr<nsISeekableStream> seekable(do_QueryInterface(mOutputStream));
+ if (!seekable)
+ return NS_ERROR_UNEXPECTED;
+
+ nsresult rv;
+
+ rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET,
+ sizeof(nsFastLoadHeader));
+ if (NS_FAILED(rv))
+ return rv;
+
+ return Init();
+}
+
+NS_IMETHODIMP
+nsFastLoadFileWriter::Close()
+{
+ nsresult rv;
+
+ memcpy(mHeader.mMagic, magic, MFL_FILE_MAGIC_SIZE);
+ mHeader.mChecksum = 0;
+ mHeader.mVersion = MFL_FILE_VERSION;
+
+ nsCOMPtr<nsISeekableStream> seekable(do_QueryInterface(mOutputStream));
+
+ PRInt64 footerOffset;
+ rv = seekable->Tell(&footerOffset);
+
+ LL_L2UI(mHeader.mFooterOffset, footerOffset);
+ if (NS_FAILED(rv))
+ return rv;
+
+ // If there is a muxed document segment open, close it now by setting its
+ // length, stored in the second PRUint32 of the segment.
+ if (mCurrentDocumentMapEntry) {
+ PRUint32 currentSegmentOffset =
+ mCurrentDocumentMapEntry->mCurrentSegmentOffset;
+ rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET,
+ currentSegmentOffset + 4);
+ if (NS_FAILED(rv))
+ return rv;
+
+ rv = Write32(mHeader.mFooterOffset - currentSegmentOffset);
+ if (NS_FAILED(rv))
+ return rv;
+
+ // Seek back to the current offset to write the footer.
+ rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET,
+ mHeader.mFooterOffset);
+ if (NS_FAILED(rv))
+ return rv;
+
+ mCurrentDocumentMapEntry = nsnull;
+ }
+
+ rv = WriteFooter();
+ if (NS_FAILED(rv))
+ return rv;
+ PRInt64 fileSize;
+ rv = seekable->Tell(&fileSize);
+ LL_L2UI(mHeader.mFileSize, fileSize);
+ if (NS_FAILED(rv))
+ return rv;
+
+ rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET, 0);
+ if (NS_FAILED(rv))
+ return rv;
+
+ rv = WriteHeader(&mHeader);
+ if (NS_FAILED(rv))
+ return rv;
+
+ // Now compute the checksum, using mFileIO to get an input stream on the
+ // underlying FastLoad file.
+ if (mFileIO) {
+ // Get the unbuffered output stream, which flushes the buffered header
+ // so we can read and checksum it along with the rest of the file, and
+ // which allows us to write the checksum directly.
+ nsCOMPtr<nsIStreamBufferAccess>
+ bufferAccess(do_QueryInterface(mOutputStream));
+ nsCOMPtr<nsIOutputStream> output;
+ rv = bufferAccess->GetUnbufferedStream(getter_AddRefs(output));
+ if (NS_FAILED(rv) || !output)
+ return NS_ERROR_UNEXPECTED;
+
+ nsCOMPtr<nsIInputStream> input;
+ rv = mFileIO->GetInputStream(getter_AddRefs(input));
+ if (NS_FAILED(rv))
+ return rv;
+
+ // Get the unbuffered input stream, to avoid copying overhead and to
+ // keep our view of the file coherent with the writer -- we don't want
+ // to hit a stale buffer in the reader's underlying stream.
+ bufferAccess = do_QueryInterface(input);
+ rv = bufferAccess->GetUnbufferedStream(getter_AddRefs(input));
+ if (NS_FAILED(rv) || !input)
+ return NS_ERROR_UNEXPECTED;
+
+ // Seek the input stream to offset 0, in case it's a reader who has
+ // already been used to consume some of the FastLoad file.
+ seekable = do_QueryInterface(input);
+ rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET, 0);
+ if (NS_FAILED(rv))
+ return rv;
+
+ char buf[MFL_CHECKSUM_BUFSIZE];
+ PRUint32 len, rem = 0;
+ PRUint32 checksum = 0;
+
+ // Ok, we're finally ready to checksum the FastLoad file we just wrote!
+ while (NS_SUCCEEDED(rv =
+ input->Read(buf + rem, sizeof buf - rem, &len)) &&
+ len) {
+ len += rem;
+ rem = NS_AccumulateFastLoadChecksum(&checksum,
+ NS_REINTERPRET_CAST(PRUint8*,
+ buf),
+ len,
+ PR_FALSE);
+ if (rem)
+ memcpy(buf, buf + len - rem, rem);
+ }
+ if (NS_FAILED(rv))
+ return rv;
+
+ if (rem) {
+ NS_AccumulateFastLoadChecksum(&checksum,
+ NS_REINTERPRET_CAST(PRUint8*, buf),
+ rem,
+ PR_TRUE);
+ }
+
+ // Store the checksum in the FastLoad file header and remember it via
+ // mHeader.mChecksum, for GetChecksum.
+ seekable = do_QueryInterface(output);
+ rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET,
+ offsetof(nsFastLoadHeader, mChecksum));
+ if (NS_FAILED(rv))
+ return rv;
+
+ mHeader.mChecksum = checksum;
+ checksum = NS_SWAP32(checksum);
+ PRUint32 bytesWritten;
+ rv = output->Write(NS_REINTERPRET_CAST(char*, &checksum),
+ sizeof checksum,
+ &bytesWritten);
+ if (NS_FAILED(rv))
+ return rv;
+ if (bytesWritten != sizeof checksum)
+ return NS_ERROR_FAILURE;
+ }
+
+ return mOutputStream->Close();
+}
+
+// Psuedo-tag used as flag between WriteSingleRefObject and WriteObjectCommon.
+#define MFL_SINGLE_REF_PSEUDO_TAG PR_BIT(MFL_OBJECT_TAG_BITS)
+
+nsresult
+nsFastLoadFileWriter::WriteObjectCommon(nsISupports* aObject,
+ PRBool aIsStrongRef,
+ PRUint32 aTags)
+{
+ nsrefcnt rc;
+ nsresult rv;
+
+ NS_ASSERTION((NS_PTR_TO_INT32(aObject) & MFL_OBJECT_DEF_TAG) == 0,
+ "odd nsISupports*, oh no!");
+
+ // Here be manual refcounting dragons!
+ rc = aObject->AddRef();
+ NS_ASSERTION(rc != 0, "bad refcnt when writing aObject!");
+
+ NSFastLoadOID oid;
+ nsCOMPtr<nsIClassInfo> classInfo;
+
+ if (rc == 2 && (aTags & MFL_SINGLE_REF_PSEUDO_TAG)) {
+ // Dull object: only one strong ref and no weak refs in serialization.
+ // Conservative: we don't trust the caller if there are more than two
+ // refs (one from the AddRef above, one from the data structure that's
+ // being serialized).
+ oid = MFL_DULL_OBJECT_OID;
+ aObject->Release();
+ } else {
+ // Object is presumed to be multiply connected through some combo of
+ // strong and weak refs. Hold onto it via mObjectMap.
+ nsSharpObjectMapEntry* entry =
+ NS_STATIC_CAST(nsSharpObjectMapEntry*,
+ PL_DHashTableOperate(&mObjectMap, aObject,
+ PL_DHASH_ADD));
+ if (!entry) {
+ aObject->Release();
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ if (!entry->mObject) {
+ // First time we've seen this object address: add it to mObjectMap
+ // and serialize the object at the current stream offset.
+ PRInt64 thisOffset;
+ rv = Tell(&thisOffset);
+ if (NS_FAILED(rv)) {
+ aObject->Release();
+ return rv;
+ }
+
+ // NB: aObject was already held, and mObject is a raw nsISupports*.
+ entry->mObject = aObject;
+
+ oid = (mObjectMap.entryCount << MFL_OBJECT_TAG_BITS);
+ entry->mOID = oid;
+
+ // NB: the (32-bit, fast) CID and object data follow the OID.
+ entry->mInfo.mCIDOffset = thisOffset + sizeof(oid);
+ entry->mInfo.mStrongRefCnt = aIsStrongRef ? 1 : 0;
+ entry->mInfo.mWeakRefCnt = aIsStrongRef ? 0 : 1;
+
+ // Record in oid the fact that we're defining this object in the
+ // stream, and get the object's class info here, so we can take
+ // note of singletons in order to avoid reserializing them when
+ // updating after reading.
+ oid |= MFL_OBJECT_DEF_TAG;
+ classInfo = do_QueryInterface(aObject);
+ if (!classInfo)
+ return NS_ERROR_FAILURE;
+
+ PRUint32 flags;
+ if (NS_SUCCEEDED(classInfo->GetFlags(&flags)) &&
+ (flags & nsIClassInfo::SINGLETON)) {
+ MFL_SET_SINGLETON_FLAG(&entry->mInfo);
+ }
+ } else {
+ // Already serialized, recover oid and update the desired refcnt.
+ oid = entry->mOID;
+ if (aIsStrongRef) {
+ ++entry->mInfo.mStrongRefCnt;
+ NS_ASSERTION(entry->mInfo.mStrongRefCnt != 0,
+ "mStrongRefCnt overflow");
+ } else {
+ MFL_BUMP_WEAK_REFCNT(&entry->mInfo);
+ NS_ASSERTION(MFL_GET_WEAK_REFCNT(&entry->mInfo) != 0,
+ "mWeakRefCnt overflow");
+ }
+
+ aObject->Release();
+ }
+ }
+
+ if (!aIsStrongRef)
+ oid |= MFL_WEAK_REF_TAG;
+ oid |= (aTags & MFL_QUERY_INTERFACE_TAG);
+
+ rv = Write32(oid ^ MFL_OID_XOR_KEY);
+ if (NS_FAILED(rv))
+ return rv;
+
+ if (oid & MFL_OBJECT_DEF_TAG) {
+ nsCOMPtr<nsISerializable> serializable(do_QueryInterface(aObject));
+ if (!serializable)
+ return NS_ERROR_FAILURE;
+
+ nsCID slowCID;
+ rv = classInfo->GetClassIDNoAlloc(&slowCID);
+ if (NS_FAILED(rv))
+ return rv;
+
+ NSFastLoadID fastCID;
+ rv = MapID(slowCID, &fastCID);
+ if (NS_FAILED(rv))
+ return rv;
+
+ rv = WriteFastID(fastCID);
+ if (NS_FAILED(rv))
+ return rv;
+
+ rv = serializable->Write(this);
+ if (NS_FAILED(rv))
+ return rv;
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsFastLoadFileWriter::WriteObject(nsISupports* aObject, PRBool aIsStrongRef)
+{
+#ifdef NS_DEBUG
+ nsCOMPtr<nsISupports> rootObject(do_QueryInterface(aObject));
+
+ NS_ASSERTION(rootObject.get() == aObject,
+ "bad call to WriteObject -- call WriteCompoundObject!");
+#endif
+
+ return WriteObjectCommon(aObject, aIsStrongRef, 0);
+}
+
+NS_IMETHODIMP
+nsFastLoadFileWriter::WriteSingleRefObject(nsISupports* aObject)
+{
+#ifdef NS_DEBUG
+ nsCOMPtr<nsISupports> rootObject(do_QueryInterface(aObject));
+
+ NS_ASSERTION(rootObject.get() == aObject,
+ "bad call to WriteSingleRefObject -- call WriteCompoundObject!");
+#endif
+
+ return WriteObjectCommon(aObject, PR_TRUE, MFL_SINGLE_REF_PSEUDO_TAG);
+}
+
+NS_IMETHODIMP
+nsFastLoadFileWriter::WriteCompoundObject(nsISupports* aObject,
+ const nsIID& aIID,
+ PRBool aIsStrongRef)
+{
+ nsresult rv;
+ nsCOMPtr<nsISupports> rootObject(do_QueryInterface(aObject));
+
+#ifdef NS_DEBUG
+ nsCOMPtr<nsISupports> roundtrip;
+ rootObject->QueryInterface(aIID, getter_AddRefs(roundtrip));
+
+ NS_ASSERTION(rootObject.get() != aObject,
+ "wasteful call to WriteCompoundObject -- call WriteObject!");
+ NS_ASSERTION(roundtrip.get() == aObject,
+ "bad aggregation or multiple inheritance detected by call to "
+ "WriteCompoundObject!");
+#endif
+
+ rv = WriteObjectCommon(rootObject, aIsStrongRef, MFL_QUERY_INTERFACE_TAG);
+ if (NS_FAILED(rv))
+ return rv;
+
+ NSFastLoadID iid;
+ rv = MapID(aIID, &iid);
+ if (NS_FAILED(rv))
+ return rv;
+
+ return WriteFastID(iid);
+}
+
+NS_IMETHODIMP
+nsFastLoadFileWriter::WriteID(const nsID& aID)
+{
+ nsresult rv;
+ NSFastLoadID fastID;
+
+ rv = MapID(aID, &fastID);
+ if (NS_FAILED(rv))
+ return rv;
+
+ return WriteFastID(fastID);
+}
+
+NS_IMETHODIMP
+nsFastLoadFileWriter::Seek(PRInt32 aWhence, PRInt64 aOffset)
+{
+ mCurrentDocumentMapEntry = nsnull;
+ nsCOMPtr<nsISeekableStream> seekable(do_QueryInterface(mOutputStream));
+ return seekable->Seek(aWhence, aOffset);
+}
+
+NS_IMETHODIMP
+nsFastLoadFileWriter::Tell(PRInt64 *aResult)
+{
+ nsCOMPtr<nsISeekableStream> seekable(do_QueryInterface(mOutputStream));
+ return seekable->Tell(aResult);
+}
+
+NS_IMETHODIMP
+nsFastLoadFileWriter::SetEOF()
+{
+ nsCOMPtr<nsISeekableStream> seekable(do_QueryInterface(mOutputStream));
+ return seekable->SetEOF();
+}
+
+NS_COM nsresult
+NS_NewFastLoadFileWriter(nsIObjectOutputStream* *aResult,
+ nsIOutputStream* aDestStream,
+ nsIFastLoadFileIO* aFileIO)
+{
+ nsFastLoadFileWriter* writer =
+ new nsFastLoadFileWriter(aDestStream, aFileIO);
+ if (!writer)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ // Stabilize writer's refcnt.
+ nsCOMPtr<nsIObjectOutputStream> stream(writer);
+
+ nsresult rv = writer->Open();
+ if (NS_FAILED(rv))
+ return rv;
+
+ *aResult = stream;
+ NS_ADDREF(*aResult);
+ return NS_OK;
+}
+
+// -------------------------- nsFastLoadFileUpdater --------------------------
+
+NS_IMPL_ISUPPORTS_INHERITED1(nsFastLoadFileUpdater,
+ nsFastLoadFileWriter,
+ nsIFastLoadFileIO)
+
+NS_IMETHODIMP
+nsFastLoadFileUpdater::GetInputStream(nsIInputStream** aResult)
+{
+ *aResult = mInputStream;
+ NS_IF_ADDREF(*aResult);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsFastLoadFileUpdater::GetOutputStream(nsIOutputStream** aResult)
+{
+ *aResult = nsnull;
+ return NS_OK;
+}
+
+PLDHashOperator PR_CALLBACK
+nsFastLoadFileUpdater::CopyReadDocumentMapEntryToUpdater(PLDHashTable *aTable,
+ PLDHashEntryHdr *aHdr,
+ PRUint32 aNumber,
+ void *aData)
+{
+ nsDocumentMapReadEntry* readEntry =
+ NS_STATIC_CAST(nsDocumentMapReadEntry*, aHdr);
+ nsFastLoadFileUpdater* updater =
+ NS_REINTERPRET_CAST(nsFastLoadFileUpdater*, aData);
+
+ void* spec = nsMemory::Clone(readEntry->mString,
+ strlen(readEntry->mString) + 1);
+ if (!spec)
+ return PL_DHASH_STOP;
+
+ nsDocumentMapWriteEntry* writeEntry =
+ NS_STATIC_CAST(nsDocumentMapWriteEntry*,
+ PL_DHashTableOperate(&updater->mDocumentMap, spec,
+ PL_DHASH_ADD));
+ if (!writeEntry) {
+ nsMemory::Free(spec);
+ return PL_DHASH_STOP;
+ }
+
+ writeEntry->mString = NS_REINTERPRET_CAST(const char*, spec);
+ writeEntry->mURI = nsnull;
+ writeEntry->mInitialSegmentOffset = readEntry->mInitialSegmentOffset;
+ writeEntry->mCurrentSegmentOffset = 0;
+ return PL_DHASH_NEXT;
+}
+
+nsresult
+nsFastLoadFileUpdater::Open(nsFastLoadFileReader* aReader)
+{
+ nsCOMPtr<nsISeekableStream> seekable(do_QueryInterface(mOutputStream));
+ if (!seekable)
+ return NS_ERROR_UNEXPECTED;
+
+ nsresult rv;
+ rv = nsFastLoadFileWriter::Init();
+ if (NS_FAILED(rv))
+ return rv;
+
+ PRUint32 i, n;
+
+ // Map from dense, zero-based, uint32 NSFastLoadID in reader to 16-byte
+ // nsID in updater.
+ nsID* readIDMap = aReader->mFooter.mIDMap;
+ for (i = 0, n = aReader->mFooter.mNumIDs; i < n; i++) {
+ NSFastLoadID fastID;
+ rv = MapID(readIDMap[i], &fastID);
+ NS_ASSERTION(fastID == i + 1, "huh?");
+ if (NS_FAILED(rv))
+ return rv;
+ }
+
+ // Map from reader dense, zero-based MFL_OID_TO_SHARP_INDEX(oid) to sharp
+ // object offset and refcnt information in updater.
+ nsFastLoadFileReader::nsObjectMapEntry* readObjectMap =
+ aReader->mFooter.mObjectMap;
+
+ // Prepare to save aReader state in case we need to seek back and read a
+ // singleton object that might otherwise get written by this updater.
+ nsDocumentMapReadEntry* saveDocMapEntry = nsnull;
+ nsCOMPtr<nsISeekableStream> inputSeekable;
+ PRInt64 saveOffset = 0;
+
+ for (i = 0, n = aReader->mFooter.mNumSharpObjects; i < n; i++) {
+ nsFastLoadFileReader::nsObjectMapEntry* readEntry = &readObjectMap[i];
+
+ NS_ASSERTION(readEntry->mCIDOffset != 0,
+ "fastload updater: mCIDOffset cannot be zero!");
+
+ // If the reader didn't read this object but it's a singleton, we must
+ // "deserialize" it now, to discover its one and only root nsISupports
+ // address. The object already exists in memory if it was created at
+ // startup without resort to the FastLoad file. The canonical example
+ // is the system principal object held by all XUL JS scripts.
+
+ nsISupports* obj = readEntry->mReadObject;
+ if (!obj && MFL_GET_SINGLETON_FLAG(readEntry)) {
+ if (!saveDocMapEntry) {
+ inputSeekable = do_QueryInterface(aReader->mInputStream);
+ rv = inputSeekable->Tell(&saveOffset);
+ if (NS_FAILED(rv))
+ return rv;
+
+ saveDocMapEntry = aReader->mCurrentDocumentMapEntry;
+ aReader->mCurrentDocumentMapEntry = nsnull;
+ }
+
+ rv = inputSeekable->Seek(nsISeekableStream::NS_SEEK_SET,
+ readEntry->mCIDOffset);
+ if (NS_FAILED(rv))
+ return rv;
+
+ rv = aReader
+ ->DeserializeObject(getter_AddRefs(readEntry->mReadObject));
+ if (NS_FAILED(rv))
+ return rv;
+ obj = readEntry->mReadObject;
+
+ // Don't forget to set mSkipOffset in case someone calls the reader
+ // to "deserialize" (yet again) the object we just read.
+ //
+ // Say the singleton is the system principal, and the FastLoad file
+ // contains data for navigator.xul including scripts and functions.
+ // If we update the FastLoad file to contain data for messenger.xul
+ // in a separate session started via mozilla -mail, *and during the
+ // same FastLoad episode in this session* race to open a navigator
+ // window, we will attempt to read all objects serialized in the
+ // navigator.xul portion of the FastLoad file.
+ //
+ // mSkipOffset must be set in such a case so the reader can skip
+ // the system principal's serialized data, because the updater for
+ // messenger.xul being opened here has already read it.
+
+ rv = inputSeekable->Tell(&readEntry->mSkipOffset);
+ if (NS_FAILED(rv))
+ return rv;
+ }
+
+ NSFastLoadOID oid = MFL_SHARP_INDEX_TO_OID(i);
+ void* key = obj
+ ? NS_REINTERPRET_CAST(void*, obj)
+ : NS_REINTERPRET_CAST(void*, (oid | MFL_OBJECT_DEF_TAG));
+
+ nsSharpObjectMapEntry* writeEntry =
+ NS_STATIC_CAST(nsSharpObjectMapEntry*,
+ PL_DHashTableOperate(&mObjectMap, key,
+ PL_DHASH_ADD));
+ if (!writeEntry)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ // Hold the object if there is one, so that objmap_ClearEntry can
+ // release the reference.
+ NS_IF_ADDREF(obj);
+ writeEntry->mObject = NS_REINTERPRET_CAST(nsISupports*, key);
+ writeEntry->mOID = oid;
+ writeEntry->mInfo.mCIDOffset = readEntry->mCIDOffset;
+ writeEntry->mInfo.mStrongRefCnt = readEntry->mSaveStrongRefCnt;
+ writeEntry->mInfo.mWeakRefCnt = readEntry->mSaveWeakRefCnt;
+ }
+
+ // If we had to read any singletons, restore aReader's saved state.
+ if (saveDocMapEntry) {
+ rv = inputSeekable->Seek(nsISeekableStream::NS_SEEK_SET, saveOffset);
+ if (NS_FAILED(rv))
+ return rv;
+
+ aReader->mCurrentDocumentMapEntry = saveDocMapEntry;
+ }
+
+ // Copy URI spec string and initial segment offset in FastLoad file from
+ // nsDocumentMapReadEntry in reader to nsDocumentMapWriteEntry in updater.
+ // If we didn't enumerate all entries, we ran out of memory.
+ n = PL_DHashTableEnumerate(&aReader->mFooter.mDocumentMap,
+ CopyReadDocumentMapEntryToUpdater,
+ this);
+ if (n != aReader->mFooter.mDocumentMap.entryCount)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ // Copy source filename dependencies from reader to updater.
+ nsISupportsArray* readDeps = aReader->mFooter.mDependencies;
+ rv = readDeps->Count(&n);
+ if (NS_FAILED(rv))
+ return rv;
+
+ for (i = 0; i < n; i++) {
+ nsCOMPtr<nsIFile> file;
+ rv = readDeps->GetElementAt(i, getter_AddRefs(file));
+ if (NS_FAILED(rv))
+ return rv;
+
+ rv = AddDependency(file);
+ if (NS_FAILED(rv))
+ return rv;
+ }
+
+ // Seek to the reader's footer offset so we overwrite the footer. First,
+ // update the header to have a zero mFooterOffset, which will invalidate
+ // the FastLoad file on next startup read attempt, should we crash before
+ // completing this update.
+ rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET,
+ offsetof(nsFastLoadHeader, mFooterOffset));
+ if (NS_FAILED(rv))
+ return rv;
+
+ rv = Write32(0);
+ if (NS_FAILED(rv))
+ return rv;
+
+ rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET,
+ aReader->mHeader.mFooterOffset);
+ if (NS_FAILED(rv))
+ return rv;
+
+ // Avoid creating yet another object by implementing nsIFastLoadFileIO on
+ // this updater, and save aReader's input stream so it can be returned by
+ // GetInputStream called from nsFastLoadFileWriter::Close. This requires
+ // that we override Close to break the resulting zero-length cycle.
+ mFileIO = this;
+ mInputStream = aReader->mInputStream;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsFastLoadFileUpdater::Close()
+{
+ // Call base-class Close implementation, which uses mFileIO.
+ nsresult rv = nsFastLoadFileWriter::Close();
+
+ // Break degenerate cycle from this->mFileIO to this.
+ mFileIO = nsnull;
+ return rv;
+}
+
+NS_COM nsresult
+NS_NewFastLoadFileUpdater(nsIObjectOutputStream* *aResult,
+ nsIOutputStream* aOutputStream,
+ nsIObjectInputStream* aReaderAsStream)
+{
+ // Make sure that aReaderAsStream is an nsFastLoadFileReader.
+ nsCOMPtr<nsIFastLoadFileReader> reader(do_QueryInterface(aReaderAsStream));
+ if (!reader)
+ return NS_ERROR_UNEXPECTED;
+
+ nsFastLoadFileUpdater* updater = new nsFastLoadFileUpdater(aOutputStream);
+ if (!updater)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ // Stabilize updater's refcnt.
+ nsCOMPtr<nsIObjectOutputStream> stream(updater);
+
+ nsresult rv = updater->Open(NS_STATIC_CAST(nsFastLoadFileReader*,
+ aReaderAsStream));
+ if (NS_FAILED(rv))
+ return rv;
+
+ *aResult = stream;
+ NS_ADDREF(*aResult);
+ return NS_OK;
+}
diff --git a/src/libs/xpcom18a4/xpcom/io/nsFastLoadFile.h b/src/libs/xpcom18a4/xpcom/io/nsFastLoadFile.h
new file mode 100644
index 00000000..9f6f061a
--- /dev/null
+++ b/src/libs/xpcom18a4/xpcom/io/nsFastLoadFile.h
@@ -0,0 +1,566 @@
+/* -*- 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 FastLoad code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2001
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Brendan Eich <brendan@mozilla.org> (original author)
+ *
+ * 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 ***** */
+
+#ifndef nsFastLoadFile_h___
+#define nsFastLoadFile_h___
+
+/**
+ * Mozilla FastLoad file format and helper types.
+ */
+
+#include "prtypes.h"
+#include "pldhash.h"
+
+#include "nsBinaryStream.h"
+#include "nsCOMPtr.h"
+#include "nsDebug.h"
+#include "nsID.h"
+#include "nsMemory.h"
+#include "nsVoidArray.h"
+
+#include "nsIFastLoadFileControl.h"
+#include "nsIFastLoadService.h"
+#include "nsISeekableStream.h"
+#include "nsISupportsArray.h"
+
+/**
+ * FastLoad file Object ID (OID) is an identifier for multiply and cyclicly
+ * connected objects in the serialized graph of all reachable objects.
+ *
+ * Holy Mixed Metaphors: JS, after Common Lisp, uses #n= to define a "sharp
+ * variable" naming an object that's multiply or cyclicly connected, and #n#
+ * to stand for a connection to an already-defined object. We too call any
+ * object with multiple references "sharp", and (here it comes) any object
+ * with only one reference "dull".
+ *
+ * Note that only sharp objects require a mapping from OID to FastLoad file
+ * offset and other information. Dull objects can be serialized _in situ_
+ * (where they are referenced) and deserialized when their (singular, shared)
+ * OID is scanned.
+ *
+ * We also compress 16-byte XPCOM IDs into 32-bit dense identifiers to save
+ * space. See nsFastLoadFooter, below, for the mapping data structure used to
+ * compute an nsID given an NSFastLoadID.
+ */
+typedef PRUint32 NSFastLoadID; // nsFastLoadFooter::mIDMap index
+typedef PRUint32 NSFastLoadOID; // nsFastLoadFooter::mObjectMap index
+
+/**
+ * A Mozilla FastLoad file is an untagged (in general) stream of objects and
+ * primitive-type data. Small integers are fairly common, and could easily be
+ * confused for NSFastLoadIDs and NSFastLoadOIDs. To help catch bugs where
+ * reader and writer code fail to match, we XOR unlikely 32-bit numbers with
+ * NSFastLoad*IDs when storing and fetching. The following unlikely values are
+ * irrational numbers ((sqrt(5)-1)/2, sqrt(2)-1) represented in fixed point.
+ *
+ * The reader XORs, converts the ID to an index, and bounds-checks all array
+ * accesses that use the index. Array access code asserts that the index is in
+ * bounds, and returns a dummy array element if it isn't.
+ */
+#define MFL_ID_XOR_KEY 0x9E3779B9 // key XOR'd with ID when serialized
+#define MFL_OID_XOR_KEY 0x6A09E667 // key XOR'd with OID when serialized
+
+/**
+ * An OID can be tagged to introduce the serialized definition of the object,
+ * or to stand for a strong or weak reference to that object. Thus the high
+ * 29 bits actually identify the object, and the low three bits tell whether
+ * the object is being defined or just referenced -- and via what inheritance
+ * chain or inner object, if necessary.
+ *
+ * The MFL_QUERY_INTERFACE_TAG bit helps us cope with aggregation and multiple
+ * inheritance: object identity follows the XPCOM rule, but a deserializer may
+ * need to query for an interface not on the primary inheritance chain ending
+ * in the nsISupports whose address uniquely identifies the XPCOM object being
+ * referenced or defined.
+ */
+#define MFL_OBJECT_TAG_BITS 3
+#define MFL_OBJECT_TAG_MASK PR_BITMASK(MFL_OBJECT_TAG_BITS)
+
+#define MFL_OBJECT_DEF_TAG 1U // object definition follows this OID
+#define MFL_WEAK_REF_TAG 2U // OID weakly refers to a prior object
+ // NB: do not confuse with nsWeakPtr!
+#define MFL_QUERY_INTERFACE_TAG 4U // QI object to the ID follows this OID
+ // NB: an NSFastLoadID, not an nsIID!
+
+/**
+ * The dull object identifier introduces the definition of all objects that
+ * have only one (necessarily strong) ref in the serialization. The definition
+ * appears at the point of reference.
+ */
+#define MFL_DULL_OBJECT_OID MFL_OBJECT_DEF_TAG
+
+/**
+ * Convert an OID to an index into nsFastLoadFooter::mObjectMap.
+ */
+#define MFL_OID_TO_SHARP_INDEX(oid) (((oid) >> MFL_OBJECT_TAG_BITS) - 1)
+#define MFL_SHARP_INDEX_TO_OID(index) (((index) + 1) << MFL_OBJECT_TAG_BITS)
+
+/**
+ * Magic "number" at start of a FastLoad file. Inspired by the PNG "magic"
+ * string, which inspired XPCOM's typelib (.xpt) file magic. Guaranteed to be
+ * corrupted by FTP-as-ASCII and other likely errors, meaningful to clued-in
+ * humans, and ending in ^Z to terminate erroneous text input on Windows.
+ */
+#define MFL_FILE_MAGIC "XPCOM\nMozFASL\r\n\032"
+#define MFL_FILE_MAGIC_SIZE 16
+
+#define MFL_FILE_VERSION_0 0
+#define MFL_FILE_VERSION_1 1000
+#define MFL_FILE_VERSION 4 // fix to note singletons in object map
+
+/**
+ * Compute Fletcher's 16-bit checksum over aLength bytes starting at aBuffer,
+ * with the initial accumulators seeded from *aChecksum, and final checksum
+ * returned in *aChecksum. The return value is the number of unchecked bytes,
+ * which may be non-zero if aBuffer is misaligned or aLength is odd. Callers
+ * should copy any remaining bytes to the front of the next buffer.
+ *
+ * If aLastBuffer is false, do not check any bytes remaining due to misaligned
+ * aBuffer or odd aLength, instead returning the remaining byte count. But if
+ * aLastBuffer is true, treat aBuffer as the last buffer in the file and check
+ * every byte, returning 0. Here's a read-loop checksumming sketch:
+ *
+ * char buf[BUFSIZE];
+ * PRUint32 len, rem = 0;
+ * PRUint32 checksum = 0;
+ *
+ * while (NS_SUCCEEDED(rv = Read(buf + rem, sizeof buf - rem, &len)) && len) {
+ * len += rem;
+ * rem = NS_AccumulateFastLoadChecksum(&checksum,
+ * NS_REINTERPRET_CAST(PRUint8*, buf),
+ * len,
+ * PR_FALSE);
+ * if (rem)
+ * memcpy(buf, buf + len - rem, rem);
+ * }
+ *
+ * if (rem) {
+ * NS_AccumulateFastLoadChecksum(&checksum,
+ * NS_REINTERPRET_CAST(PRUint8*, buf),
+ * rem,
+ * PR_TRUE);
+ * }
+ *
+ * After this, if NS_SUCCEEDED(rv), checksum contains a valid FastLoad sum.
+ */
+PR_EXTERN(PRUint32)
+NS_AccumulateFastLoadChecksum(PRUint32 *aChecksum,
+ const PRUint8* aBuffer,
+ PRUint32 aLength,
+ PRBool aLastBuffer);
+
+PR_EXTERN(PRUint32)
+NS_AddFastLoadChecksums(PRUint32 sum1, PRUint32 sum2, PRUint32 sum2ByteCount);
+
+/**
+ * Header at the start of a FastLoad file.
+ */
+struct nsFastLoadHeader {
+ char mMagic[MFL_FILE_MAGIC_SIZE];
+ PRUint32 mChecksum;
+ PRUint32 mVersion;
+ PRUint32 mFooterOffset;
+ PRUint32 mFileSize;
+};
+
+/**
+ * Footer prefix structure (footer header, ugh), after which come arrays of
+ * structures or strings counted by these members.
+ */
+struct nsFastLoadFooterPrefix {
+ PRUint32 mNumIDs;
+ PRUint32 mNumSharpObjects;
+ PRUint32 mNumMuxedDocuments;
+ PRUint32 mNumDependencies;
+};
+
+struct nsFastLoadSharpObjectInfo {
+ PRUint32 mCIDOffset; // offset of object's NSFastLoadID and data
+ PRUint16 mStrongRefCnt;
+ PRUint16 mWeakRefCnt; // high bit is singleton flag, see below
+};
+
+#define MFL_SINGLETON_FLAG 0x8000
+#define MFL_WEAK_REFCNT_MASK 0x7fff
+
+#define MFL_GET_SINGLETON_FLAG(ip) ((ip)->mWeakRefCnt & MFL_SINGLETON_FLAG)
+#define MFL_GET_WEAK_REFCNT(ip) ((ip)->mWeakRefCnt & MFL_WEAK_REFCNT_MASK)
+
+#define MFL_SET_SINGLETON_FLAG(ip) \
+ ((ip)->mWeakRefCnt |= MFL_SINGLETON_FLAG)
+#define MFL_SET_WEAK_REFCNT(ip,rc) \
+ ((ip)->mWeakRefCnt = (((ip)->mWeakRefCnt & MFL_SINGLETON_FLAG) | (rc)))
+
+#define MFL_BUMP_WEAK_REFCNT(ip) (++(ip)->mWeakRefCnt)
+#define MFL_DROP_WEAK_REFCNT(ip) (--(ip)->mWeakRefCnt)
+
+struct nsFastLoadMuxedDocumentInfo {
+ const char* mURISpec;
+ PRUint32 mInitialSegmentOffset;
+};
+
+// forward declarations of opaque types defined in nsFastLoadFile.cpp
+struct nsDocumentMapReadEntry;
+struct nsDocumentMapWriteEntry;
+
+// So nsFastLoadFileUpdater can verify that its nsIObjectInputStream parameter
+// is an nsFastLoadFileReader.
+#define NS_FASTLOADFILEREADER_IID \
+ {0x7d37d1bb,0xcef3,0x4c5f,{0x97,0x68,0x0f,0x89,0x7f,0x1a,0xe1,0x40}}
+
+struct nsIFastLoadFileReader : public nsISupports {
+ NS_DEFINE_STATIC_IID_ACCESSOR(NS_FASTLOADFILEREADER_IID)
+};
+
+/**
+ * Inherit from the concrete class nsBinaryInputStream, which inherits from
+ * abstract nsIObjectInputStream but does not implement its direct methods.
+ * Though the names are not as clear as I'd like, this seems to be the best
+ * way to share nsBinaryStream.cpp code.
+ */
+class nsFastLoadFileReader
+ : public nsBinaryInputStream,
+ public nsIFastLoadReadControl,
+ public nsISeekableStream,
+ public nsIFastLoadFileReader
+{
+ public:
+ nsFastLoadFileReader(nsIInputStream *aStream)
+ : mCurrentDocumentMapEntry(nsnull) {
+ SetInputStream(aStream);
+ MOZ_COUNT_CTOR(nsFastLoadFileReader);
+ }
+
+ virtual ~nsFastLoadFileReader() {
+ MOZ_COUNT_DTOR(nsFastLoadFileReader);
+ }
+
+ private:
+ // nsISupports methods
+ NS_DECL_ISUPPORTS_INHERITED
+
+ // overridden nsIObjectInputStream methods
+ NS_IMETHOD ReadObject(PRBool aIsStrongRef, nsISupports* *_retval);
+ NS_IMETHOD ReadID(nsID *aResult);
+
+ // nsIFastLoadFileControl methods
+ NS_DECL_NSIFASTLOADFILECONTROL
+
+ // nsIFastLoadReadControl methods
+ NS_DECL_NSIFASTLOADREADCONTROL
+
+ // nsISeekableStream methods
+ NS_DECL_NSISEEKABLESTREAM
+
+ // Override Read so we can demultiplex a document interleaved with others.
+ NS_IMETHOD Read(char* aBuffer, PRUint32 aCount, PRUint32 *aBytesRead);
+
+ // Override ReadSegments too, as nsBinaryInputStream::ReadSegments does
+ // not call through our overridden Read method -- it calls directly into
+ // the underlying input stream.
+ NS_IMETHODIMP ReadSegments(nsWriteSegmentFun aWriter, void* aClosure,
+ PRUint32 aCount, PRUint32 *aResult);
+
+ nsresult ReadHeader(nsFastLoadHeader *aHeader);
+
+ /**
+ * In-memory representation of an indexed nsFastLoadSharpObjectInfo record.
+ */
+ struct nsObjectMapEntry : public nsFastLoadSharpObjectInfo {
+ nsCOMPtr<nsISupports> mReadObject;
+ PRInt64 mSkipOffset;
+ PRUint16 mSaveStrongRefCnt; // saved for an Update
+ PRUint16 mSaveWeakRefCnt; // after a Read
+ };
+
+ /**
+ * In-memory representation of the FastLoad file footer.
+ */
+ struct nsFastLoadFooter : public nsFastLoadFooterPrefix {
+ nsFastLoadFooter()
+ : mIDMap(nsnull),
+ mObjectMap(nsnull) {
+ mDocumentMap.ops = mURIMap.ops = nsnull;
+ }
+
+ ~nsFastLoadFooter() {
+ delete[] mIDMap;
+ delete[] mObjectMap;
+ if (mDocumentMap.ops)
+ PL_DHashTableFinish(&mDocumentMap);
+ if (mURIMap.ops)
+ PL_DHashTableFinish(&mURIMap);
+ }
+
+ // These can't be static within GetID and GetSharpObjectEntry or the
+ // toolchains on HP-UX 10.20's, RH 7.0, and Mac OS X all barf at link
+ // time ("common symbols not allowed with MY_DHLIB output format", to
+ // quote the OS X rev of gcc).
+ static nsID gDummyID;
+ static nsObjectMapEntry gDummySharpObjectEntry;
+
+ const nsID& GetID(NSFastLoadID aFastId) const {
+ PRUint32 index = aFastId - 1;
+ NS_ASSERTION(index < mNumIDs, "aFastId out of range");
+ if (index >= mNumIDs)
+ return gDummyID;
+ return mIDMap[index];
+ }
+
+ nsObjectMapEntry&
+ GetSharpObjectEntry(NSFastLoadOID aOID) const {
+ PRUint32 index = MFL_OID_TO_SHARP_INDEX(aOID);
+ NS_ASSERTION(index < mNumSharpObjects, "aOID out of range");
+ if (index >= mNumSharpObjects)
+ return gDummySharpObjectEntry;
+ return mObjectMap[index];
+ }
+
+ // Map from dense, zero-based, uint32 NSFastLoadID to 16-byte nsID.
+ nsID* mIDMap;
+
+ // Map from dense, zero-based MFL_OID_TO_SHARP_INDEX(oid) to sharp
+ // object offset and refcnt information.
+ nsObjectMapEntry* mObjectMap;
+
+ // Map from URI spec string to nsDocumentMapReadEntry, which helps us
+ // demultiplex a document's objects from among the interleaved object
+ // stream segments in the FastLoad file.
+ PLDHashTable mDocumentMap;
+
+ // Fast mapping from URI object pointer to mDocumentMap entry, valid
+ // only while the muxed document is loading.
+ PLDHashTable mURIMap;
+
+ // List of source filename dependencies that should trigger regeneration
+ // of the FastLoad file.
+ nsCOMPtr<nsISupportsArray> mDependencies;
+ };
+
+ nsresult ReadFooter(nsFastLoadFooter *aFooter);
+ nsresult ReadFooterPrefix(nsFastLoadFooterPrefix *aFooterPrefix);
+ nsresult ReadSlowID(nsID *aID);
+ nsresult ReadFastID(NSFastLoadID *aID);
+ nsresult ReadSharpObjectInfo(nsFastLoadSharpObjectInfo *aInfo);
+ nsresult ReadMuxedDocumentInfo(nsFastLoadMuxedDocumentInfo *aInfo);
+ nsresult DeserializeObject(nsISupports* *aObject);
+
+ nsresult Open();
+ NS_IMETHOD Close();
+
+ protected:
+ nsFastLoadHeader mHeader;
+ nsFastLoadFooter mFooter;
+
+ nsDocumentMapReadEntry* mCurrentDocumentMapEntry;
+
+ friend class nsFastLoadFileUpdater;
+};
+
+NS_COM nsresult
+NS_NewFastLoadFileReader(nsIObjectInputStream* *aResult,
+ nsIInputStream* aSrcStream);
+
+/**
+ * Inherit from the concrete class nsBinaryInputStream, which inherits from
+ * abstract nsIObjectInputStream but does not implement its direct methods.
+ * Though the names are not as clear as I'd like, this seems to be the best
+ * way to share nsBinaryStream.cpp code.
+ */
+class nsFastLoadFileWriter
+ : public nsBinaryOutputStream,
+ public nsIFastLoadWriteControl,
+ public nsISeekableStream
+{
+ public:
+ nsFastLoadFileWriter(nsIOutputStream *aStream, nsIFastLoadFileIO* aFileIO)
+ : mCurrentDocumentMapEntry(nsnull),
+ mFileIO(aFileIO)
+ {
+ SetOutputStream(aStream);
+ mHeader.mChecksum = 0;
+ mIDMap.ops = mObjectMap.ops = mDocumentMap.ops = mURIMap.ops = nsnull;
+ mDependencyMap.ops = nsnull;
+ MOZ_COUNT_CTOR(nsFastLoadFileWriter);
+ }
+
+ virtual ~nsFastLoadFileWriter()
+ {
+ if (mIDMap.ops)
+ PL_DHashTableFinish(&mIDMap);
+ if (mObjectMap.ops)
+ PL_DHashTableFinish(&mObjectMap);
+ if (mDocumentMap.ops)
+ PL_DHashTableFinish(&mDocumentMap);
+ if (mURIMap.ops)
+ PL_DHashTableFinish(&mURIMap);
+ if (mDependencyMap.ops)
+ PL_DHashTableFinish(&mDependencyMap);
+ MOZ_COUNT_DTOR(nsFastLoadFileWriter);
+ }
+
+ private:
+ // nsISupports methods
+ NS_DECL_ISUPPORTS_INHERITED
+
+ // overridden nsIObjectOutputStream methods
+ NS_IMETHOD WriteObject(nsISupports* aObject, PRBool aIsStrongRef);
+ NS_IMETHOD WriteSingleRefObject(nsISupports* aObject);
+ NS_IMETHOD WriteCompoundObject(nsISupports* aObject,
+ const nsIID& aIID,
+ PRBool aIsStrongRef);
+ NS_IMETHOD WriteID(const nsID& aID);
+
+ // nsIFastLoadFileControl methods
+ NS_DECL_NSIFASTLOADFILECONTROL
+
+ // nsIFastLoadWriteControl methods
+ NS_DECL_NSIFASTLOADWRITECONTROL
+
+ // nsISeekableStream methods
+ NS_DECL_NSISEEKABLESTREAM
+
+ nsresult MapID(const nsID& aSlowID, NSFastLoadID *aResult);
+
+ nsresult WriteHeader(nsFastLoadHeader *aHeader);
+ nsresult WriteFooter();
+ nsresult WriteFooterPrefix(const nsFastLoadFooterPrefix& aFooterPrefix);
+ nsresult WriteSlowID(const nsID& aID);
+ nsresult WriteFastID(NSFastLoadID aID);
+ nsresult WriteSharpObjectInfo(const nsFastLoadSharpObjectInfo& aInfo);
+ nsresult WriteMuxedDocumentInfo(const nsFastLoadMuxedDocumentInfo& aInfo);
+
+ nsresult Init();
+ nsresult Open();
+ NS_IMETHOD Close();
+
+ nsresult WriteObjectCommon(nsISupports* aObject,
+ PRBool aIsStrongRef,
+ PRUint32 aQITag);
+
+ static PLDHashOperator PR_CALLBACK
+ IDMapEnumerate(PLDHashTable *aTable,
+ PLDHashEntryHdr *aHdr,
+ PRUint32 aNumber,
+ void *aData);
+
+ static PLDHashOperator PR_CALLBACK
+ ObjectMapEnumerate(PLDHashTable *aTable,
+ PLDHashEntryHdr *aHdr,
+ PRUint32 aNumber,
+ void *aData);
+
+ static PLDHashOperator PR_CALLBACK
+ DocumentMapEnumerate(PLDHashTable *aTable,
+ PLDHashEntryHdr *aHdr,
+ PRUint32 aNumber,
+ void *aData);
+
+ static PLDHashOperator PR_CALLBACK
+ DependencyMapEnumerate(PLDHashTable *aTable,
+ PLDHashEntryHdr *aHdr,
+ PRUint32 aNumber,
+ void *aData);
+
+ protected:
+ nsFastLoadHeader mHeader;
+
+ PLDHashTable mIDMap;
+ PLDHashTable mObjectMap;
+ PLDHashTable mDocumentMap;
+ PLDHashTable mURIMap;
+ PLDHashTable mDependencyMap;
+
+ nsDocumentMapWriteEntry* mCurrentDocumentMapEntry;
+ nsCOMPtr<nsIFastLoadFileIO> mFileIO;
+};
+
+NS_COM nsresult
+NS_NewFastLoadFileWriter(nsIObjectOutputStream* *aResult,
+ nsIOutputStream* aDestStream,
+ nsIFastLoadFileIO* aFileIO);
+
+/**
+ * Subclass of nsFastLoadFileWriter, friend of nsFastLoadFileReader which it
+ * wraps when a FastLoad file needs to be updated. The wrapped reader can be
+ * used to demulitplex data for documents already in the FastLoad file, while
+ * the updater writes new data over the old footer, then writes a new footer
+ * that maps all data on Close.
+ */
+class nsFastLoadFileUpdater
+ : public nsFastLoadFileWriter,
+ nsIFastLoadFileIO
+{
+ public:
+ nsFastLoadFileUpdater(nsIOutputStream* aOutputStream)
+ : nsFastLoadFileWriter(aOutputStream, nsnull) {
+ MOZ_COUNT_CTOR(nsFastLoadFileUpdater);
+ }
+
+ virtual ~nsFastLoadFileUpdater() {
+ MOZ_COUNT_DTOR(nsFastLoadFileUpdater);
+ }
+
+ private:
+ // nsISupports methods
+ NS_DECL_ISUPPORTS_INHERITED
+
+ // nsIFastLoadFileIO methods
+ NS_DECL_NSIFASTLOADFILEIO
+
+ nsresult Open(nsFastLoadFileReader* aReader);
+ NS_IMETHOD Close();
+
+ static PLDHashOperator PR_CALLBACK
+ CopyReadDocumentMapEntryToUpdater(PLDHashTable *aTable,
+ PLDHashEntryHdr *aHdr,
+ PRUint32 aNumber,
+ void *aData);
+
+ friend class nsFastLoadFileReader;
+
+ protected:
+ nsCOMPtr<nsIInputStream> mInputStream;
+};
+
+NS_COM nsresult
+NS_NewFastLoadFileUpdater(nsIObjectOutputStream* *aResult,
+ nsIOutputStream* aOutputStream,
+ nsIObjectInputStream* aReaderAsStream);
+
+#endif // nsFastLoadFile_h___
diff --git a/src/libs/xpcom18a4/xpcom/io/nsFastLoadPtr.h b/src/libs/xpcom18a4/xpcom/io/nsFastLoadPtr.h
new file mode 100644
index 00000000..0c832357
--- /dev/null
+++ b/src/libs/xpcom18a4/xpcom/io/nsFastLoadPtr.h
@@ -0,0 +1,112 @@
+/* -*- 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 FastLoad code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2001
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Brendan Eich <brendan@mozilla.org> (original author)
+ *
+ * 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 ***** */
+
+#ifndef nsFastLoadPtr_h___
+#define nsFastLoadPtr_h___
+
+/**
+ * Mozilla FastLoad file object pointer template type.
+ *
+ * Use nsFastLoadPtr<T> rather than nsCOMPtr<T> when declaring a strong XPCOM
+ * ref member of a data structure that's conditionally loaded at application
+ * startup. You must be willing to tolerate the null mRawPtr test on every
+ * dereference of this member pointer, or else copy it to a local to optimize
+ * away the cost.
+ */
+
+#ifndef nsCOMPtr_h___
+#include "nsCOMPtr.h"
+#endif
+
+#ifndef nsIFastLoadService_h___
+#include "nsIFastLoadService.h"
+#endif
+
+#ifdef VBOX_WITH_XPCOM_NAMESPACE_CLEANUP
+#define gFastLoadService_ VBoxNsxpgFastLoadService_
+#endif /* VBOX_WITH_XPCOM_NAMESPACE_CLEANUP */
+
+/**
+ * nsFastLoadPtr is a template class, so we don't want a class static service
+ * pointer member declared in nsFastLoadPtr, above. Plus, we need special
+ * declaration magic to export data across DLL/DSO boundaries. So we use an
+ * old-fashioned global variable that refers weakly to the one true FastLoad
+ * service. This pointer is maintained by that singleton's ctor and dtor.
+ */
+PR_EXPORT_DATA(nsIFastLoadService*) gFastLoadService_;
+
+template <class T>
+class nsFastLoadPtr : public nsCOMPtr<T> {
+ public:
+ nsDerivedSafe<T>* get() const {
+ if (!this->mRawPtr) {
+ gFastLoadService_->GetFastLoadReferent(
+ NS_REINTERPRET_CAST(nsISupports**,
+ &this->mRawPtr));
+ }
+ return NS_REINTERPRET_CAST(nsDerivedSafe<T>*, this->mRawPtr);
+ }
+
+ /**
+ * Deserialize an nsFastLoadPtr from aInputStream, skipping the referent
+ * object, but saving the object's offset for later deserialization.
+ *
+ * Lowercase name _a la_ get, because it's called the same way -- not via
+ * operator->().
+ */
+ nsresult read(nsIObjectInputStream* aInputStream) {
+ return gFastLoadService_->ReadFastLoadPtr(aInputStream,
+ NS_REINTERPRET_CAST(nsISupports**,
+ &this->mRawPtr));
+ }
+
+ /**
+ * Serialize an nsFastLoadPtr reference and possibly the referent object,
+ * if that object has not yet been serialized.
+ *
+ * Lowercase name _a la_ get, because it's called the same way -- not via
+ * operator->().
+ */
+ nsresult write(nsIObjectOutputStream* aOutputStream) {
+ return gFastLoadService_->WriteFastLoadPtr(aOutputStream,
+ NS_REINTERPRET_CAST(nsISupports*,
+ this->mRawPtr));
+ }
+};
+
+#endif // nsFastLoadPtr_h___
diff --git a/src/libs/xpcom18a4/xpcom/io/nsFastLoadService.cpp b/src/libs/xpcom18a4/xpcom/io/nsFastLoadService.cpp
new file mode 100644
index 00000000..4df187c1
--- /dev/null
+++ b/src/libs/xpcom18a4/xpcom/io/nsFastLoadService.cpp
@@ -0,0 +1,571 @@
+/* -*- 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 FastLoad code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2001
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Brendan Eich <brendan@mozilla.org> (original author)
+ *
+ * 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 "prtypes.h"
+#include "prio.h"
+#include "prtime.h"
+#include "pldhash.h"
+
+#include "nsAppDirectoryServiceDefs.h"
+#include "nsAutoLock.h"
+#include "nsCOMPtr.h"
+#include "nsFastLoadFile.h"
+#include "nsFastLoadPtr.h"
+#include "nsFastLoadService.h"
+#include "nsString.h"
+
+#include "nsIComponentManager.h"
+#include "nsIEnumerator.h"
+#include "nsIFastLoadFileControl.h"
+#include "nsIFile.h"
+#include "nsIObjectInputStream.h"
+#include "nsIObjectOutputStream.h"
+#include "nsISeekableStream.h"
+#include "nsISupports.h"
+
+PR_IMPLEMENT_DATA(nsIFastLoadService*) gFastLoadService_ = nsnull;
+
+NS_IMPL_THREADSAFE_ISUPPORTS1(nsFastLoadService, nsIFastLoadService)
+
+nsFastLoadService::nsFastLoadService()
+ : mLock(nsnull),
+ mFastLoadPtrMap(nsnull),
+ mDirection(0)
+{
+ NS_ASSERTION(gFastLoadService_ == nsnull, "double FastLoadService init?");
+ gFastLoadService_ = this;
+}
+
+nsFastLoadService::~nsFastLoadService()
+{
+ gFastLoadService_ = nsnull;
+
+ if (mInputStream)
+ mInputStream->Close();
+ if (mOutputStream)
+ mOutputStream->Close();
+
+ if (mFastLoadPtrMap)
+ PL_DHashTableDestroy(mFastLoadPtrMap);
+ if (mLock)
+ PR_DestroyLock(mLock);
+}
+
+NS_IMETHODIMP
+nsFastLoadService::Create(nsISupports *aOuter, REFNSIID aIID, void **aResult)
+{
+ *aResult = nsnull;
+ if (aOuter)
+ return NS_ERROR_NO_AGGREGATION;
+
+ nsFastLoadService* fastLoadService = new nsFastLoadService();
+ if (!fastLoadService)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ fastLoadService->mLock = PR_NewLock();
+ if (!fastLoadService->mLock) {
+ delete fastLoadService;
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ NS_ADDREF(fastLoadService);
+ nsresult rv = fastLoadService->QueryInterface(aIID, aResult);
+ NS_RELEASE(fastLoadService);
+ return rv;
+}
+
+#if defined XP_MAC
+
+// Mac format: "<Basename> FastLoad File" with <basename> capitalized.
+# include "nsCRT.h"
+
+# define MASSAGE_BASENAME(bn) (bn.SetCharAt(nsCRT::ToUpper(bn.CharAt(0)), 0))
+# define PLATFORM_FASL_SUFFIX " FastLoad File"
+
+#elif defined(XP_UNIX) || defined(XP_BEOS)
+
+// Unix format: "<basename>.mfasl".
+# define MASSAGE_BASENAME(bn) /* nothing */
+# define PLATFORM_FASL_SUFFIX ".mfasl"
+
+#elif defined(XP_WIN) || defined(XP_OS2)
+
+// Windows format: "<basename>.mfl".
+# define MASSAGE_BASENAME(bn) /* nothing */
+# define PLATFORM_FASL_SUFFIX ".mfl"
+
+#endif
+
+nsresult
+nsFastLoadService::NewFastLoadFile(const char* aBaseName, nsIFile* *aResult)
+{
+ nsresult rv;
+ nsCOMPtr<nsIFile> file;
+
+ rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
+ getter_AddRefs(file));
+ if (NS_FAILED(rv))
+ return rv;
+
+ nsCAutoString name(aBaseName);
+ MASSAGE_BASENAME(name);
+ name += PLATFORM_FASL_SUFFIX;
+ rv = file->AppendNative(name);
+ if (NS_FAILED(rv))
+ return rv;
+
+ *aResult = file;
+ NS_ADDREF(*aResult);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsFastLoadService::NewInputStream(nsIInputStream* aSrcStream,
+ nsIObjectInputStream* *aResult)
+{
+ nsAutoLock lock(mLock);
+
+ nsCOMPtr<nsIObjectInputStream> stream;
+ nsresult rv = NS_NewFastLoadFileReader(getter_AddRefs(stream), aSrcStream);
+ if (NS_FAILED(rv))
+ return rv;
+
+ *aResult = stream;
+ NS_ADDREF(*aResult);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsFastLoadService::NewOutputStream(nsIOutputStream* aDestStream,
+ nsIObjectOutputStream* *aResult)
+{
+ nsAutoLock lock(mLock);
+
+ return NS_NewFastLoadFileWriter(aResult, aDestStream, mFileIO);
+}
+
+NS_IMETHODIMP
+nsFastLoadService::GetInputStream(nsIObjectInputStream* *aResult)
+{
+ NS_IF_ADDREF(*aResult = mInputStream);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsFastLoadService::SetInputStream(nsIObjectInputStream* aStream)
+{
+ nsAutoLock lock(mLock);
+ mInputStream = aStream;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsFastLoadService::GetOutputStream(nsIObjectOutputStream* *aResult)
+{
+ NS_IF_ADDREF(*aResult = mOutputStream);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsFastLoadService::SetOutputStream(nsIObjectOutputStream* aStream)
+{
+ nsAutoLock lock(mLock);
+ mOutputStream = aStream;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsFastLoadService::GetFileIO(nsIFastLoadFileIO* *aResult)
+{
+ NS_IF_ADDREF(*aResult = mFileIO);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsFastLoadService::SetFileIO(nsIFastLoadFileIO* aFileIO)
+{
+ nsAutoLock lock(mLock);
+ mFileIO = aFileIO;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsFastLoadService::GetDirection(PRInt32 *aResult)
+{
+ *aResult = mDirection;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsFastLoadService::HasMuxedDocument(const char* aURISpec, PRBool *aResult)
+{
+ nsresult rv = NS_ERROR_NOT_AVAILABLE;
+ nsCOMPtr<nsIFastLoadFileControl> control;
+
+ *aResult = PR_FALSE;
+ nsAutoLock lock(mLock);
+
+ if (mInputStream) {
+ control = do_QueryInterface(mInputStream);
+ if (control)
+ rv = control->HasMuxedDocument(aURISpec, aResult);
+ }
+
+ if (! *aResult && mOutputStream) {
+ control = do_QueryInterface(mOutputStream);
+ if (control)
+ rv = control->HasMuxedDocument(aURISpec, aResult);
+ }
+
+ return rv;
+}
+
+NS_IMETHODIMP
+nsFastLoadService::StartMuxedDocument(nsISupports* aURI, const char* aURISpec,
+ PRInt32 aDirectionFlags)
+{
+ nsresult rv = NS_ERROR_NOT_AVAILABLE;
+ nsCOMPtr<nsIFastLoadFileControl> control;
+ nsAutoLock lock(mLock);
+
+ // Try for an input stream first, in case aURISpec's data is multiplexed
+ // in the current FastLoad file.
+ if ((aDirectionFlags & NS_FASTLOAD_READ) && mInputStream) {
+ control = do_QueryInterface(mInputStream);
+ if (control) {
+ // If aURISpec is not in the multiplex, control->StartMuxedDocument
+ // will return NS_ERROR_NOT_AVAILABLE.
+ rv = control->StartMuxedDocument(aURI, aURISpec);
+ if (NS_SUCCEEDED(rv) || rv != NS_ERROR_NOT_AVAILABLE)
+ return rv;
+
+ // Ok, aURISpec is not in the existing mux. If we have no output
+ // stream yet, wrap the reader with a FastLoad file updater.
+ if (!mOutputStream && mFileIO) {
+ nsCOMPtr<nsIOutputStream> output;
+ rv = mFileIO->GetOutputStream(getter_AddRefs(output));
+ if (NS_FAILED(rv))
+ return rv;
+
+ // NB: mInputStream must be an nsFastLoadFileReader!
+ rv = NS_NewFastLoadFileUpdater(getter_AddRefs(mOutputStream),
+ output,
+ mInputStream);
+ if (NS_FAILED(rv))
+ return rv;
+ }
+
+ if (aDirectionFlags == NS_FASTLOAD_READ) {
+ // Tell our caller to re-start multiplexing, rather than attempt
+ // to select and deserialize now.
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+ }
+ }
+
+ if ((aDirectionFlags & NS_FASTLOAD_WRITE) && mOutputStream) {
+ control = do_QueryInterface(mOutputStream);
+ if (control)
+ rv = control->StartMuxedDocument(aURI, aURISpec);
+ }
+ return rv;
+}
+
+NS_IMETHODIMP
+nsFastLoadService::SelectMuxedDocument(nsISupports* aURI, nsISupports** aResult)
+{
+ nsresult rv = NS_ERROR_NOT_AVAILABLE;
+ nsCOMPtr<nsIFastLoadFileControl> control;
+ nsAutoLock lock(mLock);
+
+ // Try to select the reader, if any; then only if the URI was not in the
+ // file already, select the writer/updater.
+ if (mInputStream) {
+ control = do_QueryInterface(mInputStream);
+ if (control) {
+ rv = control->SelectMuxedDocument(aURI, aResult);
+ if (NS_SUCCEEDED(rv))
+ mDirection = NS_FASTLOAD_READ;
+ }
+ }
+
+ if (rv == NS_ERROR_NOT_AVAILABLE && mOutputStream) {
+ control = do_QueryInterface(mOutputStream);
+ if (control) {
+ rv = control->SelectMuxedDocument(aURI, aResult);
+ if (NS_SUCCEEDED(rv))
+ mDirection = NS_FASTLOAD_WRITE;
+ }
+ }
+
+ return rv;
+}
+
+NS_IMETHODIMP
+nsFastLoadService::EndMuxedDocument(nsISupports* aURI)
+{
+ nsresult rv = NS_ERROR_NOT_AVAILABLE;
+ nsCOMPtr<nsIFastLoadFileControl> control;
+ nsAutoLock lock(mLock);
+
+ // Try to end the document identified by aURI in the reader, if any; then
+ // only if the URI was not in the file already, end the writer/updater.
+ if (mInputStream) {
+ control = do_QueryInterface(mInputStream);
+ if (control)
+ rv = control->EndMuxedDocument(aURI);
+ }
+
+ if (rv == NS_ERROR_NOT_AVAILABLE && mOutputStream) {
+ control = do_QueryInterface(mOutputStream);
+ if (control)
+ rv = control->EndMuxedDocument(aURI);
+ }
+
+ mDirection = 0;
+ return rv;
+}
+
+NS_IMETHODIMP
+nsFastLoadService::AddDependency(nsIFile* aFile)
+{
+ nsAutoLock lock(mLock);
+
+ nsCOMPtr<nsIFastLoadWriteControl> control(do_QueryInterface(mOutputStream));
+ if (!control)
+ return NS_ERROR_NOT_AVAILABLE;
+
+ return control->AddDependency(aFile);
+}
+
+NS_IMETHODIMP
+nsFastLoadService::ComputeChecksum(nsIFile* aFile,
+ nsIFastLoadReadControl* aControl,
+ PRUint32 *aChecksum)
+{
+ nsCAutoString path;
+ nsresult rv = aFile->GetNativePath(path);
+ if (NS_FAILED(rv))
+ return rv;
+
+ nsCStringKey key(path);
+ PRUint32 checksum = NS_PTR_TO_INT32(mChecksumTable.Get(&key));
+ if (checksum) {
+ *aChecksum = checksum;
+ return NS_OK;
+ }
+
+ rv = aControl->ComputeChecksum(&checksum);
+ if (NS_FAILED(rv))
+ return rv;
+
+#ifndef VBOX
+ mChecksumTable.Put(&key, NS_INT32_TO_PTR(checksum));
+#else /* VBOX */
+ mChecksumTable.Put(&key, (void *)(uintptr_t)checksum);
+#endif /* VBOX */
+ *aChecksum = checksum;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsFastLoadService::CacheChecksum(nsIFile* aFile, nsIObjectOutputStream *aStream)
+{
+ nsCOMPtr<nsIFastLoadFileControl> control(do_QueryInterface(aStream));
+ if (!control)
+ return NS_ERROR_FAILURE;
+
+ PRUint32 checksum;
+ nsresult rv = control->GetChecksum(&checksum);
+ if (NS_FAILED(rv))
+ return rv;
+
+ nsCAutoString path;
+ rv = aFile->GetNativePath(path);
+ if (NS_FAILED(rv))
+ return rv;
+
+ nsCStringKey key(path);
+#ifndef VBOX
+ mChecksumTable.Put(&key, NS_INT32_TO_PTR(checksum));
+#else /* VBOX */
+ mChecksumTable.Put(&key, (void *)(uintptr_t)checksum);
+#endif /* VBOX */
+ return NS_OK;
+}
+
+struct nsFastLoadPtrEntry : public PLDHashEntryHdr {
+ nsISupports** mPtrAddr; // key, must come first for PL_DHashGetStubOps
+ PRUint32 mOffset;
+};
+
+NS_IMETHODIMP
+nsFastLoadService::GetFastLoadReferent(nsISupports* *aPtrAddr)
+{
+ NS_ASSERTION(*aPtrAddr == nsnull,
+ "aPtrAddr doesn't point to null nsFastLoadPtr<T>::mRawAddr?");
+
+ nsAutoLock lock(mLock);
+ if (!mFastLoadPtrMap || !mInputStream)
+ return NS_OK;
+
+ nsFastLoadPtrEntry* entry =
+ NS_STATIC_CAST(nsFastLoadPtrEntry*,
+ PL_DHashTableOperate(mFastLoadPtrMap, aPtrAddr,
+ PL_DHASH_LOOKUP));
+ if (PL_DHASH_ENTRY_IS_FREE(entry))
+ return NS_OK;
+
+ nsresult rv;
+ nsCOMPtr<nsISeekableStream> seekable(do_QueryInterface(mInputStream));
+
+ rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET, entry->mOffset);
+ if (NS_FAILED(rv))
+ return rv;
+
+ rv = mInputStream->ReadObject(PR_TRUE, aPtrAddr);
+ if (NS_FAILED(rv))
+ return rv;
+
+ // Shrink the table if half the entries are removed sentinels.
+ PRUint32 size = PL_DHASH_TABLE_SIZE(mFastLoadPtrMap);
+ if (mFastLoadPtrMap->removedCount >= (size >> 2))
+ PL_DHashTableOperate(mFastLoadPtrMap, entry, PL_DHASH_REMOVE);
+ else
+ PL_DHashTableRawRemove(mFastLoadPtrMap, entry);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsFastLoadService::ReadFastLoadPtr(nsIObjectInputStream* aInputStream,
+ nsISupports* *aPtrAddr)
+{
+ // nsFastLoadPtrs self-construct to null, so if we have a non-null value
+ // in our inout parameter, we must have been read already, alright!
+ if (*aPtrAddr)
+ return NS_OK;
+
+ nsresult rv;
+ PRUint32 nextOffset;
+ nsAutoLock lock(mLock);
+
+ rv = aInputStream->Read32(&nextOffset);
+ if (NS_FAILED(rv))
+ return rv;
+
+ nsCOMPtr<nsISeekableStream> seekable(do_QueryInterface(aInputStream));
+ if (!seekable)
+ return NS_ERROR_FAILURE;
+
+ PRInt64 thisOffset;
+ rv = seekable->Tell(&thisOffset);
+ if (NS_FAILED(rv))
+ return rv;
+
+ rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET, nextOffset);
+ if (NS_FAILED(rv))
+ return rv;
+
+ if (!mFastLoadPtrMap) {
+ mFastLoadPtrMap = PL_NewDHashTable(PL_DHashGetStubOps(), this,
+ sizeof(nsFastLoadPtrEntry),
+ PL_DHASH_MIN_SIZE);
+ if (!mFastLoadPtrMap)
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ nsFastLoadPtrEntry* entry =
+ NS_STATIC_CAST(nsFastLoadPtrEntry*,
+ PL_DHashTableOperate(mFastLoadPtrMap, aPtrAddr,
+ PL_DHASH_ADD));
+ NS_ASSERTION(entry->mPtrAddr == nsnull, "duplicate nsFastLoadPtr?!");
+
+ entry->mPtrAddr = aPtrAddr;
+
+ LL_L2UI(entry->mOffset, thisOffset);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsFastLoadService::WriteFastLoadPtr(nsIObjectOutputStream* aOutputStream,
+ nsISupports* aObject)
+{
+ NS_ASSERTION(aObject != nsnull, "writing an unread nsFastLoadPtr?!");
+ if (!aObject)
+ return NS_ERROR_UNEXPECTED;
+
+ nsresult rv;
+ nsAutoLock lock(mLock); // serialize writes to aOutputStream
+
+ nsCOMPtr<nsISeekableStream> seekable(do_QueryInterface(aOutputStream));
+ if (!seekable)
+ return NS_ERROR_FAILURE;
+
+ PRInt64 saveOffset;
+ rv = seekable->Tell(&saveOffset);
+ if (NS_FAILED(rv))
+ return rv;
+
+ rv = aOutputStream->Write32(0); // nextOffset placeholder
+ if (NS_FAILED(rv))
+ return rv;
+
+ rv = aOutputStream->WriteObject(aObject, PR_TRUE);
+ if (NS_FAILED(rv))
+ return rv;
+
+ PRInt64 nextOffset;
+ rv = seekable->Tell(&nextOffset);
+ if (NS_FAILED(rv))
+ return rv;
+
+ rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET, saveOffset);
+ if (NS_FAILED(rv))
+ return rv;
+
+ rv = aOutputStream->Write32(nextOffset);
+ if (NS_FAILED(rv))
+ return rv;
+
+ rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET, nextOffset);
+ if (NS_FAILED(rv))
+ return rv;
+
+ return NS_OK;
+}
diff --git a/src/libs/xpcom18a4/xpcom/io/nsFastLoadService.h b/src/libs/xpcom18a4/xpcom/io/nsFastLoadService.h
new file mode 100644
index 00000000..8f1aa6ae
--- /dev/null
+++ b/src/libs/xpcom18a4/xpcom/io/nsFastLoadService.h
@@ -0,0 +1,71 @@
+/* -*- 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 FastLoad code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2001
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Brendan Eich <brendan@mozilla.org> (original author)
+ *
+ * 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 "prtypes.h"
+#include "pldhash.h"
+#include "nsCOMPtr.h"
+#include "nsHashtable.h"
+#include "nsIFastLoadService.h"
+#include "nsIObjectInputStream.h"
+#include "nsIObjectOutputStream.h"
+
+class nsFastLoadFileReader;
+class nsFastLoadFileWriter;
+
+class nsFastLoadService : public nsIFastLoadService
+{
+ public:
+ nsFastLoadService();
+ private:
+ ~nsFastLoadService();
+
+ public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIFASTLOADSERVICE
+
+ static NS_METHOD
+ Create(nsISupports *aOuter, REFNSIID aIID, void **aResult);
+
+ private:
+ PRLock* mLock;
+ PLDHashTable* mFastLoadPtrMap;
+ nsCOMPtr<nsIObjectInputStream> mInputStream;
+ nsCOMPtr<nsIObjectOutputStream> mOutputStream;
+ nsCOMPtr<nsIFastLoadFileIO> mFileIO;
+ PRInt32 mDirection;
+ nsHashtable mChecksumTable;
+};
diff --git a/src/libs/xpcom18a4/xpcom/io/nsIAsyncInputStream.idl b/src/libs/xpcom18a4/xpcom/io/nsIAsyncInputStream.idl
new file mode 100644
index 00000000..6ce0bb30
--- /dev/null
+++ b/src/libs/xpcom18a4/xpcom/io/nsIAsyncInputStream.idl
@@ -0,0 +1,136 @@
+/* ***** 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.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2002
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Darin Fisher <darin@netscape.com>
+ *
+ * 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 "nsIInputStream.idl"
+
+interface nsIInputStreamCallback;
+interface nsIEventTarget;
+
+/**
+ * If an input stream is non-blocking, it may return NS_BASE_STREAM_WOULD_BLOCK
+ * when read. The caller must then wait for the stream to have some data to
+ * read. If the stream implements nsIAsyncInputStream, then the caller can use
+ * this interface to request an asynchronous notification when the stream
+ * becomes readable or closed (via the AsyncWait method).
+ *
+ * While this interface is almost exclusively used with non-blocking streams, it
+ * is not necessary that nsIInputStream::isNonBlocking return true. Nor is it
+ * necessary that a non-blocking nsIInputStream implementation also implement
+ * nsIAsyncInputStream.
+ */
+[scriptable, uuid(15a15329-00de-44e8-ab06-0d0b0d43dc5b)]
+interface nsIAsyncInputStream : nsIInputStream
+{
+ /**
+ * This method closes the stream and sets its internal status. If the
+ * stream is already closed, then this method is ignored. Once the stream
+ * is closed, the stream's status cannot be changed. Any successful status
+ * code passed to this method is treated as NS_BASE_STREAM_CLOSED, which
+ * has an effect equivalent to nsIInputStream::close.
+ *
+ * NOTE: this method exists in part to support pipes, which have both an
+ * input end and an output end. If the input end of a pipe is closed, then
+ * writes to the output end of the pipe will fail. The error code returned
+ * when an attempt is made to write to a "broken" pipe corresponds to the
+ * status code passed in when the input end of the pipe was closed, which
+ * greatly simplifies working with pipes in some cases.
+ *
+ * @param aStatus
+ * The error that will be reported if this stream is accessed after
+ * it has been closed.
+ */
+ void closeWithStatus(in nsresult aStatus);
+
+ /**
+ * Asynchronously wait for the stream to be readable or closed. The
+ * notification is one-shot, meaning that each asyncWait call will result
+ * in exactly one notification callback. After the OnInputStreamReady event
+ * is dispatched, the stream releases its reference to the
+ * nsIInputStreamCallback object. It is safe to call asyncWait again from the
+ * notification handler.
+ *
+ * This method may be called at any time (even if read has not been called).
+ * In other words, this method may be called when the stream already has
+ * data to read. It may also be called when the stream is closed. If the
+ * stream is already readable or closed when AsyncWait is called, then the
+ * OnInputStreamReady event will be dispatched immediately. Otherwise, the
+ * event will be dispatched when the stream becomes readable or closed.
+ *
+ * @param aCallback
+ * This object is notified when the stream becomes ready.
+ * @param aFlags
+ * This parameter specifies optional flags passed in to configure
+ * the behavior of this method. Pass zero to specify no flags.
+ * @param aRequestedCount
+ * Wait until at least this many bytes can be read. This is only
+ * a suggestion to the underlying stream; it may be ignored. The
+ * caller may pass zero to indicate no preference.
+ * @param aEventTarget
+ * Specify NULL to receive notification on ANY thread (possibly even
+ * recursively on the calling thread -- i.e., synchronously), or
+ * specify that the notification be delivered to a specific event
+ * target.
+ */
+ void asyncWait(in nsIInputStreamCallback aCallback,
+ in unsigned long aFlags,
+ in unsigned long aRequestedCount,
+ in nsIEventTarget aEventTarget);
+
+ /**
+ * If passed to asyncWait, this flag overrides the default behavior,
+ * causing the OnInputStreamReady notification to be suppressed until the
+ * stream becomes closed (either as a result of closeWithStatus/close being
+ * called on the stream or possibly due to some error in the underlying
+ * stream).
+ */
+ const unsigned long WAIT_CLOSURE_ONLY = (1<<0);
+};
+
+/**
+ * This is a companion interface for nsIAsyncInputStream::asyncWait.
+ */
+[scriptable, uuid(d1f28e94-3a6e-4050-a5f5-2e81b1fc2a43)]
+interface nsIInputStreamCallback : nsISupports
+{
+ /**
+ * Called to indicate that the stream is either readable or closed.
+ *
+ * @param aStream
+ * The stream whose asyncWait method was called.
+ */
+ void onInputStreamReady(in nsIAsyncInputStream aStream);
+};
diff --git a/src/libs/xpcom18a4/xpcom/io/nsIAsyncOutputStream.idl b/src/libs/xpcom18a4/xpcom/io/nsIAsyncOutputStream.idl
new file mode 100644
index 00000000..b0dd9ec0
--- /dev/null
+++ b/src/libs/xpcom18a4/xpcom/io/nsIAsyncOutputStream.idl
@@ -0,0 +1,136 @@
+/* ***** 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.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2002
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Darin Fisher <darin@netscape.com>
+ *
+ * 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 "nsIOutputStream.idl"
+
+interface nsIOutputStreamCallback;
+interface nsIEventTarget;
+
+/**
+ * If an output stream is non-blocking, it may return NS_BASE_STREAM_WOULD_BLOCK
+ * when written to. The caller must then wait for the stream to become
+ * writable. If the stream implements nsIAsyncOutputStream, then the caller can
+ * use this interface to request an asynchronous notification when the stream
+ * becomes writable or closed (via the AsyncWait method).
+ *
+ * While this interface is almost exclusively used with non-blocking streams, it
+ * is not necessary that nsIOutputStream::isNonBlocking return true. Nor is it
+ * necessary that a non-blocking nsIOutputStream implementation also implement
+ * nsIAsyncOutputStream.
+ */
+[scriptable, uuid(10dc9c94-8aff-49c6-8af9-d7fdb7339dae)]
+interface nsIAsyncOutputStream : nsIOutputStream
+{
+ /**
+ * This method closes the stream and sets its internal status. If the
+ * stream is already closed, then this method is ignored. Once the stream
+ * is closed, the stream's status cannot be changed. Any successful status
+ * code passed to this method is treated as NS_BASE_STREAM_CLOSED, which
+ * is equivalent to nsIInputStream::close.
+ *
+ * NOTE: this method exists in part to support pipes, which have both an
+ * input end and an output end. If the output end of a pipe is closed, then
+ * reads from the input end of the pipe will fail. The error code returned
+ * when an attempt is made to read from a "closed" pipe corresponds to the
+ * status code passed in when the output end of the pipe is closed, which
+ * greatly simplifies working with pipes in some cases.
+ *
+ * @param aStatus
+ * The error that will be reported if this stream is accessed after
+ * it has been closed.
+ */
+ void closeWithStatus(in nsresult reason);
+
+ /**
+ * Asynchronously wait for the stream to be writable or closed. The
+ * notification is one-shot, meaning that each asyncWait call will result
+ * in exactly one notification callback. After the OnOutputStreamReady event
+ * is dispatched, the stream releases its reference to the
+ * nsIOutputStreamCallback object. It is safe to call asyncWait again from the
+ * notification handler.
+ *
+ * This method may be called at any time (even if write has not been called).
+ * In other words, this method may be called when the stream already has
+ * room for more data. It may also be called when the stream is closed. If
+ * the stream is already writable or closed when AsyncWait is called, then the
+ * OnOutputStreamReady event will be dispatched immediately. Otherwise, the
+ * event will be dispatched when the stream becomes writable or closed.
+ *
+ * @param aCallback
+ * This object is notified when the stream becomes ready.
+ * @param aFlags
+ * This parameter specifies optional flags passed in to configure
+ * the behavior of this method. Pass zero to specify no flags.
+ * @param aRequestedCount
+ * Wait until at least this many bytes can be written. This is only
+ * a suggestion to the underlying stream; it may be ignored. The
+ * caller may pass zero to indicate no preference.
+ * @param aEventTarget
+ * Specify NULL to receive notification on ANY thread (possibly even
+ * recursively on the calling thread -- i.e., synchronously), or
+ * specify that the notification be delivered to a specific event
+ * target.
+ */
+ void asyncWait(in nsIOutputStreamCallback aCallback,
+ in unsigned long aFlags,
+ in unsigned long aRequestedCount,
+ in nsIEventTarget aEventTarget);
+
+ /**
+ * If passed to asyncWait, this flag overrides the default behavior,
+ * causing the OnOutputStreamReady notification to be suppressed until the
+ * stream becomes closed (either as a result of closeWithStatus/close being
+ * called on the stream or possibly due to some error in the underlying
+ * stream).
+ */
+ const unsigned long WAIT_CLOSURE_ONLY = (1<<0);
+};
+
+/**
+ * This is a companion interface for nsIAsyncOutputStream::asyncWait.
+ */
+[scriptable, uuid(40dbcdff-9053-42c5-a57c-3ec910d0f148)]
+interface nsIOutputStreamCallback : nsISupports
+{
+ /**
+ * Called to indicate that the stream is either writable or closed.
+ *
+ * @param aStream
+ * The stream whose asyncWait method was called.
+ */
+ void onOutputStreamReady(in nsIAsyncOutputStream aStream);
+};
diff --git a/src/libs/xpcom18a4/xpcom/io/nsIBaseStream.idl b/src/libs/xpcom18a4/xpcom/io/nsIBaseStream.idl
new file mode 100644
index 00000000..12e4c5f0
--- /dev/null
+++ b/src/libs/xpcom18a4/xpcom/io/nsIBaseStream.idl
@@ -0,0 +1,45 @@
+/* -*- Mode: C++; tab-width: 2; 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 ***** */
+
+#include "nsISupports.idl"
+
+[scriptable, uuid(6ccb17a0-e95e-11d1-beae-00805f8a66dc)]
+interface nsIBaseStream : nsISupports
+{
+ /** Close the stream. */
+ void close();
+};
diff --git a/src/libs/xpcom18a4/xpcom/io/nsIBinaryInputStream.idl b/src/libs/xpcom18a4/xpcom/io/nsIBinaryInputStream.idl
new file mode 100644
index 00000000..bc9ea0b5
--- /dev/null
+++ b/src/libs/xpcom18a4/xpcom/io/nsIBinaryInputStream.idl
@@ -0,0 +1,121 @@
+/* -*- 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, released
+ * March 31, 1998.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1998-1999
+ * 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 ***** */
+
+#include "nsIInputStream.idl"
+#include "nsrootidl.idl"
+
+/**
+ * This interface allows consumption of primitive data types from a "binary
+ * stream" containing untagged, big-endian binary data, i.e. as produced by an
+ * implementation of nsIBinaryOutputStream. This might be used, for example,
+ * to implement network protocols or to read from architecture-neutral disk
+ * files, i.e. ones that can be read and written by both big-endian and
+ * little-endian platforms.
+ *
+ * @See nsIBinaryOutputStream
+ */
+
+[scriptable, uuid(7b456cb0-8772-11d3-90cf-0040056a906e)]
+interface nsIBinaryInputStream : nsIInputStream {
+ void setInputStream(in nsIInputStream aInputStream);
+
+ PRBool readBoolean();
+
+ PRUint8 read8();
+ PRUint16 read16();
+ PRUint32 read32();
+ PRUint64 read64();
+
+ float readFloat();
+ double readDouble();
+
+ /**
+ * Read a NUL-terminated 8-bit char* string from a binary stream.
+ */
+ ACString readCString();
+
+ /**
+ * Read a NUL-terminated 16-bit PRUnichar* string from a binary stream.
+ */
+ AString readString();
+
+ /**
+ * Read an opaque byte array from a binary stream.
+ */
+ void readBytes(in PRUint32 aLength,
+ [size_is(aLength), retval] out string aString);
+
+ /**
+ * Read an opaque byte array from a binary stream, storing the results
+ * as an array of PRUint8s.
+ */
+ void readByteArray(in PRUint32 aLength,
+ [array, size_is(aLength), retval] out PRUint8 aBytes);
+};
+
+%{C++
+
+inline nsresult
+NS_ReadOptionalCString(nsIBinaryInputStream* aStream, nsACString& aResult)
+{
+ PRBool nonnull;
+ nsresult rv = aStream->ReadBoolean(&nonnull);
+ if (NS_SUCCEEDED(rv)) {
+ if (nonnull)
+ rv = aStream->ReadCString(aResult);
+ else
+ aResult.Truncate();
+ }
+ return rv;
+}
+
+inline nsresult
+NS_ReadOptionalString(nsIBinaryInputStream* aStream, nsAString& aResult)
+{
+ PRBool nonnull;
+ nsresult rv = aStream->ReadBoolean(&nonnull);
+ if (NS_SUCCEEDED(rv)) {
+ if (nonnull)
+ rv = aStream->ReadString(aResult);
+ else
+ aResult.Truncate();
+ }
+ return rv;
+}
+
+%}
diff --git a/src/libs/xpcom18a4/xpcom/io/nsIBinaryOutputStream.idl b/src/libs/xpcom18a4/xpcom/io/nsIBinaryOutputStream.idl
new file mode 100644
index 00000000..16a100b7
--- /dev/null
+++ b/src/libs/xpcom18a4/xpcom/io/nsIBinaryOutputStream.idl
@@ -0,0 +1,119 @@
+/* -*- 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, released
+ * March 31, 1998.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1998-1999
+ * 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 ***** */
+
+#include "nsIOutputStream.idl"
+#include "nsrootidl.idl"
+
+/**
+ * This interface allows writing of primitive data types (integers,
+ * floating-point values, booleans, etc.) to a stream in a binary, untagged,
+ * fixed-endianness format. This might be used, for example, to implement
+ * network protocols or to produce architecture-neutral binary disk files,
+ * i.e. ones that can be read and written by both big-endian and little-endian
+ * platforms. Output is written in big-endian order (high-order byte first),
+ * as this is traditional network order.
+ *
+ * @See nsIBinaryInputStream
+ */
+
+[scriptable, uuid(204ee610-8765-11d3-90cf-0040056a906e)]
+interface nsIBinaryOutputStream : nsIOutputStream {
+ void setOutputStream(in nsIOutputStream aOutputStream);
+
+ void writeBoolean(in PRBool aBoolean);
+
+ void write8(in PRUint8 aByte);
+ void write16(in PRUint16 a16);
+ void write32(in PRUint32 a32);
+ void write64(in PRUint64 a64);
+
+ void writeFloat(in float aFloat);
+ void writeDouble(in double aDouble);
+
+ /**
+ * Write a NUL-terminated 8-bit char* string to a binary stream.
+ */
+ void writeStringZ(in string aString);
+
+ /**
+ * Write a NUL-terminated 16-bit PRUnichar* string to a binary stream.
+ */
+ void writeWStringZ(in wstring aString);
+
+ /**
+ * Write a NUL-terminated UTF8-encoded string to a binary stream, produced
+ * from a NUL-terminated 16-bit PRUnichar* string argument.
+ */
+ void writeUtf8Z(in wstring aString);
+
+ /**
+ * Write an opaque byte array to a binary stream.
+ */
+ void writeBytes([size_is(aLength)] in string aString, in PRUint32 aLength);
+
+ /**
+ * Write an opaque byte array to a binary stream.
+ */
+ void writeByteArray([array, size_is(aLength)] in PRUint8 aBytes,
+ in PRUint32 aLength);
+
+};
+
+%{C++
+
+inline nsresult
+NS_WriteOptionalStringZ(nsIBinaryOutputStream* aStream, const char* aString)
+{
+ PRBool nonnull = (aString != nsnull);
+ nsresult rv = aStream->WriteBoolean(nonnull);
+ if (NS_SUCCEEDED(rv) && nonnull)
+ rv = aStream->WriteStringZ(aString);
+ return rv;
+}
+
+inline nsresult
+NS_WriteOptionalWStringZ(nsIBinaryOutputStream* aStream, const PRUnichar* aString)
+{
+ PRBool nonnull = (aString != nsnull);
+ nsresult rv = aStream->WriteBoolean(nonnull);
+ if (NS_SUCCEEDED(rv) && nonnull)
+ rv = aStream->WriteWStringZ(aString);
+ return rv;
+}
+
+%}
diff --git a/src/libs/xpcom18a4/xpcom/io/nsIByteArrayInputStream.idl b/src/libs/xpcom18a4/xpcom/io/nsIByteArrayInputStream.idl
new file mode 100644
index 00000000..a124e923
--- /dev/null
+++ b/src/libs/xpcom18a4/xpcom/io/nsIByteArrayInputStream.idl
@@ -0,0 +1,48 @@
+/* -*- Mode: C++; tab-width: 2; 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 ***** */
+
+#include "nsIInputStream.idl"
+
+[scriptable, uuid(b5a21556-35fc-4815-aff1-f9142639686e)]
+interface nsIByteArrayInputStream : nsIInputStream
+{
+};
+
+%{C++
+extern NS_COM nsresult
+NS_NewByteArrayInputStream (nsIByteArrayInputStream ** aResult, char * buffer, unsigned long size);
+%}
diff --git a/src/libs/xpcom18a4/xpcom/io/nsIDirectoryEnumerator.idl b/src/libs/xpcom18a4/xpcom/io/nsIDirectoryEnumerator.idl
new file mode 100644
index 00000000..b32a7f62
--- /dev/null
+++ b/src/libs/xpcom18a4/xpcom/io/nsIDirectoryEnumerator.idl
@@ -0,0 +1,68 @@
+/* -*- 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 the Directory Enumerator Interface.
+ *
+ * The Initial Developer of the Original Code is Google Inc.
+ * Portions created by the Initial Developer are Copyright (C) 2005
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Ben Goodger <ben@mozilla.org>
+ *
+ * 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 "nsISupports.idl"
+
+interface nsIFile;
+
+/**
+ * This interface provides a means for enumerating the contents of a directory.
+ * It is similar to nsISimpleEnumerator except the retrieved entries are QI'ed
+ * to nsIFile, and there is a mechanism for closing the directory when the
+ * enumeration is complete.
+ *
+ * @status UNDER_REVIEW
+ */
+[scriptable, uuid(31f7f4ae-6916-4f2d-a81e-926a4e3022ee)]
+interface nsIDirectoryEnumerator : nsISupports
+{
+ /**
+ * Retrieves the next file in the sequence. The "nextFile" element is the
+ * first element upon the first call. This attribute is null if there is no
+ * next element.
+ */
+ readonly attribute nsIFile nextFile;
+
+ /**
+ * Closes the directory being enumerated, releasing the system resource.
+ * @throws NS_OK if the call succeeded and the directory was closed.
+ * NS_ERROR_FAILURE if the directory close failed.
+ * It is safe to call this function many times.
+ */
+ void close();
+};
+
diff --git a/src/libs/xpcom18a4/xpcom/io/nsIDirectoryService.idl b/src/libs/xpcom18a4/xpcom/io/nsIDirectoryService.idl
new file mode 100644
index 00000000..c34b7aa9
--- /dev/null
+++ b/src/libs/xpcom18a4/xpcom/io/nsIDirectoryService.idl
@@ -0,0 +1,138 @@
+/* -*- Mode: C++; tab-width: 2; 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.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 ***** */
+
+#include "nsISupports.idl"
+#include "nsIFile.idl"
+
+/**
+ * nsIDirectoryServiceProvider
+ *
+ * Used by Directory Service to get file locations.
+ *
+ * @status FROZEN
+ */
+
+[scriptable, uuid(bbf8cab0-d43a-11d3-8cc2-00609792278c)]
+interface nsIDirectoryServiceProvider: nsISupports
+{
+ /**
+ * getFile
+ *
+ * Directory Service calls this when it gets the first request for
+ * a prop or on every request if the prop is not persistent.
+ *
+ * @param prop The symbolic name of the file.
+ * @param persistent TRUE - The returned file will be cached by Directory
+ * Service. Subsequent requests for this prop will
+ * bypass the provider and use the cache.
+ * FALSE - The provider will be asked for this prop
+ * each time it is requested.
+ *
+ * @return The file represented by the property.
+ *
+ */
+ nsIFile getFile(in string prop, out PRBool persistent);
+};
+
+/**
+ * nsIDirectoryServiceProvider2
+ *
+ * An extension of nsIDirectoryServiceProvider which allows
+ * multiple files to be returned for the given key.
+ *
+ * @status FROZEN
+ */
+
+[scriptable, uuid(2f977d4b-5485-11d4-87e2-0010a4e75ef2)]
+interface nsIDirectoryServiceProvider2: nsIDirectoryServiceProvider
+{
+ /**
+ * getFiles
+ *
+ * Directory Service calls this when it gets a request for
+ * a prop and the requested type is nsISimpleEnumerator.
+ *
+ * @param prop The symbolic name of the file list.
+ *
+ * @return An enumerator for a list of file locations.
+ * The elements in the enumeration are nsIFile
+ *
+ */
+ nsISimpleEnumerator getFiles(in string prop);
+};
+
+/**
+ * nsIDirectoryService
+ *
+ * @status FROZEN
+ */
+
+[scriptable, uuid(57a66a60-d43a-11d3-8cc2-00609792278c)]
+interface nsIDirectoryService: nsISupports
+{
+ /**
+ * init
+ *
+ * Must be called. Used internally by XPCOM initialization.
+ *
+ */
+ void init();
+
+ /**
+ * registerProvider
+ *
+ * Register a provider with the service.
+ *
+ * @param prov The service will keep a strong reference
+ * to this object. It will be released when
+ * the service is released.
+ *
+ */
+ void registerProvider(in nsIDirectoryServiceProvider prov);
+
+ /**
+ * unregisterProvider
+ *
+ * Unregister a provider with the service.
+ *
+ * @param prov
+ *
+ */
+ void unregisterProvider(in nsIDirectoryServiceProvider prov);
+};
+
+
diff --git a/src/libs/xpcom18a4/xpcom/io/nsIFastLoadFileControl.idl b/src/libs/xpcom18a4/xpcom/io/nsIFastLoadFileControl.idl
new file mode 100644
index 00000000..99f5a287
--- /dev/null
+++ b/src/libs/xpcom18a4/xpcom/io/nsIFastLoadFileControl.idl
@@ -0,0 +1,123 @@
+/* -*- 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 FastLoad code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2001
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Brendan Eich <brendan@mozilla.org> (original author)
+ *
+ * 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 "nsISupports.idl"
+#include "nsrootidl.idl"
+
+interface nsIFile;
+interface nsISimpleEnumerator;
+
+/**
+ * The nsIFastLoadFileControl interface and its subinterfaces are mix-ins for
+ * classes implementing nsIObjectInputStream and nsIObjectOutputStream, so that
+ * those stream types can be used with nsIFastLoadService to access and compute
+ * FastLoad file checksums, update and check FastLoad file dependencies, and
+ * multiplex documents loaded via non-blocking i/o.
+ *
+ * If an nsIObjectInputStream class doesn't support nsIFastLoadReadControl, or
+ * an nsIObjectOutputStream class doesn't support nsIFastLoadWriteControl, that
+ * implementation may still be useful for object serialization, but it can't be
+ * used to read or write a Mozilla FastLoad file.
+ */
+[scriptable, uuid(8a1e2c63-af50-4147-af7e-26289dc180dd)]
+interface nsIFastLoadFileControl : nsISupports
+{
+ /**
+ * Get and set the recorded checksum value from the FastLoad file header.
+ */
+ attribute PRUint32 checksum;
+
+ /**
+ * Multiplexed document control methods. A FastLoad file may contain
+ * multiple interleaved documents identified by a URI specifier string,
+ * and indexed for fast multiplexor select by an opaque URI object key.
+ * You StartMuxedDocument when initiating a document load, then Select
+ * before every batch of calls to (de)serialize document data, and End
+ * when the load completes.
+ *
+ * Document multiplexing is necessary to support incremental FastLoad
+ * development in a non-blocking i/o architecture such as Mozilla, where
+ * some (but not all, at first, or for a while during development) of the
+ * results of parsing and compiling various inputs can be multiplexed to
+ * or from a FastLoad file.
+ *
+ * Note: Select returns the previously selected URI object in case the
+ * caller is synchronously selecting and writing data to the FastLoad
+ * file, so the caller can reselect the previous URI and return to code
+ * the continues to write FastLoad data for the previous URI, unaware of
+ * the nested select/write/reselect.
+ */
+ void startMuxedDocument(in nsISupports aURI, in string aURISpec);
+ nsISupports selectMuxedDocument(in nsISupports aURI);
+ void endMuxedDocument(in nsISupports aURI);
+
+ /**
+ * Return true if aURISpec identifies a muxed document in the FastLoad
+ * file, false otherwise.
+ */
+ boolean hasMuxedDocument(in string aURISpec);
+};
+
+[scriptable, uuid(652ecec6-d40b-45b6-afef-641d6c63a35b)]
+interface nsIFastLoadReadControl : nsIFastLoadFileControl
+{
+ /**
+ * Computes the correct checksum of the FastLoad file, independent of the
+ * header's checksum value. The header checksum field is treated as zero
+ * when computing the checksum.
+ */
+ PRUint32 computeChecksum();
+
+ /**
+ * Get the collection of dependency nsIFile instances recorded during the
+ * FastLoad file write or read/update process, and checked during the read
+ * process to invalidate the FastLoad file if any dependencies are newer.
+ */
+ readonly attribute nsISimpleEnumerator dependencies;
+};
+
+[scriptable, uuid(2ad6e9e6-1379-4e45-a899-a54b27ff915c)]
+interface nsIFastLoadWriteControl : nsIFastLoadFileControl
+{
+ /**
+ * Add a file dependency of the FastLoad file (e.g., a .jar file) to the
+ * set of dependencies that trigger regeneration if any dependency has a
+ * last-modified-time greater than the FastLoad file's mtime.
+ */
+ void addDependency(in nsIFile aFile);
+};
diff --git a/src/libs/xpcom18a4/xpcom/io/nsIFastLoadService.idl b/src/libs/xpcom18a4/xpcom/io/nsIFastLoadService.idl
new file mode 100644
index 00000000..0b177463
--- /dev/null
+++ b/src/libs/xpcom18a4/xpcom/io/nsIFastLoadService.idl
@@ -0,0 +1,155 @@
+/* -*- 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 FastLoad code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2001
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Brendan Eich <brendan@mozilla.org> (original author)
+ *
+ * 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 "nsISupports.idl"
+#include "nsrootidl.idl"
+
+interface nsIFastLoadReadControl;
+interface nsIFile;
+interface nsIInputStream;
+interface nsIOutputStream;
+interface nsIObjectInputStream;
+interface nsIObjectOutputStream;
+
+[scriptable, uuid(715577db-d9c5-464a-a32e-0a40c29b22d4)]
+interface nsIFastLoadFileIO : nsISupports
+{
+ readonly attribute nsIInputStream inputStream;
+ readonly attribute nsIOutputStream outputStream;
+};
+
+[scriptable, uuid(759e475e-0c23-4dbf-b1b8-78c9369e3072)]
+interface nsIFastLoadService : nsISupports
+{
+ nsIFile newFastLoadFile(in string aBaseName);
+
+ nsIObjectInputStream newInputStream(in nsIInputStream aSrcStream);
+ nsIObjectOutputStream newOutputStream(in nsIOutputStream aDestStream);
+
+ // Flag values for the direction attribute and the aDirectionFlags
+ // parameter to startMuxedDocument.
+ const PRInt32 NS_FASTLOAD_READ = 1;
+ const PRInt32 NS_FASTLOAD_WRITE = 2;
+
+ attribute nsIObjectInputStream inputStream;
+ attribute nsIObjectOutputStream outputStream;
+ attribute nsIFastLoadFileIO fileIO;
+ readonly attribute PRInt32 direction;
+
+ /**
+ * These methods associate a URI object with its spec, for faster select
+ * using the object pointer as a key, rather than the spec string. The
+ * selectMuxedDocument method returns the previously selected URI object,
+ * in case a caller needs to reselect the previous after muxing data for
+ * a given URI synchronously. For the non-blocking or "asynchronous" i/o
+ * case, the caller must select the source URI from the FastLoad multiplex
+ * before writing a new burst of data parsed from the slow-loaded source.
+ *
+ * Clients of inputStream and outputStream should try to demultiplex data
+ * from the input stream only if fastLoadService->StartMuxedDocument(uri,
+ * urispec, NS_FASTLOAD_READ) succeeds. If StartMuxedDocument fails with
+ * NS_ERROR_NOT_AVAILABLE, callers should slow-load the documents, muxing
+ * their data to the current output stream.
+ */
+ void startMuxedDocument(in nsISupports aURI,
+ in string aURISpec,
+ in PRInt32 aDirectionFlags);
+ nsISupports selectMuxedDocument(in nsISupports aURI);
+ void endMuxedDocument(in nsISupports aURI);
+
+ void addDependency(in nsIFile aFile);
+
+ PRUint32 computeChecksum(in nsIFile aFile,
+ in nsIFastLoadReadControl aControl);
+ void cacheChecksum(in nsIFile aFile,
+ in nsIObjectOutputStream aStream);
+
+ [noscript] void getFastLoadReferent(inout nsISupports aPtr);
+
+ [noscript] void readFastLoadPtr(in nsIObjectInputStream aInputStream,
+ inout nsISupports aPtr);
+
+ [noscript] void writeFastLoadPtr(in nsIObjectOutputStream aOutputStream,
+ in nsISupports aPtr);
+
+ /**
+ * Return true if aURISpec identifies a muxed document in the FastLoad
+ * file, false otherwise.
+ */
+ boolean hasMuxedDocument(in string aURISpec);
+};
+
+%{C++
+#define NS_FASTLOADSERVICE_CLASSNAME "Mozilla FastLoad Service"
+
+#define NS_FASTLOADSERVICE_CID \
+ {0xc943093c,0xac94,0x4bee,{0x84,0x0b,0x8b,0x5a,0x6e,0x31,0x4f,0xa7}}
+
+#define NS_FASTLOADSERVICE_CONTRACTID \
+ "@mozilla.org/fast-load-service;1"
+
+#ifndef nsCOMPtr_h___
+# include "nsCOMPtr.h"
+#endif
+#ifndef __gen_nsIFile_h__
+# include "nsIFile.h"
+#endif
+#ifndef nsIServiceManager_h___
+# include "nsIServiceManager.h"
+#endif
+
+inline const nsGetServiceByCID
+do_GetFastLoadService(nsresult *aResultCode = 0)
+{
+ static NS_DEFINE_CID(kFastLoadServiceCID, NS_FASTLOADSERVICE_CID);
+ return nsGetServiceByCID(kFastLoadServiceCID, nsnull, aResultCode);
+}
+
+inline nsresult
+NS_AddFastLoadDependency(nsIFile* aFile)
+{
+ nsCOMPtr<nsIFastLoadService> fastLoadService(do_GetFastLoadService());
+ if (fastLoadService) {
+ nsresult rv = fastLoadService->AddDependency(aFile);
+ if (NS_FAILED(rv) && rv != NS_ERROR_NOT_AVAILABLE)
+ return rv;
+ }
+ return NS_OK;
+}
+
+%}
diff --git a/src/libs/xpcom18a4/xpcom/io/nsIFile.idl b/src/libs/xpcom18a4/xpcom/io/nsIFile.idl
new file mode 100644
index 00000000..1d70858e
--- /dev/null
+++ b/src/libs/xpcom18a4/xpcom/io/nsIFile.idl
@@ -0,0 +1,343 @@
+/* -*- 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, released
+ * March 31, 1998.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1998-1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Doug Turner <dougt@netscape.com>
+ * Christopher Blizzard <blizzard@mozilla.org>
+ * Darin Fisher <darin@netscape.com>
+ *
+ * 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 "nsISupports.idl"
+
+interface nsISimpleEnumerator;
+
+/**
+ * This is the only correct cross-platform way to specify a file.
+ * Strings are not such a way. If you grew up on windows or unix, you
+ * may think they are. Welcome to reality.
+ *
+ * All methods with string parameters have two forms. The preferred
+ * form operates on UCS-2 encoded characters strings. An alternate
+ * form operates on characters strings encoded in the "native" charset.
+ *
+ * A string containing characters encoded in the native charset cannot
+ * be safely passed to javascript via xpconnect. Therefore, the "native
+ * methods" are not scriptable.
+ *
+ * @status FROZEN
+ */
+[scriptable, uuid(c8c0a080-0868-11d3-915f-d9d889d48e3c)]
+interface nsIFile : nsISupports
+{
+ /**
+ * Create Types
+ *
+ * NORMAL_FILE_TYPE - A normal file.
+ * DIRECTORY_TYPE - A directory/folder.
+ */
+ const unsigned long NORMAL_FILE_TYPE = 0;
+ const unsigned long DIRECTORY_TYPE = 1;
+
+ /**
+ * append[Native]
+ *
+ * This function is used for constructing a descendent of the
+ * current nsIFile.
+ *
+ * @param node
+ * A string which is intended to be a child node of the nsIFile.
+ * For the |appendNative| method, the node must be in the native
+ * filesystem charset.
+ */
+ void append(in AString node);
+ [noscript] void appendNative(in ACString node);
+
+ /**
+ * Normalize the pathName (e.g. removing .. and . components on Unix).
+ */
+ void normalize();
+
+ /**
+ * create
+ *
+ * This function will create a new file or directory in the
+ * file system. Any nodes that have not been created or
+ * resolved, will be. If the file or directory already
+ * exists create() will return NS_ERROR_FILE_ALREADY_EXISTS.
+ *
+ * @param type
+ * This specifies the type of file system object
+ * to be made. The only two types at this time
+ * are file and directory which are defined above.
+ * If the type is unrecongnized, we will return an
+ * error (NS_ERROR_FILE_UNKNOWN_TYPE).
+ *
+ * @param permissions
+ * The unix style octal permissions. This may
+ * be ignored on systems that do not need to do
+ * permissions.
+ */
+ void create(in unsigned long type, in unsigned long permissions);
+
+ /**
+ * Accessor to the leaf name of the file itself.
+ * For the |nativeLeafName| method, the nativeLeafName must
+ * be in the native filesystem charset.
+ */
+ attribute AString leafName;
+ [noscript] attribute ACString nativeLeafName;
+
+ /**
+ * copyTo[Native]
+ *
+ * This will copy this file to the specified newParentDir.
+ * If a newName is specified, the file will be renamed.
+ * If 'this' is not created we will return an error
+ * (NS_ERROR_FILE_TARGET_DOES_NOT_EXIST).
+ *
+ * copyTo may fail if the file already exists in the destination
+ * directory.
+ *
+ * copyTo will NOT resolve aliases/shortcuts during the copy.
+ *
+ * @param newParentDir
+ * This param is the destination directory. If the
+ * newParentDir is null, copyTo() will use the parent
+ * directory of this file. If the newParentDir is not
+ * empty and is not a directory, an error will be
+ * returned (NS_ERROR_FILE_DESTINATION_NOT_DIR). For the
+ * |CopyToNative| method, the newName must be in the
+ * native filesystem charset.
+ *
+ * @param newName
+ * This param allows you to specify a new name for
+ * the file to be copied. This param may be empty, in
+ * which case the current leaf name will be used.
+ */
+ void copyTo(in nsIFile newParentDir, in AString newName);
+ [noscript] void CopyToNative(in nsIFile newParentDir, in ACString newName);
+
+ /**
+ * copyToFollowingLinks[Native]
+ *
+ * This function is identical to copyTo with the exception that,
+ * as the name implies, it follows symbolic links. The XP_UNIX
+ * implementation always follow symbolic links when copying. For
+ * the |CopyToFollowingLinks| method, the newName must be in the
+ * native filesystem charset.
+ */
+ void copyToFollowingLinks(in nsIFile newParentDir, in AString newName);
+ [noscript] void copyToFollowingLinksNative(in nsIFile newParentDir, in ACString newName);
+
+ /**
+ * moveTo[Native]
+ *
+ * A method to move this file or directory to newParentDir.
+ * If a newName is specified, the file or directory will be renamed.
+ * If 'this' is not created we will return an error
+ * (NS_ERROR_FILE_TARGET_DOES_NOT_EXIST).
+ * If 'this' is a file, and the destination file already exists, moveTo
+ * will replace the old file.
+ *
+ * moveTo will NOT resolve aliases/shortcuts during the copy.
+ * moveTo will do the right thing and allow copies across volumes.
+ * moveTo will return an error (NS_ERROR_FILE_DIR_NOT_EMPTY) if 'this' is
+ * a directory and the destination directory is not empty.
+ * moveTo will return an error (NS_ERROR_FILE_ACCESS_DENIED) if 'this' is
+ * a directory and the destination directory is not writable.
+ *
+ * @param newParentDir
+ * This param is the destination directory. If the
+ * newParentDir is empty, moveTo() will rename the file
+ * within its current directory. If the newParentDir is
+ * not empty and does not name a directory, an error will
+ * be returned (NS_ERROR_FILE_DESTINATION_NOT_DIR). For
+ * the |moveToNative| method, the newName must be in the
+ * native filesystem charset.
+ *
+ * @param newName
+ * This param allows you to specify a new name for
+ * the file to be moved. This param may be empty, in
+ * which case the current leaf name will be used.
+ */
+ void moveTo(in nsIFile newParentDir, in AString newName);
+ [noscript] void moveToNative(in nsIFile newParentDir, in ACString newName);
+
+ /**
+ * This will try to delete this file. The 'recursive' flag
+ * must be PR_TRUE to delete directories which are not empty.
+ *
+ * This will not resolve any symlinks.
+ */
+ void remove(in boolean recursive);
+
+ /**
+ * Attributes of nsIFile.
+ */
+
+ attribute unsigned long permissions;
+ attribute unsigned long permissionsOfLink;
+
+ /**
+ * File Times are to be in milliseconds from
+ * midnight (00:00:00), January 1, 1970 Greenwich Mean
+ * Time (GMT).
+ */
+ attribute PRInt64 lastModifiedTime;
+ attribute PRInt64 lastModifiedTimeOfLink;
+
+ /**
+ * WARNING! On the Mac, getting/setting the file size with nsIFile
+ * only deals with the size of the data fork. If you need to
+ * know the size of the combined data and resource forks use the
+ * GetFileSizeWithResFork() method defined on nsILocalFileMac.
+ */
+ attribute PRInt64 fileSize;
+ readonly attribute PRInt64 fileSizeOfLink;
+
+ /**
+ * target & path
+ *
+ * Accessor to the string path. The native version of these
+ * strings are not guaranteed to be a usable path to pass to
+ * NSPR or the C stdlib. There are problems that affect
+ * platforms on which a path does not fully specify a file
+ * because two volumes can have the same name (e.g., XP_MAC).
+ * This is solved by holding "private", native data in the
+ * nsIFile implementation. This native data is lost when
+ * you convert to a string.
+ *
+ * DO NOT PASS TO USE WITH NSPR OR STDLIB!
+ *
+ * target
+ * Find out what the symlink points at. Will give error
+ * (NS_ERROR_FILE_INVALID_PATH) if not a symlink.
+ *
+ * path
+ * Find out what the nsIFile points at.
+ *
+ * Note that the ACString attributes are returned in the
+ * native filesystem charset.
+ *
+ */
+ readonly attribute AString target;
+ [noscript] readonly attribute ACString nativeTarget;
+ readonly attribute AString path;
+ [noscript] readonly attribute ACString nativePath;
+
+ boolean exists();
+ boolean isWritable();
+ boolean isReadable();
+ boolean isExecutable();
+ boolean isHidden();
+ boolean isDirectory();
+ boolean isFile();
+ boolean isSymlink();
+ /**
+ * Not a regular file, not a directory, not a symlink.
+ */
+ boolean isSpecial();
+
+ /**
+ * createUnique
+ *
+ * This function will create a new file or directory in the
+ * file system. Any nodes that have not been created or
+ * resolved, will be. If this file already exists, we try
+ * variations on the leaf name "suggestedName" until we find
+ * one that did not already exist.
+ *
+ * If the search for nonexistent files takes too long
+ * (thousands of the variants already exist), we give up and
+ * return NS_ERROR_FILE_TOO_BIG.
+ *
+ * @param type
+ * This specifies the type of file system object
+ * to be made. The only two types at this time
+ * are file and directory which are defined above.
+ * If the type is unrecongnized, we will return an
+ * error (NS_ERROR_FILE_UNKNOWN_TYPE).
+ *
+ * @param permissions
+ * The unix style octal permissions. This may
+ * be ignored on systems that do not need to do
+ * permissions.
+ */
+ void createUnique(in unsigned long type, in unsigned long permissions);
+
+ /**
+ * clone()
+ *
+ * This function will allocate and initialize a nsIFile object to the
+ * exact location of the |this| nsIFile.
+ *
+ * @param file
+ * A nsIFile which this object will be initialize
+ * with.
+ *
+ */
+ nsIFile clone();
+
+ /**
+ * Will determine if the inFile equals this.
+ */
+ boolean equals(in nsIFile inFile);
+
+ /**
+ * Will determine if inFile is a descendant of this file
+ * If |recur| is true, look in subdirectories too
+ */
+ boolean contains(in nsIFile inFile, in boolean recur);
+
+ /**
+ * Parent will be null when this is at the top of the volume.
+ */
+ readonly attribute nsIFile parent;
+
+ /**
+ * Returns an enumeration of the elements in a directory. Each
+ * element in the enumeration is an nsIFile.
+ *
+ * @return NS_ERROR_FILE_NOT_DIRECTORY if the current nsIFile does
+ * not specify a directory.
+ */
+ readonly attribute nsISimpleEnumerator directoryEntries;
+};
+
+%{C++
+#ifndef MOZILLA_STRICT_API
+#include "nsDirectoryServiceUtils.h"
+#endif
+%}
diff --git a/src/libs/xpcom18a4/xpcom/io/nsIInputStream.idl b/src/libs/xpcom18a4/xpcom/io/nsIInputStream.idl
new file mode 100644
index 00000000..827f6363
--- /dev/null
+++ b/src/libs/xpcom18a4/xpcom/io/nsIInputStream.idl
@@ -0,0 +1,139 @@
+/* -*- Mode: C++; tab-width: 2; 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.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):
+ * Warren Harris <warren@netscape.com>
+ * Darin Fisher <darin@netscape.com>
+ *
+ * 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 "nsISupports.idl"
+
+interface nsIInputStream;
+
+%{C++
+/**
+ * The signature of the writer function passed to ReadSegments. This
+ * is the "consumer" of data that gets read from the stream's buffer.
+ *
+ * @param aInStream stream being read
+ * @param aClosure opaque parameter passed to ReadSegments
+ * @param aFromSegment pointer to memory owned by the input stream
+ * @param aToOffset amount already read (since ReadSegments was called)
+ * @param aCount length of fromSegment
+ * @param aWriteCount number of bytes read
+ *
+ * Implementers should return the following:
+ *
+ * @return NS_OK and (*aWriteCount > 0) if consumed some data
+ * @return <any-error> if not interested in consuming any data
+ *
+ * Errors are never passed to the caller of ReadSegments.
+ *
+ * NOTE: returning NS_OK and (*aWriteCount = 0) has undefined behavior.
+ *
+ * @status FROZEN
+ */
+typedef NS_CALLBACK(nsWriteSegmentFun)(nsIInputStream *aInStream,
+ void *aClosure,
+ const char *aFromSegment,
+ PRUint32 aToOffset,
+ PRUint32 aCount,
+ PRUint32 *aWriteCount);
+%}
+
+native nsWriteSegmentFun(nsWriteSegmentFun);
+
+/**
+ * nsIInputStream
+ *
+ * @status FROZEN
+ */
+[scriptable, uuid(fa9c7f6c-61b3-11d4-9877-00c04fa0cf4a)]
+interface nsIInputStream : nsISupports
+{
+ /**
+ * Close the stream.
+ */
+ void close();
+
+ /**
+ * @return number of bytes currently available in the stream
+ */
+ unsigned long available();
+
+ /**
+ * Read data from the stream.
+ *
+ * @param aBuf the buffer into which the data is to be read
+ * @param aCount the maximum number of bytes to be read
+ *
+ * @return number of bytes read (may be less than aCount).
+ * @return 0 if reached end of file
+ *
+ * @throws NS_BASE_STREAM_WOULD_BLOCK if reading from the input stream would
+ * block the calling thread (non-blocking mode only)
+ * @throws <other-error> on failure
+ */
+ [noscript] unsigned long read(in charPtr aBuf, in unsigned long aCount);
+
+ /**
+ * Low-level read method that has access to the stream's underlying buffer.
+ * The writer function may be called multiple times for segmented buffers.
+ * ReadSegments is expected to keep calling the writer until either there is
+ * nothing left to read or the writer returns an error. ReadSegments should
+ * not call the writer with zero bytes to consume.
+ *
+ * @param aWriter the "consumer" of the data to be read
+ * @param aClosure opaque parameter passed to writer
+ * @param aCount the maximum number of bytes to be read
+ *
+ * @return number of bytes read (may be less than aCount)
+ * @return 0 if reached end of file (or if aWriter refused to consume data)
+ *
+ * @throws NS_BASE_STREAM_WOULD_BLOCK if reading from the input stream would
+ * block the calling thread (non-blocking mode only)
+ * @throws <other-error> on failure
+ *
+ * NOTE: this function may be unimplemented if a stream has no underlying
+ * buffer (e.g., socket input stream).
+ */
+ [noscript] unsigned long readSegments(in nsWriteSegmentFun aWriter,
+ in voidPtr aClosure,
+ in unsigned long aCount);
+
+ /**
+ * @return true if stream is non-blocking
+ */
+ boolean isNonBlocking();
+};
diff --git a/src/libs/xpcom18a4/xpcom/io/nsIInputStreamTee.idl b/src/libs/xpcom18a4/xpcom/io/nsIInputStreamTee.idl
new file mode 100644
index 00000000..524e7ee2
--- /dev/null
+++ b/src/libs/xpcom18a4/xpcom/io/nsIInputStreamTee.idl
@@ -0,0 +1,61 @@
+/* -*- 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.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2001
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Darin Fisher <darin@netscape.com> (original author)
+ *
+ * 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 "nsIInputStream.idl"
+
+interface nsIOutputStream;
+
+/**
+ * A nsIInputStreamTee is a wrapper for an input stream, that when read
+ * reads the specified amount of data from its |source| and copies that
+ * data to its |sink|. |sink| must be a blocking output stream.
+ */
+[scriptable, uuid(44e8b2c8-1ecb-4a63-8b23-3e3500c34f32)]
+interface nsIInputStreamTee : nsIInputStream
+{
+ attribute nsIInputStream source;
+ attribute nsIOutputStream sink;
+};
+
+%{C++
+// factory method
+extern NS_COM nsresult
+NS_NewInputStreamTee(nsIInputStream **tee, // read from this input stream
+ nsIInputStream *source,
+ nsIOutputStream *sink);
+%}
diff --git a/src/libs/xpcom18a4/xpcom/io/nsILineInputStream.idl b/src/libs/xpcom18a4/xpcom/io/nsILineInputStream.idl
new file mode 100644
index 00000000..23781dc4
--- /dev/null
+++ b/src/libs/xpcom18a4/xpcom/io/nsILineInputStream.idl
@@ -0,0 +1,54 @@
+/* -*- Mode: C++; tab-width: 2; 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
+ * Boris Zbarsky <bzbarsky@mit.edu>.
+ * Portions created by the Initial Developer are Copyright (C) 2001
+ * 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 ***** */
+
+#include "nsISupports.idl"
+
+interface nsILineInputStream;
+
+[scriptable, uuid(c97b466c-1e6e-4773-a4ab-2b2b3190a7a6)]
+interface nsILineInputStream : nsISupports
+{
+ /**
+ * Read a single line from the stream, where a line is a
+ * possibly zero length sequence of 8bit chars terminated by a
+ * CR, LF, CRLF, LFCR, or eof.
+ * The line terminator is not returned.
+ * Return false for end of file, true otherwise
+ */
+ boolean readLine(out ACString aLine);
+};
diff --git a/src/libs/xpcom18a4/xpcom/io/nsILocalFile.idl b/src/libs/xpcom18a4/xpcom/io/nsILocalFile.idl
new file mode 100644
index 00000000..8d7a1dda
--- /dev/null
+++ b/src/libs/xpcom18a4/xpcom/io/nsILocalFile.idl
@@ -0,0 +1,180 @@
+/* -*- Mode: C++; tab-width: 2; 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, released
+ * March 31, 1998.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1998-1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Doug Turner <dougt@netscape.com>
+ * Darin Fisher <darin@netscape.com>
+ *
+ * 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 "nsIFile.idl"
+
+%{C++
+#include "prio.h"
+#include "prlink.h"
+#include <stdio.h>
+%}
+
+[ptr] native PRFileDescStar(PRFileDesc);
+[ptr] native PRLibraryStar(PRLibrary);
+[ptr] native FILE(FILE);
+
+/**
+ * This interface adds methods to nsIFile that are particular to a file
+ * that is accessible via the local file system.
+ *
+ * It follows the same string conventions as nsIFile.
+ *
+ * @status FROZEN
+ */
+[scriptable, uuid(aa610f20-a889-11d3-8c81-000064657374)]
+interface nsILocalFile : nsIFile
+{
+ /**
+ * initWith[Native]Path
+ *
+ * This function will initialize the nsILocalFile object. Any
+ * internal state information will be reset.
+ *
+ * NOTE: This function has a known bug on the macintosh and
+ * other OSes which do not represent file locations as paths.
+ * If you do use this function, be very aware of this problem!
+ *
+ * @param filePath
+ * A string which specifies a full file path to a
+ * location. Relative paths will be treated as an
+ * error (NS_ERROR_FILE_UNRECOGNIZED_PATH). For
+ * initWithNativePath, the filePath must be in the native
+ * filesystem charset.
+ */
+ void initWithPath(in AString filePath);
+ [noscript] void initWithNativePath(in ACString filePath);
+
+ /**
+ * initWithFile
+ *
+ * Initialize this object with another file
+ *
+ * @param aFile
+ * the file this becomes equivalent to
+ */
+ void initWithFile(in nsILocalFile aFile);
+
+ /**
+ * followLinks
+ *
+ * This attribute will determine if the nsLocalFile will auto
+ * resolve symbolic links. By default, this value will be false
+ * on all non unix systems. On unix, this attribute is effectively
+ * a noop.
+ */
+ attribute PRBool followLinks;
+
+ [noscript] PRFileDescStar openNSPRFileDesc(in long flags, in long mode);
+ [noscript] FILE openANSIFileDesc(in string mode);
+
+ [noscript] PRLibraryStar load();
+
+ readonly attribute PRInt64 diskSpaceAvailable;
+
+ /**
+ * appendRelative[Native]Path
+ *
+ * Append a relative path to the current path of the nsILocalFile object.
+ *
+ * @param relativeFilePath
+ * relativeFilePath is a native relative path. For security reasons,
+ * this cannot contain .. or cannot start with a directory separator.
+ * For the |appendRelativeNativePath| method, the relativeFilePath
+ * must be in the native filesystem charset.
+ */
+ void appendRelativePath(in AString relativeFilePath);
+ [noscript] void appendRelativeNativePath(in ACString relativeFilePath);
+
+ /**
+ * Accessor to a null terminated string which will specify
+ * the file in a persistent manner for disk storage.
+ *
+ * The character set of this attribute is undefined. DO NOT TRY TO
+ * INTERPRET IT AS HUMAN READABLE TEXT!
+ */
+ attribute ACString persistentDescriptor;
+
+ /**
+ * reveal
+ *
+ * Ask the operating system to open the folder which contains
+ * this file or folder. This routine only works on platforms which
+ * support the ability to open a folder...
+ */
+ void reveal();
+
+ /**
+ * launch
+ *
+ * Ask the operating system to attempt to open the file.
+ * this really just simulates "double clicking" the file on your platform.
+ * This routine only works on platforms which support this functionality.
+ */
+ void launch();
+
+ /**
+ * getRelativeDescriptor
+ *
+ * Returns a relative file path in an opaque, XP format. It is therefore
+ * not a native path.
+ *
+ * The character set of the string returned from this function is
+ * undefined. DO NOT TRY TO INTERPRET IT AS HUMAN READABLE TEXT!
+ *
+ * @param fromFile
+ * the file from which the descriptor is relative.
+ * There is no defined result if this param is null.
+ */
+ ACString getRelativeDescriptor(in nsILocalFile fromFile);
+
+ /**
+ * setRelativeDescriptor
+ *
+ * Initializes the file to the location relative to fromFile using
+ * a string returned by getRelativeDescriptor.
+ *
+ * @param fromFile
+ * the file to which the descriptor is relative
+ * @param relative
+ * the relative descriptor obtained from getRelativeDescriptor
+ */
+ void setRelativeDescriptor(in nsILocalFile fromFile, in ACString relativeDesc);
+};
+
diff --git a/src/libs/xpcom18a4/xpcom/io/nsILocalFileMac.idl b/src/libs/xpcom18a4/xpcom/io/nsILocalFileMac.idl
new file mode 100644
index 00000000..e04ea7c5
--- /dev/null
+++ b/src/libs/xpcom18a4/xpcom/io/nsILocalFileMac.idl
@@ -0,0 +1,262 @@
+/* -*- 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.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2001
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Conrad Carlen <ccarlen@netscape.com>
+ * Mark Mentovai <mark@moxienet.com>
+ *
+ * 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 "nsILocalFile.idl"
+
+%{C++
+#include <Files.h>
+#include <CFURL.h>
+%}
+
+ native OSType(OSType);
+ native FSSpec(FSSpec);
+[ptr] native FSSpecPtr(FSSpec);
+ native FSRef(FSRef);
+[ptr] native FSRefPtr(FSRef);
+ native CFURLRef(CFURLRef);
+
+[scriptable, uuid(748f3ffe-27d9-4402-9de9-494badbeebf4)]
+interface nsILocalFileMac : nsILocalFile
+{
+ /**
+ * initWithCFURL
+ *
+ * Init this object with a CFURLRef
+ *
+ * NOTE: Supported only for XP_MACOSX or TARGET_CARBON
+ * NOTE: If the path of the CFURL is /a/b/c, at least a/b must exist beforehand.
+ *
+ * @param aCFURL the CoreFoundation URL
+ *
+ */
+ [noscript] void initWithCFURL(in CFURLRef aCFURL);
+
+ /**
+ * initWithFSRef
+ *
+ * Init this object with an FSRef
+ *
+ * NOTE: Supported only for XP_MACOSX or TARGET_CARBON
+ *
+ * @param aFSRef the native file spec
+ *
+ */
+ [noscript] void initWithFSRef([const] in FSRefPtr aFSRef);
+
+ /**
+ * initWithFSSpec
+ *
+ * Init this object with an FSSpec
+ * Legacy method - leaving in place for now
+ *
+ * @param aFileSpec the native file spec
+ *
+ */
+ [noscript] void initWithFSSpec([const] in FSSpecPtr aFileSpec);
+
+ /**
+ * initToAppWithCreatorCode
+ *
+ * Init this object to point to an application having the given
+ * creator code. If this app is missing, this will fail. It will first
+ * look for running application with the given creator.
+ *
+ * @param aAppCreator the signature of the app
+ *
+ */
+ [noscript] void initToAppWithCreatorCode(in OSType aAppCreator);
+
+ /**
+ * getCFURL
+ *
+ * Returns the CFURLRef of the file object. The caller is
+ * responsible for calling CFRelease() on it.
+ *
+ * NOTE: Observes the state of the followLinks attribute.
+ * If the file object is an alias and followLinks is TRUE, returns
+ * the target of the alias. If followLinks is FALSE, returns
+ * the unresolved alias file.
+ *
+ * NOTE: Supported only for XP_MACOSX or TARGET_CARBON
+ *
+ * @return
+ *
+ */
+ [noscript] CFURLRef getCFURL();
+
+ /**
+ * getFSRef
+ *
+ * Returns the FSRef of the file object.
+ *
+ * NOTE: Observes the state of the followLinks attribute.
+ * If the file object is an alias and followLinks is TRUE, returns
+ * the target of the alias. If followLinks is FALSE, returns
+ * the unresolved alias file.
+ *
+ * NOTE: Supported only for XP_MACOSX or TARGET_CARBON
+ *
+ * @return
+ *
+ */
+ [noscript] FSRef getFSRef();
+
+ /**
+ * getFSSpec
+ *
+ * Returns the FSSpec of the file object.
+ *
+ * NOTE: Observes the state of the followLinks attribute.
+ * If the file object is an alias and followLinks is TRUE, returns
+ * the target of the alias. If followLinks is FALSE, returns
+ * the unresolved alias file.
+ *
+ * @return
+ *
+ */
+ [noscript] FSSpec getFSSpec();
+
+ /**
+ * fileSizeWithResFork
+ *
+ * Returns the combined size of both the data fork and the resource
+ * fork (if present) rather than just the size of the data fork
+ * as returned by GetFileSize()
+ *
+ */
+ readonly attribute PRInt64 fileSizeWithResFork;
+
+ /**
+ * Use with SetFileType() to specify the signature of current process
+ */
+ const unsigned long CURRENT_PROCESS_CREATOR = 0x8000000;
+
+ /**
+ * fileType, creator
+ *
+ * File type and creator attributes
+ *
+ */
+ [noscript] attribute OSType fileType;
+ [noscript] attribute OSType fileCreator;
+
+ /**
+ * setFileTypeAndCreatorFromMIMEType
+ *
+ * Sets the file type and creator code from a MIME type.
+ * Internet Config is used to determine the mapping.
+ *
+ * @param aMIMEType
+ *
+ */
+ void setFileTypeAndCreatorFromMIMEType(in string aMIMEType);
+
+ /**
+ * setFileTypeAndCreatorFromExtension
+ *
+ * Sets the file type and creator code from a file extension
+ * Internet Config is used to determine the mapping.
+ *
+ * @param aExtension
+ *
+ */
+ void setFileTypeAndCreatorFromExtension(in string aExtension);
+
+ /**
+ * launchWithDoc
+ *
+ * Launch the application that this file points to with a document.
+ *
+ * @param aDocToLoad Must not be NULL. If no document, use nsILocalFile::launch
+ * @param aLaunchInBackground TRUE if the application should not come to the front.
+ *
+ */
+ void launchWithDoc(in nsILocalFile aDocToLoad, in boolean aLaunchInBackground);
+
+ /**
+ * openDocWithApp
+ *
+ * Open the document that this file points to with the given application.
+ *
+ * @param aAppToOpenWith The application with which to open the document.
+ * If NULL, the creator code of the document is used
+ * to determine the application.
+ * @param aLaunchInBackground TRUE if the application should not come to the front.
+ *
+ */
+ void openDocWithApp(in nsILocalFile aAppToOpenWith, in boolean aLaunchInBackground);
+
+ /**
+ * isPackage
+ *
+ * returns true if a directory is determined to be a package under Mac OS 9/X
+ *
+ */
+ boolean isPackage();
+
+ /**
+ * bundleDisplayName
+ *
+ * returns the display name of the application bundle (usually the human
+ * readable name of the application)
+ */
+ readonly attribute AString bundleDisplayName;
+
+ /**
+ * bundleIdentifier
+ *
+ * returns the identifier of the bundle
+ */
+ readonly attribute AUTF8String bundleIdentifier;
+};
+
+%{C++
+extern "C"
+{
+
+#ifndef XP_MACOSX
+NS_EXPORT const char* NS_TruncNodeName(const char *aNode, char *outBuf);
+#endif
+
+NS_EXPORT nsresult NS_NewLocalFileWithFSSpec(const FSSpec* inSpec, PRBool followSymlinks, nsILocalFileMac* *result);
+
+// NS_NewLocalFileWithFSRef is available since Mozilla 1.8.1.
+NS_EXPORT nsresult NS_NewLocalFileWithFSRef(const FSRef* aFSRef, PRBool aFollowSymlinks, nsILocalFileMac** result);
+}
+%}
diff --git a/src/libs/xpcom18a4/xpcom/io/nsILocalFileOS2.idl b/src/libs/xpcom18a4/xpcom/io/nsILocalFileOS2.idl
new file mode 100644
index 00000000..95b4177b
--- /dev/null
+++ b/src/libs/xpcom18a4/xpcom/io/nsILocalFileOS2.idl
@@ -0,0 +1,90 @@
+/* -*- 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.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Richard L. Walsh.
+ * Portions created by the Initial Developer are Copyright (C) 2005
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Rich Walsh <dragtext@e-vertise.com> (Original Author)
+ *
+ * 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 "nsILocalFile.idl"
+
+interface nsIArray;
+
+[scriptable, uuid(26de2089-239d-4697-818b-bae1fe8e8e0d)]
+interface nsILocalFileOS2 : nsILocalFile
+{
+ /**
+ * getFileTypes
+ *
+ * Returns the file's .TYPE extended attribute as an array of
+ * nsISupportsCStrings.
+ *
+ */
+ nsIArray getFileTypes( );
+
+ /**
+ * setFileTypes
+ *
+ * Sets the file's .TYPE extended attribute from a comma-separated
+ * list of types (this format is used because clients are unlikely
+ * to write more than a single type).
+ * @param fileTypes
+ * a string in the filesystem's native character set
+ *
+ */
+ void setFileTypes( in ACString fileTypes );
+
+ /**
+ * isFileType
+ *
+ * Returns TRUE if the file has a .TYPE extended attribute that
+ * matches the string passed in. The comparison is case-sensitive.
+ * @param fileType
+ * a string in the filesystem's native character set
+ *
+ */
+ PRBool isFileType( in ACString fileType );
+
+ /**
+ * setFileSource
+ *
+ * Identifies the origin of a downloaded file by writing the
+ * source URI's spec to the .SUBJECT extended attribute.
+ *
+ * @param aURI
+ * the source URI
+ *
+ */
+ void setFileSource( in AUTF8String aURI );
+};
diff --git a/src/libs/xpcom18a4/xpcom/io/nsIMultiplexInputStream.idl b/src/libs/xpcom18a4/xpcom/io/nsIMultiplexInputStream.idl
new file mode 100644
index 00000000..ec309ecd
--- /dev/null
+++ b/src/libs/xpcom18a4/xpcom/io/nsIMultiplexInputStream.idl
@@ -0,0 +1,88 @@
+/* -*- 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 frightening to behold.
+ *
+ * The Initial Developer of the Original Code is
+ * Jonas Sicking.
+ * Portions created by the Initial Developer are Copyright (C) 2001
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Jonas Sicking <sicking@bigfoot.com>
+ *
+ * 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 "nsIInputStream.idl"
+
+/**
+ * The multiplex stream concatinates a list of input streams into a single
+ * stream.
+ */
+
+[scriptable, uuid(a076fd12-1dd1-11b2-b19a-d53b5dffaade)]
+interface nsIMultiplexInputStream : nsIInputStream
+{
+ /**
+ * Number of streams in this multiplex-stream
+ */
+ readonly attribute unsigned long count;
+
+ /**
+ * Appends a stream to the end of the streams. The cursor of the stream
+ * should be located at the beginning of the stream if the implementation
+ * of this nsIMultiplexInputStream also is used as an nsISeekableStream.
+ * @param stream stream to append
+ */
+ void appendStream(in nsIInputStream stream);
+
+ /**
+ * Insert a stream at specified index. If the cursor of this stream is at
+ * the beginning of the stream at index, the cursor will be placed at the
+ * beginning of the inserted stream instead.
+ * The cursor of the new stream should be located at the beginning of the
+ * stream if the implementation of this nsIMultiplexInputStream also is
+ * used as an nsISeekableStream.
+ * @param stream stream to insert
+ * @param index index to insert stream at, must be <= count
+ */
+ void insertStream(in nsIInputStream stream, in unsigned long index);
+
+ /**
+ * Remove stream at specified index. If this stream is the one currently
+ * being read the readcursor is moved to the beginning of the next
+ * stream
+ * @param index remove stream at this index, must be < count
+ */
+ void removeStream(in unsigned long index);
+
+ /**
+ * Get stream at specified index.
+ * @param index return stream at this index, must be < count
+ * @return stream at specified index
+ */
+ nsIInputStream getStream(in unsigned long index);
+};
diff --git a/src/libs/xpcom18a4/xpcom/io/nsIObjectInputStream.idl b/src/libs/xpcom18a4/xpcom/io/nsIObjectInputStream.idl
new file mode 100644
index 00000000..97c0ca6e
--- /dev/null
+++ b/src/libs/xpcom18a4/xpcom/io/nsIObjectInputStream.idl
@@ -0,0 +1,87 @@
+/* -*- 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 FastLoad code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2001
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Brendan Eich <brendan@mozilla.org> (original author)
+ *
+ * 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 "nsIBinaryInputStream.idl"
+#include "nsrootidl.idl"
+
+/**
+ * @see nsIObjectOutputStream
+ * @see nsIBinaryInputStream
+ */
+
+[scriptable, uuid(6c248606-4eae-46fa-9df0-ba58502368eb)]
+interface nsIObjectInputStream : nsIBinaryInputStream
+{
+ /**
+ * Read an object from this stream to satisfy a strong or weak reference
+ * to one of its interfaces. If the interface was not along the primary
+ * inheritance chain ending in the "root" or XPCOM-identity nsISupports,
+ * readObject will QueryInterface from the deserialized object root to the
+ * correct interface, which was specified when the object was serialized.
+ *
+ * @see nsIObjectOutputStream
+ */
+ nsISupports readObject(in PRBool aIsStrongRef);
+
+ [notxpcom] nsresult readID(out nsID aID);
+
+ /**
+ * Optimized deserialization support -- see nsIStreamBufferAccess.idl.
+ */
+ [notxpcom] charPtr getBuffer(in PRUint32 aLength, in PRUint32 aAlignMask);
+ [notxpcom] void putBuffer(in charPtr aBuffer, in PRUint32 aLength);
+};
+
+%{C++
+
+inline nsresult
+NS_ReadOptionalObject(nsIObjectInputStream* aStream, PRBool aIsStrongRef,
+ nsISupports* *aResult)
+{
+ PRBool nonnull;
+ nsresult rv = aStream->ReadBoolean(&nonnull);
+ if (NS_SUCCEEDED(rv)) {
+ if (nonnull)
+ rv = aStream->ReadObject(aIsStrongRef, aResult);
+ else
+ *aResult = nsnull;
+ }
+ return rv;
+}
+
+%}
diff --git a/src/libs/xpcom18a4/xpcom/io/nsIObjectOutputStream.idl b/src/libs/xpcom18a4/xpcom/io/nsIObjectOutputStream.idl
new file mode 100644
index 00000000..b8072127
--- /dev/null
+++ b/src/libs/xpcom18a4/xpcom/io/nsIObjectOutputStream.idl
@@ -0,0 +1,131 @@
+/* -*- 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 FastLoad code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2001
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Brendan Eich <brendan@mozilla.org> (original author)
+ *
+ * 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 "nsIBinaryOutputStream.idl"
+#include "nsrootidl.idl"
+
+/**
+ * @See nsIObjectInputStream
+ * @See nsIBinaryOutputStream
+ */
+
+[scriptable, uuid(92c898ac-5fde-4b99-87b3-5d486422094b)]
+interface nsIObjectOutputStream : nsIBinaryOutputStream
+{
+ /**
+ * Write the object whose "root" or XPCOM-identity nsISupports is aObject.
+ * The cause for writing this object is a strong or weak reference, so the
+ * aIsStrongRef argument must tell which kind of pointer is being followed
+ * here during serialization.
+ *
+ * If the object has only one strong reference in the serialization and no
+ * weak refs, use writeSingleRefObject. This is a valuable optimization:
+ * it saves space in the stream, and cycles on both ends of the process.
+ *
+ * If the reference being serialized is a pointer to an interface not on
+ * the primary inheritance chain ending in the root nsISupports, you must
+ * call writeCompoundObject instead of this method.
+ */
+ void writeObject(in nsISupports aObject, in PRBool aIsStrongRef);
+
+ /**
+ * Write an object referenced singly and strongly via its root nsISupports
+ * or a subclass of its root nsISupports. There must not be other refs to
+ * aObject in memory, or in the serialization.
+ */
+ void writeSingleRefObject(in nsISupports aObject);
+
+ /**
+ * Write the object referenced by an interface pointer at aObject that
+ * inherits from a non-primary nsISupports, i.e., a reference to one of
+ * the multiply inherited interfaces derived from an nsISupports other
+ * than the root or XPCOM-identity nsISupports; or a reference to an
+ * inner object in the case of true XPCOM aggregation. aIID identifies
+ * this interface.
+ */
+ void writeCompoundObject(in nsISupports aObject,
+ in nsIIDRef aIID,
+ in PRBool aIsStrongRef);
+
+ void writeID(in nsIDRef aID);
+
+ /**
+ * Optimized serialization support -- see nsIStreamBufferAccess.idl.
+ */
+ [notxpcom] charPtr getBuffer(in PRUint32 aLength, in PRUint32 aAlignMask);
+ [notxpcom] void putBuffer(in charPtr aBuffer, in PRUint32 aLength);
+};
+
+%{C++
+
+inline nsresult
+NS_WriteOptionalObject(nsIObjectOutputStream* aStream, nsISupports* aObject,
+ PRBool aIsStrongRef)
+{
+ PRBool nonnull = (aObject != nsnull);
+ nsresult rv = aStream->WriteBoolean(nonnull);
+ if (NS_SUCCEEDED(rv) && nonnull)
+ rv = aStream->WriteObject(aObject, aIsStrongRef);
+ return rv;
+}
+
+inline nsresult
+NS_WriteOptionalSingleRefObject(nsIObjectOutputStream* aStream,
+ nsISupports* aObject)
+{
+ PRBool nonnull = (aObject != nsnull);
+ nsresult rv = aStream->WriteBoolean(nonnull);
+ if (NS_SUCCEEDED(rv) && nonnull)
+ rv = aStream->WriteSingleRefObject(aObject);
+ return rv;
+}
+
+inline nsresult
+NS_WriteOptionalCompoundObject(nsIObjectOutputStream* aStream,
+ nsISupports* aObject,
+ const nsIID& aIID,
+ PRBool aIsStrongRef)
+{
+ PRBool nonnull = (aObject != nsnull);
+ nsresult rv = aStream->WriteBoolean(nonnull);
+ if (NS_SUCCEEDED(rv) && nonnull)
+ rv = aStream->WriteCompoundObject(aObject, aIID, aIsStrongRef);
+ return rv;
+}
+
+%}
diff --git a/src/libs/xpcom18a4/xpcom/io/nsIObservableInputStream.idl b/src/libs/xpcom18a4/xpcom/io/nsIObservableInputStream.idl
new file mode 100644
index 00000000..66b2d6e6
--- /dev/null
+++ b/src/libs/xpcom18a4/xpcom/io/nsIObservableInputStream.idl
@@ -0,0 +1,67 @@
+/* -*- 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.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corp.
+ * Portions created by the Initial Developer are Copyright (C) 2002
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s): Darin Fisher <darin@netscape.com>
+ *
+ * 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 "nsISupports.idl"
+
+interface nsIInputStream;
+interface nsIInputStreamObserver;
+
+[scriptable, uuid(6f489efe-1dd2-11b2-af78-fb84ae21591b)]
+interface nsIObservableInputStream : nsISupports
+{
+ /**
+ * Allows users to set an observer on an input stream to receive notifications
+ * about the consumer emptying the input stream's underlying buffer, or closing the
+ * stream. This is necessary for non-blocking streams so that the producer can suspend
+ * itself until more data can be written.
+ */
+ attribute nsIInputStreamObserver observer;
+};
+
+[scriptable, uuid(019d67cc-61b4-11d4-9877-00c04fa0cf4a)]
+interface nsIInputStreamObserver : nsISupports
+{
+ /**
+ * Called when the input stream's consumer has read all the existing data from the stream.
+ */
+ void onEmpty(in nsIInputStream inStr);
+
+ /**
+ * Called when the consumer closes its end of the stream.
+ */
+ void onClose(in nsIInputStream inStr);
+};
diff --git a/src/libs/xpcom18a4/xpcom/io/nsIObservableOutputStream.idl b/src/libs/xpcom18a4/xpcom/io/nsIObservableOutputStream.idl
new file mode 100644
index 00000000..4cdab3c1
--- /dev/null
+++ b/src/libs/xpcom18a4/xpcom/io/nsIObservableOutputStream.idl
@@ -0,0 +1,67 @@
+/* -*- 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.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corp.
+ * Portions created by the Initial Developer are Copyright (C) 2002
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s): Darin Fisher <darin@netscape.com>
+ *
+ * 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 "nsISupports.idl"
+
+interface nsIOutputStream;
+interface nsIOutputStreamObserver;
+
+[scriptable, uuid(15b374f8-1dd2-11b2-87ab-c3299d704fd3)]
+interface nsIObservableOutputStream : nsISupports
+{
+ /**
+ * Allows users to set an observer on an input stream to receive notifications
+ * about the consumer emptying the input stream's underlying buffer, or closing the
+ * stream. This is necessary for non-blocking streams so that the producer can suspend
+ * itself until more data can be written.
+ */
+ attribute nsIOutputStreamObserver observer;
+};
+
+[scriptable, uuid(12314194-61b4-11d4-9877-00c04fa0cf4a)]
+interface nsIOutputStreamObserver : nsISupports
+{
+ /**
+ * Called when the output stream's producer has written more data into the stream.
+ */
+ void onWrite(in nsIOutputStream outStr, in unsigned long amount);
+
+ /**
+ * Called when the stream's underlying buffer becomes full.
+ */
+ void onFull(in nsIOutputStream outStr);
+};
diff --git a/src/libs/xpcom18a4/xpcom/io/nsIOutputStream.idl b/src/libs/xpcom18a4/xpcom/io/nsIOutputStream.idl
new file mode 100644
index 00000000..ec5035c6
--- /dev/null
+++ b/src/libs/xpcom18a4/xpcom/io/nsIOutputStream.idl
@@ -0,0 +1,167 @@
+/* -*- Mode: C++; tab-width: 2; 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.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):
+ * Warren Harris <warren@netscape.com>
+ * Darin Fisher <darin@netscape.com>
+ *
+ * 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 "nsISupports.idl"
+
+interface nsIOutputStream;
+interface nsIInputStream;
+
+%{C++
+/**
+ * The signature for the reader function passed to WriteSegments. This
+ * is the "provider" of data that gets written into the stream's buffer.
+ *
+ * @param aOutStream stream being written to
+ * @param aClosure opaque parameter passed to WriteSegments
+ * @param aToSegment pointer to memory owned by the output stream
+ * @param aFromOffset amount already written (since WriteSegments was called)
+ * @param aCount length of toSegment
+ * @param aReadCount number of bytes written
+ *
+ * Implementers should return the following:
+ *
+ * @return NS_OK and (*aReadCount > 0) if successfully provided some data
+ * @return NS_OK and (*aReadCount = 0) or
+ * @return <any-error> if not interested in providing any data
+ *
+ * Errors are never passed to the caller of WriteSegments.
+ *
+ * @status FROZEN
+ */
+typedef NS_CALLBACK(nsReadSegmentFun)(nsIOutputStream *aOutStream,
+ void *aClosure,
+ char *aToSegment,
+ PRUint32 aFromOffset,
+ PRUint32 aCount,
+ PRUint32 *aReadCount);
+%}
+
+native nsReadSegmentFun(nsReadSegmentFun);
+
+/**
+ * nsIOutputStream
+ *
+ * @status FROZEN
+ */
+[scriptable, uuid(0d0acd2a-61b4-11d4-9877-00c04fa0cf4a)]
+interface nsIOutputStream : nsISupports
+{
+ /**
+ * Close the stream. Forces the output stream to flush any buffered data.
+ *
+ * @throws NS_BASE_STREAM_WOULD_BLOCK if unable to flush without blocking
+ * the calling thread (non-blocking mode only)
+ */
+ void close();
+
+ /**
+ * Flush the stream.
+ *
+ * @throws NS_BASE_STREAM_WOULD_BLOCK if unable to flush without blocking
+ * the calling thread (non-blocking mode only)
+ */
+ void flush();
+
+ /**
+ * Write data into the stream.
+ *
+ * @param aBuf the buffer containing the data to be written
+ * @param aCount the maximum number of bytes to be written
+ *
+ * @return number of bytes written (may be less than aCount)
+ *
+ * @throws NS_BASE_STREAM_WOULD_BLOCK if writing to the output stream would
+ * block the calling thread (non-blocking mode only)
+ * @throws <other-error> on failure
+ */
+ unsigned long write(in string aBuf, in unsigned long aCount);
+
+ /**
+ * Writes data into the stream from an input stream.
+ *
+ * @param aFromStream the stream containing the data to be written
+ * @param aCount the maximum number of bytes to be written
+ *
+ * @return number of bytes written (may be less than aCount)
+ *
+ * @throws NS_BASE_STREAM_WOULD_BLOCK if writing to the output stream would
+ * block the calling thread (non-blocking mode only)
+ * @throws <other-error> on failure
+ *
+ * NOTE: This method is defined by this interface in order to allow the
+ * output stream to efficiently copy the data from the input stream into
+ * its internal buffer (if any). If this method was provided as an external
+ * facility, a separate char* buffer would need to be used in order to call
+ * the output stream's other Write method.
+ */
+ unsigned long writeFrom(in nsIInputStream aFromStream,
+ in unsigned long aCount);
+
+ /**
+ * Low-level write method that has access to the stream's underlying buffer.
+ * The reader function may be called multiple times for segmented buffers.
+ * WriteSegments is expected to keep calling the reader until either there
+ * is nothing left to write or the reader returns an error. WriteSegments
+ * should not call the reader with zero bytes to provide.
+ *
+ * @param aReader the "provider" of the data to be written
+ * @param aClosure opaque parameter passed to reader
+ * @param aCount the maximum number of bytes to be written
+ *
+ * @return number of bytes written (may be less than aCount)
+ *
+ * @throws NS_BASE_STREAM_WOULD_BLOCK if writing to the output stream would
+ * block the calling thread (non-blocking mode only)
+ * @throws <other-error> on failure
+ *
+ * NOTE: this function may be unimplemented if a stream has no underlying
+ * buffer (e.g., socket output stream).
+ */
+ [noscript] unsigned long writeSegments(in nsReadSegmentFun aReader,
+ in voidPtr aClosure,
+ in unsigned long aCount);
+
+ /**
+ * @return true if stream is non-blocking
+ *
+ * NOTE: writing to a blocking output stream will block the calling thread
+ * until all given data can be consumed by the stream.
+ */
+ boolean isNonBlocking();
+};
diff --git a/src/libs/xpcom18a4/xpcom/io/nsIPipe.idl b/src/libs/xpcom18a4/xpcom/io/nsIPipe.idl
new file mode 100644
index 00000000..bbe3d978
--- /dev/null
+++ b/src/libs/xpcom18a4/xpcom/io/nsIPipe.idl
@@ -0,0 +1,211 @@
+/* -*- 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.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 ***** */
+
+#include "nsIAsyncInputStream.idl"
+#include "nsIAsyncOutputStream.idl"
+
+interface nsIMemory;
+
+/**
+ * nsIPipe represents an in-process buffer that can be read using nsIInputStream
+ * and written using nsIOutputStream. The reader and writer of a pipe do not
+ * have to be on the same thread. As a result, the pipe is an ideal mechanism
+ * to bridge data exchange between two threads. For example, a worker thread
+ * might write data to a pipe from which the main thread will read.
+ *
+ * Each end of the pipe can be either blocking or non-blocking. Recall that a
+ * non-blocking stream will return NS_BASE_STREAM_WOULD_BLOCK if it cannot be
+ * read or written to without blocking the calling thread. For example, if you
+ * try to read from an empty pipe that has not yet been closed, then if that
+ * pipe's input end is non-blocking, then the read call will fail immediately
+ * with NS_BASE_STREAM_WOULD_BLOCK as the error condition. However, if that
+ * pipe's input end is blocking, then the read call will not return until the
+ * pipe has data or until the pipe is closed. This example presumes that the
+ * pipe is being filled asynchronously on some background thread.
+ *
+ * The pipe supports nsIAsyncInputStream and nsIAsyncOutputStream, which give
+ * the user of a non-blocking pipe the ability to wait for the pipe to become
+ * ready again. For example, in the case of an empty non-blocking pipe, the
+ * user can call AsyncWait on the input end of the pipe to be notified when
+ * the pipe has data to read (or when the pipe becomes closed).
+ *
+ * NS_NewPipe2 and NS_NewPipe provide convenient pipe constructors. In most
+ * cases nsIPipe is not actually used. It is usually enough to just get
+ * references to the pipe's input and output end. In which case, the pipe is
+ * automatically closed when the respective pipe ends are released.
+ */
+[scriptable, uuid(f4211abc-61b3-11d4-9877-00c04fa0cf4a)]
+interface nsIPipe : nsISupports
+{
+ /**
+ * initialize this pipe
+ */
+ void init(in boolean nonBlockingInput,
+ in boolean nonBlockingOutput,
+ in unsigned long segmentSize,
+ in unsigned long segmentCount,
+ in nsIMemory segmentAllocator);
+
+ /**
+ * The pipe's input end, which also implements nsISearchableInputStream.
+ */
+ readonly attribute nsIAsyncInputStream inputStream;
+
+ /**
+ * The pipe's output end.
+ */
+ readonly attribute nsIAsyncOutputStream outputStream;
+};
+
+/**
+ * XXX this interface doesn't really belong in here. It is here because
+ * currently nsPipeInputStream is the only implementation of this interface.
+ */
+[scriptable, uuid(8C39EF62-F7C9-11d4-98F5-001083010E9B)]
+interface nsISearchableInputStream : nsISupports
+{
+ /**
+ * Searches for a string in the input stream. Since the stream has a notion
+ * of EOF, it is possible that the string may at some time be in the
+ * buffer, but is is not currently found up to some offset. Consequently,
+ * both the found and not found cases return an offset:
+ * if found, return offset where it was found
+ * if not found, return offset of the first byte not searched
+ * In the case the stream is at EOF and the string is not found, the first
+ * byte not searched will correspond to the length of the buffer.
+ */
+ void search(in string forString,
+ in boolean ignoreCase,
+ out boolean found,
+ out unsigned long offsetSearchedTo);
+};
+
+%{C++
+
+/**
+ * NS_NewPipe2
+ *
+ * This function supercedes NS_NewPipe. It differs from NS_NewPipe in two
+ * major ways:
+ * (1) returns nsIAsyncInputStream and nsIAsyncOutputStream, so it is
+ * not necessary to QI in order to access these interfaces.
+ * (2) the size of the pipe is determined by the number of segments
+ * times the size of each segment.
+ *
+ * @param pipeIn
+ * resulting input end of the pipe
+ * @param pipeOut
+ * resulting output end of the pipe
+ * @param nonBlockingInput
+ * true specifies non-blocking input stream behavior
+ * @param nonBlockingOutput
+ * true specifies non-blocking output stream behavior
+ * @param segmentSize
+ * specifies the segment size in bytes (pass 0 to use default value)
+ * @param segmentCount
+ * specifies the max number of segments (pass 0 to use default value)
+ * passing PR_UINT32_MAX here causes the pipe to have "infinite" space.
+ * this mode can be useful in some cases, but should always be used with
+ * caution. the default value for this parameter is a finite value.
+ * @param segmentAlloc
+ * pass reference to nsIMemory to have all pipe allocations use this
+ * allocator (pass null to use the default allocator)
+ */
+extern NS_COM nsresult
+NS_NewPipe2(nsIAsyncInputStream **pipeIn,
+ nsIAsyncOutputStream **pipeOut,
+ PRBool nonBlockingInput = PR_FALSE,
+ PRBool nonBlockingOutput = PR_FALSE,
+ PRUint32 segmentSize = 0,
+ PRUint32 segmentCount = 0,
+ nsIMemory *segmentAlloc = nsnull);
+
+/**
+ * NS_NewPipe
+ *
+ * Preserved for backwards compatibility. Plus, this interface is more
+ * amiable in certain contexts (e.g., when you don't need the pipe's async
+ * capabilities).
+ *
+ * @param pipeIn
+ * resulting input end of the pipe
+ * @param pipeOut
+ * resulting output end of the pipe
+ * @param segmentSize
+ * specifies the segment size in bytes (pass 0 to use default value)
+ * @param maxSize
+ * specifies the max size of the pipe (pass 0 to use default value)
+ * number of segments is maxSize / segmentSize, and maxSize must be a
+ * multiple of segmentSize. passing PR_UINT32_MAX here causes the
+ * pipe to have "infinite" space. this mode can be useful in some
+ * cases, but should always be used with caution. the default value
+ * for this parameter is a finite value.
+ * @param nonBlockingInput
+ * true specifies non-blocking input stream behavior
+ * @param nonBlockingOutput
+ * true specifies non-blocking output stream behavior
+ * @param segmentAlloc
+ * pass reference to nsIMemory to have all pipe allocations use this
+ * allocator (pass null to use the default allocator)
+ */
+inline nsresult
+NS_NewPipe(nsIInputStream **pipeIn,
+ nsIOutputStream **pipeOut,
+ PRUint32 segmentSize = 0,
+ PRUint32 maxSize = 0,
+ PRBool nonBlockingInput = PR_FALSE,
+ PRBool nonBlockingOutput = PR_FALSE,
+ nsIMemory *segmentAlloc = nsnull)
+{
+ PRUint32 segmentCount;
+ if (segmentSize == 0)
+ segmentCount = 0; // use default
+ else
+ segmentCount = maxSize / segmentSize;
+
+ nsIAsyncInputStream *in;
+ nsIAsyncOutputStream *out;
+ nsresult rv = NS_NewPipe2(&in, &out, nonBlockingInput, nonBlockingOutput,
+ segmentSize, segmentCount, segmentAlloc);
+ if (NS_FAILED(rv)) return rv;
+
+ *pipeIn = in;
+ *pipeOut = out;
+ return NS_OK;
+}
+
+%}
diff --git a/src/libs/xpcom18a4/xpcom/io/nsIScriptableInputStream.idl b/src/libs/xpcom18a4/xpcom/io/nsIScriptableInputStream.idl
new file mode 100644
index 00000000..9a21b1d0
--- /dev/null
+++ b/src/libs/xpcom18a4/xpcom/io/nsIScriptableInputStream.idl
@@ -0,0 +1,73 @@
+/* -*- Mode: IDL; 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, released
+ * March 31, 1998.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1998-1999
+ * 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 ***** */
+
+#include "nsISupports.idl"
+
+interface nsIInputStream;
+
+/**
+ * nsIScriptableInputStream provides scriptable access to the nsIInputStream.
+ *
+ * @status FROZEN
+ */
+[scriptable, uuid(a2a32f90-9b90-11d3-a189-0050041caf44)]
+interface nsIScriptableInputStream : nsISupports
+{
+ /**
+ * Closes the stream.
+ */
+ void close();
+
+ /** Wrap the given nsIInputStream with this nsIScriptableInputStream.
+ * @param aInputStream [in] parameter providing the stream to wrap
+ */
+ void init(in nsIInputStream aInputStream);
+
+ /** Return the number of bytes currently available in the stream
+ * @param _retval [out] parameter to hold the number of bytes
+ * if an error occurs, the parameter will be undefined
+ * @return error status
+ */
+ unsigned long available();
+
+ /** Read data from the stream.
+ * @param aCount [in] the maximum number of bytes to read
+ * @param _retval [out] the data
+ */
+ string read(in unsigned long aCount);
+};
diff --git a/src/libs/xpcom18a4/xpcom/io/nsISeekableStream.idl b/src/libs/xpcom18a4/xpcom/io/nsISeekableStream.idl
new file mode 100644
index 00000000..b2827c12
--- /dev/null
+++ b/src/libs/xpcom18a4/xpcom/io/nsISeekableStream.idl
@@ -0,0 +1,102 @@
+/* -*- 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 FastLoad code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2001
+ * 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 ***** */
+
+
+/*
+ * nsISeekableStream
+ *
+ * Note that a stream might not implement all methods (e.g., a readonly stream
+ * won't implement setEOF)
+ *
+ * @status UNDER_REVIEW
+ */
+
+#include "nsISupports.idl"
+
+[scriptable, uuid(8429d350-1040-4661-8b71-f2a6ba455980)]
+interface nsISeekableStream : nsISupports
+{
+ /*
+ * Sets the stream pointer to the value of the 'offset' parameter
+ */
+ const PRInt32 NS_SEEK_SET = 0;
+
+ /*
+ * Sets the stream pointer to its current location plus the value
+ * of the offset parameter.
+ */
+ const PRInt32 NS_SEEK_CUR = 1;
+
+ /*
+ * Sets the stream pointer to the size of the stream plus the value
+ * of the offset parameter.
+ */
+ const PRInt32 NS_SEEK_END = 2;
+
+ /**
+ * seek
+ *
+ * This method moves the stream offset of the steam implementing this
+ * interface.
+ *
+ * @param whence specifies how to interpret the 'offset' parameter in
+ * setting the stream offset associated with the implementing
+ * stream.
+ *
+ * @param offset specifies a value, in bytes, that is used in conjunction
+ * with the 'whence' parameter to set the stream offset of the
+ * implementing stream. A negative value causes seeking in
+ * the reverse direction.
+ */
+ void seek(in long whence, in long long offset);
+
+ /**
+ * tell
+ *
+ * This method reports the current offset, in bytes, from the start of the
+ * stream.
+ */
+ long long tell();
+
+
+ /**
+ * setEOF
+ *
+ * This method truncates the stream at the current offset.
+ */
+ void setEOF();
+};
diff --git a/src/libs/xpcom18a4/xpcom/io/nsIStorageStream.idl b/src/libs/xpcom18a4/xpcom/io/nsIStorageStream.idl
new file mode 100644
index 00000000..148136b9
--- /dev/null
+++ b/src/libs/xpcom18a4/xpcom/io/nsIStorageStream.idl
@@ -0,0 +1,107 @@
+/* -*- 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, released
+ * March 31, 1998.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1998-1999
+ * 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 ***** */
+
+#include "nsISupports.idl"
+#include "nsrootidl.idl"
+
+interface nsIMemory;
+interface nsIInputStream;
+interface nsIOutputStream;
+
+/**
+ * The nsIStorageStream interface maintains an internal data buffer that can be
+ * filled using a single output stream. One or more independent input streams
+ * can be created to read the data from the buffer non-destructively.
+ */
+
+[scriptable, uuid(604ad9d0-753e-11d3-90ca-34278643278f)]
+interface nsIStorageStream : nsISupports
+{
+ /**
+ *
+ * Initialize the stream, setting up the amount of space that will be
+ * allocated for the stream's backing-store.
+ *
+ * @param segmentSize
+ * Size of each segment. Must be a power of two.
+ * @param maxSize
+ * Maximum total size of this stream. length will always be less
+ * than or equal to this value. Passing PR_UINT32_MAX is safe.
+ * @param segmentAllocator
+ * Which allocator to use for the segments. May be null, in which
+ * case a default allocator will be used.
+ */
+ void init(in PRUint32 segmentSize, in PRUint32 maxSize, in nsIMemory segmentAllocator);
+
+ /**
+ * Get a reference to the one and only output stream for this instance.
+ * The zero-based startPosition argument is used is used to set the initial
+ * write cursor position. The startPosition cannot be set larger than the
+ * current buffer length. Calling this method has the side-effect of
+ * truncating the internal buffer to startPosition bytes.
+ */
+ nsIOutputStream getOutputStream(in PRInt32 startPosition);
+
+ /**
+ * Create a new input stream to read data (written by the singleton output
+ * stream) from the internal buffer. Multiple, independent input streams
+ * can be created.
+ */
+ nsIInputStream newInputStream(in PRInt32 startPosition);
+
+ /**
+ * The length attribute indicates the total number of bytes stored in the
+ * nsIStorageStream internal buffer, regardless of any consumption by input
+ * streams. Assigning to the length field can be used to truncate the
+ * buffer data, but can not be used when either the instance's output
+ * stream is in use.
+ *
+ * @See #writeInProgress */
+ attribute PRUint32 length;
+
+ /**
+ * True, when output stream has not yet been Close'ed
+ */
+ readonly attribute boolean writeInProgress;
+};
+
+%{C++
+// Factory method
+NS_COM nsresult
+NS_NewStorageStream(PRUint32 segmentSize, PRUint32 maxSize, nsIStorageStream **result);
+%}
diff --git a/src/libs/xpcom18a4/xpcom/io/nsIStreamBufferAccess.idl b/src/libs/xpcom18a4/xpcom/io/nsIStreamBufferAccess.idl
new file mode 100644
index 00000000..dc41585f
--- /dev/null
+++ b/src/libs/xpcom18a4/xpcom/io/nsIStreamBufferAccess.idl
@@ -0,0 +1,191 @@
+/* -*- 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 FastLoad code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2001
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Brendan Eich <brendan@mozilla.org> (original author)
+ *
+ * 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 "nsISupports.idl"
+#include "nsrootidl.idl"
+
+/**
+ * An interface for access to a buffering stream implementation's underlying
+ * memory buffer.
+ *
+ * Stream implementations that QueryInterface to nsIStreamBufferAccess must
+ * ensure that all buffers are aligned on the most restrictive type size for
+ * the current architecture (e.g., sizeof(double) for RISCy CPUs). malloc(3)
+ * satisfies this requirement.
+ */
+[uuid(ac923b72-ac87-4892-ac7a-ca385d429435)]
+interface nsIStreamBufferAccess : nsISupports
+{
+ /**
+ * Get access to a contiguous, aligned run of bytes in the stream's buffer.
+ * Exactly one successful getBuffer call must occur before a putBuffer call
+ * taking the non-null pointer returned by the successful getBuffer.
+ *
+ * The run of bytes are the next bytes (modulo alignment padding) to read
+ * for an input stream, and the next bytes (modulo alignment padding) to
+ * store before (eventually) writing buffered data to an output stream.
+ * There can be space beyond this run of bytes in the buffer for further
+ * accesses before the fill or flush point is reached.
+ *
+ * @param aLength
+ * Count of contiguous bytes requested at the address A that satisfies
+ * (A & aAlignMask) == 0 in the buffer, starting from the current stream
+ * position, mapped to a buffer address B. The stream implementation
+ * must pad from B to A by skipping bytes (if input stream) or storing
+ * zero bytes (if output stream).
+ *
+ * @param aAlignMask
+ * Bit-mask computed by subtracting 1 from the power-of-two alignment
+ * modulus (e.g., 3 or sizeof(PRUint32)-1 for PRUint32 alignment).
+ *
+ * @return
+ * The aligned pointer to aLength bytes in the buffer, or null if the
+ * buffer has no room for aLength bytes starting at the next address A
+ * after the current position that satisfies (A & aAlignMask) == 0.
+ */
+ [notxpcom] charPtr getBuffer(in PRUint32 aLength, in PRUint32 aAlignMask);
+
+ /**
+ * Relinquish access to the stream's buffer, filling if at end of an input
+ * buffer, flushing if completing an output buffer. After a getBuffer call
+ * that returns non-null, putBuffer must be called.
+ *
+ * @param aBuffer
+ * A non-null pointer returned by getBuffer on the same stream buffer
+ * access object.
+ *
+ * @param aLength
+ * The same count of contiguous bytes passed to the getBuffer call that
+ * returned aBuffer.
+ */
+ [notxpcom] void putBuffer(in charPtr aBuffer, in PRUint32 aLength);
+
+ /**
+ * Disable and enable buffering on the stream implementing this interface.
+ * DisableBuffering flushes an output stream's buffer, and invalidates an
+ * input stream's buffer.
+ */
+ void disableBuffering();
+ void enableBuffering();
+
+ /**
+ * The underlying, unbuffered input or output stream.
+ */
+ readonly attribute nsISupports unbufferedStream;
+};
+
+%{C++
+
+// Swap macros, used to convert to/from canonical (big-endian) format
+#if defined IS_LITTLE_ENDIAN
+
+# define NS_SWAP16(x) ((((x) & 0xff) << 8) | (((x) >> 8) & 0xff))
+# define NS_SWAP32(x) ((NS_SWAP16((x) & 0xffff) << 16) | (NS_SWAP16((x) >> 16)))
+
+// We want to avoid casting to 32-bit types if possible, since that violates
+// aliasing rules (a standard compiler may assume that pointers of two types
+// do not address overlapping storage).
+//
+// XXX What if we have a compiler that follows aliasing rules strictly but
+// doesn't have a 64-bit int type?
+//
+// XXXbe shouldn't NSPR's LL_INIT work for non-constant arguments in all cases?
+
+# if defined HAVE_LONG_LONG
+# if PR_BYTES_PER_LONG == 8
+# define ULL_(x) x ## UL
+# elif (defined WIN32 || defined WIN16) && !defined __GNUC__
+# define ULL_(x) ((uint64) x ## i64)
+# else
+# define ULL_(x) x ## ULL
+# endif
+
+# define NS_SWAP64(x) ((((x) & ULL_(0xff00000000000000)) >> 56) | \
+ (((x) & ULL_(0x00ff000000000000)) >> 40) | \
+ (((x) & ULL_(0x0000ff0000000000)) >> 24) | \
+ (((x) & ULL_(0x000000ff00000000)) >> 8) | \
+ (((x) & ULL_(0x00000000ff000000)) << 8) | \
+ (((x) & ULL_(0x0000000000ff0000)) << 24) | \
+ (((x) & ULL_(0x000000000000ff00)) << 40) | \
+ (((x) /* & ULL_(0x00000000000000ff) */) << 56))
+# else
+# define NS_SWAP64(x) LL_INIT((((x).lo /* & 0xff000000ul */) >> 24) | \
+ (((x).lo & 0x00ff0000ul) >> 8) | \
+ (((x).lo & 0x0000ff00ul) << 8) | \
+ (((x).lo /* & 0x000000fful */) << 24), \
+ (((x).hi /* & 0xff000000ul */) >> 24) | \
+ (((x).hi & 0x00ff0000ul) >> 8) | \
+ (((x).hi & 0x0000ff00ul) << 8) | \
+ (((x).hi /* & 0x000000fful */) << 24))
+# endif
+
+#elif defined IS_BIG_ENDIAN
+
+# define NS_SWAP16(x) (x)
+# define NS_SWAP32(x) (x)
+# define NS_SWAP64(x) (x)
+
+#else
+
+# error "Unknown byte order"
+
+#endif
+
+/**
+ * These macros get and put a buffer given either an sba parameter that may
+ * point to an object implementing nsIStreamBufferAccess, nsIObjectInputStream,
+ * or nsIObjectOutputStream.
+ */
+#define NS_GET_BUFFER(sba,n,a) ((sba)->GetBuffer(n, a))
+#define NS_PUT_BUFFER(sba,p,n) ((sba)->PutBuffer(p, n))
+
+#define NS_GET8(p) (*(PRUint8*)(p))
+#define NS_GET16(p) NS_SWAP16(*(PRUint16*)(p))
+#define NS_GET32(p) NS_SWAP32(*(PRUint32*)(p))
+#define NS_GET64(p) NS_SWAP64(*(PRUint64*)(p))
+#define NS_GET_FLOAT(p) ((float)NS_SWAP32(*(PRUint32*)(p)))
+#define NS_GET_DOUBLE(p) ((double)NS_SWAP64(*(PRUint64*)(p)))
+
+#define NS_PUT8(p,x) (*(PRUint8*)(p) = (x))
+#define NS_PUT16(p,x) (*(PRUint16*)(p) = NS_SWAP16(x))
+#define NS_PUT32(p,x) (*(PRUint32*)(p) = NS_SWAP32(x))
+#define NS_PUT64(p,x) (*(PRUint64*)(p) = NS_SWAP64(x))
+#define NS_PUT_FLOAT(p,x) (*(PRUint32*)(p) = NS_SWAP32(*(PRUint32*)&(x)))
+#define NS_PUT_DOUBLE(p,x) (*(PRUint64*)(p) = NS_SWAP64(*(PRUint64*)&(x)))
+
+%}
diff --git a/src/libs/xpcom18a4/xpcom/io/nsIStringStream.idl b/src/libs/xpcom18a4/xpcom/io/nsIStringStream.idl
new file mode 100644
index 00000000..09a90548
--- /dev/null
+++ b/src/libs/xpcom18a4/xpcom/io/nsIStringStream.idl
@@ -0,0 +1,138 @@
+/* -*- Mode: C++; tab-width: 2; 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):
+ * mcmullen@netscape.com (original author)
+ * scc@mozilla.org
+ * davidm@netscape.com
+ * darin@netscape.com
+ *
+ * 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 ***** */
+
+/**
+ * Based on original code from nsIStringStream.h
+ */
+
+#include "nsIInputStream.idl"
+
+/**
+ * nsIStringInputStream
+ *
+ * Provides scriptable and specialized C++ only methods for initializing a
+ * nsIInputStream implementation with a simple character array.
+ */
+[scriptable, uuid(450cd2d4-f0fd-424d-b365-b1251f80fd53)]
+interface nsIStringInputStream : nsIInputStream
+{
+ /**
+ * SetData - assign data to the input stream (copied on assignment).
+ *
+ * @param data - stream data
+ * @param dataLen - stream data length (-1 if length should be computed)
+ *
+ * NOTE: C++ code should consider using AdoptData or ShareData to avoid
+ * making an extra copy of the stream data.
+ */
+ void setData(in string data, in long dataLen);
+
+ /**
+ * NOTE: the following methods are designed to give C++ code added control
+ * over the ownership and lifetime of the stream data. Use with care :-)
+ */
+
+ /**
+ * AdoptData - assign data to the input stream. the input stream takes
+ * ownership of the given data buffer and will nsMemory::Free it when
+ * the input stream is destroyed.
+ *
+ * @param data - stream data
+ * @param dataLen - stream data length (-1 if length should be computed)
+ */
+ [noscript] void adoptData(in charPtr data, in long dataLen);
+
+ /**
+ * ShareData - assign data to the input stream. the input stream references
+ * the given data buffer until the input stream is destroyed. the given
+ * data buffer must outlive the input stream.
+ *
+ * @param data - stream data
+ * @param dataLen - stream data length (-1 if length should be computed)
+ */
+ [noscript] void shareData(in string data, in long dataLen);
+};
+
+%{C++
+
+//-----------------------------------------------------------------------------
+// C++ factory methods
+//-----------------------------------------------------------------------------
+
+#include "nsIInputStream.h"
+#include "nsString.h"
+
+#ifdef VBOX_WITH_XPCOM_NAMESPACE_CLEANUP
+#define NS_NewByteInputStream VBoxNsxpNS_NewByteInputStream
+#define NS_NewCStringInputStream VBoxNsxpNS_NewCStringInputStream
+#define NS_NewCharInputStream VBoxNsxpNS_NewCharInputStream
+#define NS_NewStringInputStream VBoxNsxpNS_NewStringInputStream
+#endif /* VBOX_WITH_XPCOM_NAMESPACE_CLEANUP */
+
+//-----------------------------------------------------------------------------
+// Factory method to get an nsInputStream from an nsAString. Result will
+// implement nsIStringInputStream and nsIRandomAccessStore
+extern "C" NS_COM nsresult
+NS_NewStringInputStream(nsIInputStream** aStreamResult,
+ const nsAString& aStringToRead);
+
+//-----------------------------------------------------------------------------
+// Factory method to get an nsInputStream from an nsACString. Result will
+// implement nsIStringInputStream and nsIRandomAccessStore
+extern "C" NS_COM nsresult
+NS_NewCStringInputStream(nsIInputStream** aStreamResult,
+ const nsACString& aStringToRead);
+
+//-----------------------------------------------------------------------------
+// Factory method to get an nsInputStream from a string. Result will
+// implement nsIStringInputStream and nsIRandomAccessStore
+extern "C" NS_COM nsresult
+NS_NewCharInputStream(nsIInputStream** aStreamResult,
+ const char* aStringToRead);
+
+//-----------------------------------------------------------------------------
+// Factory method to get an nsInputStream from a byte buffer. Result will
+// implement nsIStringInputStream and nsIRandomAccessStore
+extern "C" NS_COM nsresult
+NS_NewByteInputStream(nsIInputStream** aStreamResult,
+ const char* aStringToRead,
+ PRInt32 aLength);
+%}
diff --git a/src/libs/xpcom18a4/xpcom/io/nsIUnicharInputStream.h b/src/libs/xpcom18a4/xpcom/io/nsIUnicharInputStream.h
new file mode 100644
index 00000000..7232249d
--- /dev/null
+++ b/src/libs/xpcom18a4/xpcom/io/nsIUnicharInputStream.h
@@ -0,0 +1,94 @@
+/* -*- Mode: C++; tab-width: 2; 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 ***** */
+#ifndef nsIUnicharInputStream_h___
+#define nsIUnicharInputStream_h___
+
+#include "nsIInputStream.h"
+#include "nsString.h"
+#include "nscore.h"
+
+class nsIUnicharInputStream;
+
+typedef NS_CALLBACK(nsWriteUnicharSegmentFun)(nsIUnicharInputStream *aInStream,
+ void *aClosure,
+ const PRUnichar *aFromSegment,
+ PRUint32 aToOffset,
+ PRUint32 aCount,
+ PRUint32 *aWriteCount);
+/* c4bcf6ee-3a79-4d77-8d48-f17be3199b3b */
+#define NS_IUNICHAR_INPUT_STREAM_IID \
+{ 0xc4bcf6ee, 0x3a79, 0x4d77, \
+ {0x8d, 0x48, 0xf1, 0x7b, 0xe3, 0x19, 0x9b, 0x3b} }
+
+/** Abstract unicode character input stream
+ * @see nsIInputStream
+ */
+class NS_NO_VTABLE nsIUnicharInputStream : public nsISupports {
+public:
+ NS_DEFINE_STATIC_IID_ACCESSOR(NS_IUNICHAR_INPUT_STREAM_IID)
+
+ NS_IMETHOD Read(PRUnichar* aBuf,
+ PRUint32 aCount,
+ PRUint32 *aReadCount) = 0;
+ NS_IMETHOD Close() = 0;
+ NS_IMETHOD ReadSegments(nsWriteUnicharSegmentFun aWriter,
+ void* aClosure,
+ PRUint32 aCount,
+ PRUint32 *aReadCount) = 0;
+};
+
+/**
+ * Create a nsIUnicharInputStream that wraps up a string. Data is fed
+ * from the string out until the done. When this object is destroyed
+ * it destroyes the string (so make a copy if you don't want it doing
+ * that)
+ */
+extern NS_COM nsresult
+ NS_NewStringUnicharInputStream(nsIUnicharInputStream** aInstancePtrResult,
+ nsString* aString);
+
+/** Create a new nsUnicharInputStream that provides a converter for the
+ * byte input stream aStreamToWrap. If no converter can be found then
+ * nsnull is returned and the error code is set to
+ * NS_INPUTSTREAM_NO_CONVERTER.
+ */
+extern NS_COM nsresult
+ NS_NewUTF8ConverterStream(nsIUnicharInputStream** aInstancePtrResult,
+ nsIInputStream* aStreamToWrap,
+ PRInt32 aBufferSize = 0);
+
+#endif /* nsUnicharInputStream_h___ */
diff --git a/src/libs/xpcom18a4/xpcom/io/nsInputStreamTee.cpp b/src/libs/xpcom18a4/xpcom/io/nsInputStreamTee.cpp
new file mode 100644
index 00000000..b0651ad1
--- /dev/null
+++ b/src/libs/xpcom18a4/xpcom/io/nsInputStreamTee.cpp
@@ -0,0 +1,224 @@
+/* -*- Mode: C++; tab-width: 2; 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.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2001
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Darin Fisher <darin@netscape.com> (original author)
+ *
+ * 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 "nsIInputStreamTee.h"
+#include "nsIInputStream.h"
+#include "nsIOutputStream.h"
+#include "nsCOMPtr.h"
+
+class nsInputStreamTee : public nsIInputStreamTee
+{
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIINPUTSTREAM
+ NS_DECL_NSIINPUTSTREAMTEE
+
+ nsInputStreamTee();
+
+private:
+ ~nsInputStreamTee() {}
+
+ nsresult TeeSegment(const char *buf, PRUint32 count);
+
+ static NS_METHOD WriteSegmentFun(nsIInputStream *, void *, const char *,
+ PRUint32, PRUint32, PRUint32 *);
+
+private:
+ nsCOMPtr<nsIInputStream> mSource;
+ nsCOMPtr<nsIOutputStream> mSink;
+ nsWriteSegmentFun mWriter; // for implementing ReadSegments
+ void *mClosure; // for implementing ReadSegments
+};
+
+nsInputStreamTee::nsInputStreamTee()
+{
+}
+
+nsresult
+nsInputStreamTee::TeeSegment(const char *buf, PRUint32 count)
+{
+ if (!mSink)
+ return NS_OK; // nothing to do
+ nsresult rv;
+ PRUint32 bytesWritten = 0;
+ while (count) {
+ rv = mSink->Write(buf + bytesWritten, count, &bytesWritten);
+ if (NS_FAILED(rv)) {
+ // ok, this is not a fatal error... just drop our reference to mSink
+ // and continue on as if nothing happened.
+ NS_WARNING("Write failed (non-fatal)");
+ // catch possible misuse of the input stream tee
+ NS_ASSERTION(rv != NS_BASE_STREAM_WOULD_BLOCK, "sink must be a blocking stream");
+ mSink = 0;
+ break;
+ }
+ NS_ASSERTION(bytesWritten <= count, "wrote too much");
+ count -= bytesWritten;
+ }
+ return NS_OK;
+}
+
+NS_METHOD
+nsInputStreamTee::WriteSegmentFun(nsIInputStream *in, void *closure, const char *fromSegment,
+ PRUint32 offset, PRUint32 count, PRUint32 *writeCount)
+{
+ nsInputStreamTee *tee = NS_REINTERPRET_CAST(nsInputStreamTee *, closure);
+
+ nsresult rv = tee->mWriter(in, tee->mClosure, fromSegment, offset, count, writeCount);
+ if (NS_FAILED(rv) || (*writeCount == 0)) {
+ NS_ASSERTION((NS_FAILED(rv) ? (*writeCount == 0) : PR_TRUE),
+ "writer returned an error with non-zero writeCount");
+ return rv;
+ }
+
+ return tee->TeeSegment(fromSegment, *writeCount);
+}
+
+NS_IMPL_ISUPPORTS2(nsInputStreamTee,
+ nsIInputStreamTee,
+ nsIInputStream)
+
+NS_IMETHODIMP
+nsInputStreamTee::Close()
+{
+ NS_ENSURE_TRUE(mSource, NS_ERROR_NOT_INITIALIZED);
+ nsresult rv = mSource->Close();
+ mSource = 0;
+ mSink = 0;
+ return rv;
+}
+
+NS_IMETHODIMP
+nsInputStreamTee::Available(PRUint32 *avail)
+{
+ NS_ENSURE_TRUE(mSource, NS_ERROR_NOT_INITIALIZED);
+ return mSource->Available(avail);
+}
+
+NS_IMETHODIMP
+nsInputStreamTee::Read(char *buf, PRUint32 count, PRUint32 *bytesRead)
+{
+ NS_ENSURE_TRUE(mSource, NS_ERROR_NOT_INITIALIZED);
+
+ nsresult rv = mSource->Read(buf, count, bytesRead);
+ if (NS_FAILED(rv) || (*bytesRead == 0))
+ return rv;
+
+ return TeeSegment(buf, *bytesRead);
+}
+
+NS_IMETHODIMP
+nsInputStreamTee::ReadSegments(nsWriteSegmentFun writer,
+ void *closure,
+ PRUint32 count,
+ PRUint32 *bytesRead)
+{
+ NS_ENSURE_TRUE(mSource, NS_ERROR_NOT_INITIALIZED);
+
+ mWriter = writer;
+ mClosure = closure;
+
+ return mSource->ReadSegments(WriteSegmentFun, this, count, bytesRead);
+}
+
+NS_IMETHODIMP
+nsInputStreamTee::IsNonBlocking(PRBool *result)
+{
+ NS_ENSURE_TRUE(mSource, NS_ERROR_NOT_INITIALIZED);
+ return mSource->IsNonBlocking(result);
+}
+
+NS_IMETHODIMP
+nsInputStreamTee::SetSource(nsIInputStream *source)
+{
+ mSource = source;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsInputStreamTee::GetSource(nsIInputStream **source)
+{
+ NS_IF_ADDREF(*source = mSource);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsInputStreamTee::SetSink(nsIOutputStream *sink)
+{
+#ifdef DEBUG
+ if (sink) {
+ PRBool nonBlocking;
+ nsresult rv = sink->IsNonBlocking(&nonBlocking);
+ if (NS_FAILED(rv) || nonBlocking)
+ NS_ERROR("sink should be a blocking stream");
+ }
+#endif
+ mSink = sink;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsInputStreamTee::GetSink(nsIOutputStream **sink)
+{
+ NS_IF_ADDREF(*sink = mSink);
+ return NS_OK;
+}
+
+// factory method
+
+NS_COM nsresult
+NS_NewInputStreamTee(nsIInputStream **result,
+ nsIInputStream *source,
+ nsIOutputStream *sink)
+{
+ nsresult rv;
+
+ nsCOMPtr<nsIInputStreamTee> tee;
+ NS_NEWXPCOM(tee, nsInputStreamTee);
+ if (!tee)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ rv = tee->SetSource(source);
+ if (NS_FAILED(rv)) return rv;
+
+ rv = tee->SetSink(sink);
+ if (NS_FAILED(rv)) return rv;
+
+ NS_ADDREF(*result = tee);
+ return rv;
+}
diff --git a/src/libs/xpcom18a4/xpcom/io/nsLinebreakConverter.cpp b/src/libs/xpcom18a4/xpcom/io/nsLinebreakConverter.cpp
new file mode 100644
index 00000000..88905f97
--- /dev/null
+++ b/src/libs/xpcom18a4/xpcom/io/nsLinebreakConverter.cpp
@@ -0,0 +1,495 @@
+/* -*- Mode: C++; tab-width: 2; 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 ***** */
+
+#include "nsLinebreakConverter.h"
+
+#include "nsMemory.h"
+#include "nsCRT.h"
+
+
+#if defined(XP_WIN) && defined(_MSC_VER) && (_MSC_VER <= 1100)
+#define LOSER_CHAR_CAST(t) (char *)(t)
+#define LOSER_UNICHAR_CAST(t) (PRUnichar *)(t)
+#else
+#define LOSER_CHAR_CAST(t) (t)
+#define LOSER_UNICHAR_CAST(t) (t)
+#endif
+
+/*----------------------------------------------------------------------------
+ GetLinebreakString
+
+ Could make this inline
+----------------------------------------------------------------------------*/
+static const char* GetLinebreakString(nsLinebreakConverter::ELinebreakType aBreakType)
+{
+ static const char* const sLinebreaks[] = {
+ "", // any
+ NS_LINEBREAK, // platform
+ LFSTR, // content
+ CRLF, // net
+ CRSTR, // Mac
+ LFSTR, // Unix
+ CRLF, // Windows
+ nsnull
+ };
+
+ return sLinebreaks[aBreakType];
+}
+
+
+/*----------------------------------------------------------------------------
+ AppendLinebreak
+
+ Wee inline method to append a line break. Modifies ioDest.
+----------------------------------------------------------------------------*/
+template<class T>
+void AppendLinebreak(T*& ioDest, const char* lineBreakStr)
+{
+ *ioDest++ = *lineBreakStr;
+
+ if (lineBreakStr[1])
+ *ioDest++ = lineBreakStr[1];
+}
+
+/*----------------------------------------------------------------------------
+ CountChars
+
+ Counts occurrences of breakStr in aSrc
+----------------------------------------------------------------------------*/
+template<class T>
+PRInt32 CountLinebreaks(const T* aSrc, PRInt32 inLen, const char* breakStr)
+{
+ const T* src = aSrc;
+ const T* srcEnd = aSrc + inLen;
+ PRInt32 theCount = 0;
+
+ while (src < srcEnd)
+ {
+ if (*src == *breakStr)
+ {
+ src ++;
+ if (src < srcEnd && breakStr[1] && *src == breakStr[1])
+ src ++;
+
+ theCount ++;
+ }
+ else
+ {
+ src ++;
+ }
+ }
+
+ return theCount;
+}
+
+
+/*----------------------------------------------------------------------------
+ ConvertBreaks
+
+ ioLen *includes* a terminating null, if any
+----------------------------------------------------------------------------*/
+template<class T>
+static T* ConvertBreaks(const T* inSrc, PRInt32& ioLen, const char* srcBreak, const char* destBreak)
+{
+ NS_ASSERTION(inSrc && srcBreak && destBreak, "Got a null string");
+
+ T* resultString = nsnull;
+
+ // handle the no conversion case
+ if (nsCRT::strcmp(srcBreak, destBreak) == 0)
+ {
+ resultString = (T *)nsMemory::Alloc(sizeof(T) * ioLen);
+ if (!resultString) return nsnull;
+ memcpy(resultString, inSrc, sizeof(T) * ioLen); // includes the null, if any
+ return resultString;
+ }
+
+ PRInt32 srcBreakLen = strlen(srcBreak);
+ PRInt32 destBreakLen = strlen(destBreak);
+
+ // handle the easy case, where the string length does not change, and the
+ // breaks are only 1 char long, i.e. CR <-> LF
+ if (srcBreakLen == destBreakLen && srcBreakLen == 1)
+ {
+ resultString = (T *)nsMemory::Alloc(sizeof(T) * ioLen);
+ if (!resultString) return nsnull;
+
+ const T* src = inSrc;
+ const T* srcEnd = inSrc + ioLen; // includes null, if any
+ T* dst = resultString;
+
+ char srcBreakChar = *srcBreak; // we know it's one char long already
+ char dstBreakChar = *destBreak;
+
+ while (src < srcEnd)
+ {
+ if (*src == srcBreakChar)
+ {
+ *dst++ = dstBreakChar;
+ src++;
+ }
+ else
+ {
+ *dst++ = *src++;
+ }
+ }
+
+ // ioLen does not change
+ }
+ else
+ {
+ // src and dest termination is different length. Do it a slower way.
+
+ // count linebreaks in src. Assumes that chars in 2-char linebreaks are unique.
+ PRInt32 numLinebreaks = CountLinebreaks(inSrc, ioLen, srcBreak);
+
+ PRInt32 newBufLen = ioLen - (numLinebreaks * srcBreakLen) + (numLinebreaks * destBreakLen);
+ resultString = (T *)nsMemory::Alloc(sizeof(T) * newBufLen);
+ if (!resultString) return nsnull;
+
+ const T* src = inSrc;
+ const T* srcEnd = inSrc + ioLen; // includes null, if any
+ T* dst = resultString;
+
+ while (src < srcEnd)
+ {
+ if (*src == *srcBreak)
+ {
+ *dst++ = *destBreak;
+ if (destBreak[1])
+ *dst++ = destBreak[1];
+
+ src ++;
+ if (src < srcEnd && srcBreak[1] && *src == srcBreak[1])
+ src ++;
+ }
+ else
+ {
+ *dst++ = *src++;
+ }
+ }
+
+ ioLen = newBufLen;
+ }
+
+ return resultString;
+}
+
+
+/*----------------------------------------------------------------------------
+ ConvertBreaksInSitu
+
+ Convert breaks in situ. Can only do this if the linebreak length
+ does not change.
+----------------------------------------------------------------------------*/
+template<class T>
+static void ConvertBreaksInSitu(T* inSrc, PRInt32 inLen, char srcBreak, char destBreak)
+{
+ T* src = inSrc;
+ T* srcEnd = inSrc + inLen;
+
+ while (src < srcEnd)
+ {
+ if (*src == srcBreak)
+ *src = destBreak;
+
+ src ++;
+ }
+}
+
+
+/*----------------------------------------------------------------------------
+ ConvertUnknownBreaks
+
+ Convert unknown line breaks to the specified break.
+
+ This will convert CRLF pairs to one break, and single CR or LF to a break.
+----------------------------------------------------------------------------*/
+template<class T>
+static T* ConvertUnknownBreaks(const T* inSrc, PRInt32& ioLen, const char* destBreak)
+{
+ const T* src = inSrc;
+ const T* srcEnd = inSrc + ioLen; // includes null, if any
+
+ PRInt32 destBreakLen = strlen(destBreak);
+ PRInt32 finalLen = 0;
+
+ while (src < srcEnd)
+ {
+ if (*src == nsCRT::CR)
+ {
+ if (src < srcEnd && src[1] == nsCRT::LF)
+ {
+ // CRLF
+ finalLen += destBreakLen;
+ src ++;
+ }
+ else
+ {
+ // Lone CR
+ finalLen += destBreakLen;
+ }
+ }
+ else if (*src == nsCRT::LF)
+ {
+ // Lone LF
+ finalLen += destBreakLen;
+ }
+ else
+ {
+ finalLen ++;
+ }
+ src ++;
+ }
+
+ T* resultString = (T *)nsMemory::Alloc(sizeof(T) * finalLen);
+ if (!resultString) return nsnull;
+
+ src = inSrc;
+ srcEnd = inSrc + ioLen; // includes null, if any
+
+ T* dst = resultString;
+
+ while (src < srcEnd)
+ {
+ if (*src == nsCRT::CR)
+ {
+ if (src < srcEnd && src[1] == nsCRT::LF)
+ {
+ // CRLF
+ AppendLinebreak(dst, destBreak);
+ src ++;
+ }
+ else
+ {
+ // Lone CR
+ AppendLinebreak(dst, destBreak);
+ }
+ }
+ else if (*src == nsCRT::LF)
+ {
+ // Lone LF
+ AppendLinebreak(dst, destBreak);
+ }
+ else
+ {
+ *dst++ = *src;
+ }
+ src ++;
+ }
+
+ ioLen = finalLen;
+ return resultString;
+}
+
+
+#ifdef XP_MAC
+#pragma mark -
+#endif
+
+
+/*----------------------------------------------------------------------------
+ ConvertLineBreaks
+
+----------------------------------------------------------------------------*/
+char* nsLinebreakConverter::ConvertLineBreaks(const char* aSrc,
+ ELinebreakType aSrcBreaks, ELinebreakType aDestBreaks, PRInt32 aSrcLen, PRInt32* outLen)
+{
+ NS_ASSERTION(aDestBreaks != eLinebreakAny, "Invalid parameter");
+ if (!aSrc) return nsnull;
+
+ PRInt32 sourceLen = (aSrcLen == kIgnoreLen) ? strlen(aSrc) + 1 : aSrcLen;
+
+ char* resultString;
+ if (aSrcBreaks == eLinebreakAny)
+ resultString = ConvertUnknownBreaks(LOSER_CHAR_CAST(aSrc), sourceLen, GetLinebreakString(aDestBreaks));
+ else
+ resultString = ConvertBreaks(LOSER_CHAR_CAST(aSrc), sourceLen, GetLinebreakString(aSrcBreaks), GetLinebreakString(aDestBreaks));
+
+ if (outLen)
+ *outLen = sourceLen;
+ return resultString;
+}
+
+
+/*----------------------------------------------------------------------------
+ ConvertLineBreaksInSitu
+
+----------------------------------------------------------------------------*/
+nsresult nsLinebreakConverter::ConvertLineBreaksInSitu(char **ioBuffer, ELinebreakType aSrcBreaks,
+ ELinebreakType aDestBreaks, PRInt32 aSrcLen, PRInt32* outLen)
+{
+ NS_ASSERTION(ioBuffer && *ioBuffer, "Null pointer passed");
+ if (!ioBuffer || !*ioBuffer) return NS_ERROR_NULL_POINTER;
+
+ NS_ASSERTION(aDestBreaks != eLinebreakAny, "Invalid parameter");
+
+ PRInt32 sourceLen = (aSrcLen == kIgnoreLen) ? strlen(*ioBuffer) + 1 : aSrcLen;
+
+ // can we convert in-place?
+ const char* srcBreaks = GetLinebreakString(aSrcBreaks);
+ const char* dstBreaks = GetLinebreakString(aDestBreaks);
+
+ if ( (aSrcBreaks != eLinebreakAny) &&
+ (strlen(srcBreaks) == 1) &&
+ (strlen(dstBreaks) == 1) )
+ {
+ ConvertBreaksInSitu(*ioBuffer, sourceLen, *srcBreaks, *dstBreaks);
+ if (outLen)
+ *outLen = sourceLen;
+ }
+ else
+ {
+ char* destBuffer;
+
+ if (aSrcBreaks == eLinebreakAny)
+ destBuffer = ConvertUnknownBreaks(*ioBuffer, sourceLen, dstBreaks);
+ else
+ destBuffer = ConvertBreaks(*ioBuffer, sourceLen, srcBreaks, dstBreaks);
+
+ if (!destBuffer) return NS_ERROR_OUT_OF_MEMORY;
+ *ioBuffer = destBuffer;
+ if (outLen)
+ *outLen = sourceLen;
+ }
+
+ return NS_OK;
+}
+
+
+/*----------------------------------------------------------------------------
+ ConvertUnicharLineBreaks
+
+----------------------------------------------------------------------------*/
+PRUnichar* nsLinebreakConverter::ConvertUnicharLineBreaks(const PRUnichar* aSrc,
+ ELinebreakType aSrcBreaks, ELinebreakType aDestBreaks, PRInt32 aSrcLen, PRInt32* outLen)
+{
+ NS_ASSERTION(aDestBreaks != eLinebreakAny, "Invalid parameter");
+ if (!aSrc) return nsnull;
+
+ PRInt32 bufLen = (aSrcLen == kIgnoreLen) ? nsCRT::strlen(aSrc) + 1 : aSrcLen;
+
+ PRUnichar* resultString;
+ if (aSrcBreaks == eLinebreakAny)
+ resultString = ConvertUnknownBreaks(LOSER_UNICHAR_CAST(aSrc), bufLen, GetLinebreakString(aDestBreaks));
+ else
+ resultString = ConvertBreaks(LOSER_UNICHAR_CAST(aSrc), bufLen, GetLinebreakString(aSrcBreaks), GetLinebreakString(aDestBreaks));
+
+ if (outLen)
+ *outLen = bufLen;
+ return resultString;
+}
+
+
+/*----------------------------------------------------------------------------
+ ConvertStringLineBreaks
+
+----------------------------------------------------------------------------*/
+nsresult nsLinebreakConverter::ConvertUnicharLineBreaksInSitu(PRUnichar **ioBuffer,
+ ELinebreakType aSrcBreaks, ELinebreakType aDestBreaks, PRInt32 aSrcLen, PRInt32* outLen)
+{
+ NS_ASSERTION(ioBuffer && *ioBuffer, "Null pointer passed");
+ if (!ioBuffer || !*ioBuffer) return NS_ERROR_NULL_POINTER;
+ NS_ASSERTION(aDestBreaks != eLinebreakAny, "Invalid parameter");
+
+ PRInt32 sourceLen = (aSrcLen == kIgnoreLen) ? nsCRT::strlen(*ioBuffer) + 1 : aSrcLen;
+
+ // can we convert in-place?
+ const char* srcBreaks = GetLinebreakString(aSrcBreaks);
+ const char* dstBreaks = GetLinebreakString(aDestBreaks);
+
+ if ( (aSrcBreaks != eLinebreakAny) &&
+ (strlen(srcBreaks) == 1) &&
+ (strlen(dstBreaks) == 1) )
+ {
+ ConvertBreaksInSitu(*ioBuffer, sourceLen, *srcBreaks, *dstBreaks);
+ if (outLen)
+ *outLen = sourceLen;
+ }
+ else
+ {
+ PRUnichar* destBuffer;
+
+ if (aSrcBreaks == eLinebreakAny)
+ destBuffer = ConvertUnknownBreaks(*ioBuffer, sourceLen, dstBreaks);
+ else
+ destBuffer = ConvertBreaks(*ioBuffer, sourceLen, srcBreaks, dstBreaks);
+
+ if (!destBuffer) return NS_ERROR_OUT_OF_MEMORY;
+ *ioBuffer = destBuffer;
+ if (outLen)
+ *outLen = sourceLen;
+ }
+
+ return NS_OK;
+}
+
+/*----------------------------------------------------------------------------
+ ConvertStringLineBreaks
+
+----------------------------------------------------------------------------*/
+nsresult nsLinebreakConverter::ConvertStringLineBreaks(nsString& ioString,
+ ELinebreakType aSrcBreaks, ELinebreakType aDestBreaks)
+{
+
+ NS_ASSERTION(aDestBreaks != eLinebreakAny, "Invalid parameter");
+
+ // nothing to do
+ if (ioString.IsEmpty()) return NS_OK;
+
+ nsresult rv;
+
+ // remember the old buffer in case
+ // we blow it away later
+ nsString::char_iterator stringBuf;
+ ioString.BeginWriting(stringBuf);
+
+ PRInt32 newLen;
+
+ rv = ConvertUnicharLineBreaksInSitu(&stringBuf,
+ aSrcBreaks, aDestBreaks,
+ ioString.Length() + 1, &newLen);
+ if (NS_FAILED(rv)) return rv;
+
+ if (stringBuf != ioString.get())
+ ioString.Adopt(stringBuf);
+
+ return NS_OK;
+}
+
+
+
diff --git a/src/libs/xpcom18a4/xpcom/io/nsLinebreakConverter.h b/src/libs/xpcom18a4/xpcom/io/nsLinebreakConverter.h
new file mode 100644
index 00000000..ec4bce57
--- /dev/null
+++ b/src/libs/xpcom18a4/xpcom/io/nsLinebreakConverter.h
@@ -0,0 +1,156 @@
+/* -*- Mode: C++; tab-width: 2; 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 ***** */
+
+#ifndef nsLinebreakConverter_h_
+#define nsLinebreakConverter_h_
+
+
+#include "nscore.h"
+#include "nsString.h"
+
+// utility class for converting between different line breaks.
+
+class NS_COM nsLinebreakConverter
+{
+public:
+
+ // Note: enum must match char* array in GetLinebreakString
+ typedef enum {
+ eLinebreakAny, // any kind of linebreak (i.e. "don't care" source)
+
+ eLinebreakPlatform, // platform linebreak
+ eLinebreakContent, // Content model linebreak (LF)
+ eLinebreakNet, // Form submission linebreak (CRLF)
+
+ eLinebreakMac, // CR
+ eLinebreakUnix, // LF
+ eLinebreakWindows // CRLF
+
+ } ELinebreakType;
+
+ enum {
+ kIgnoreLen = -1
+ };
+
+ /* ConvertLineBreaks
+ * Convert line breaks in the supplied string, allocating and returning
+ * a new buffer. Returns nsnull on failure.
+ * @param aSrc: the source string. if aSrcLen == kIgnoreLen this string is assumed
+ * to be null terminated, otherwise it must be at least aSrcLen long.
+ * @param aSrcBreaks: the line breaks in the source. If unknown, pass eLinebreakAny.
+ * If known, pass the known value, as this may be more efficient.
+ * @param aDestBreaks: the line breaks you want in the output.
+ * @param aSrcLen: length of the source. If -1, the source is assumed to be a null-
+ * terminated string.
+ * @param aOutLen: used to return character length of returned buffer, if not null.
+ */
+ static char* ConvertLineBreaks(const char* aSrc,
+ ELinebreakType aSrcBreaks, ELinebreakType aDestBreaks,
+ PRInt32 aSrcLen = kIgnoreLen, PRInt32* aOutLen = nsnull);
+
+
+ /* ConvertUnicharLineBreaks
+ * Convert line breaks in the supplied string, allocating and returning
+ * a new buffer. Returns nsnull on failure.
+ * @param aSrc: the source string. if aSrcLen == kIgnoreLen this string is assumed
+ * to be null terminated, otherwise it must be at least aSrcLen long.
+ * @param aSrcBreaks: the line breaks in the source. If unknown, pass eLinebreakAny.
+ * If known, pass the known value, as this may be more efficient.
+ * @param aDestBreaks: the line breaks you want in the output.
+ * @param aSrcLen: length of the source, in characters. If -1, the source is assumed to be a null-
+ * terminated string.
+ * @param aOutLen: used to return character length of returned buffer, if not null.
+ */
+ static PRUnichar* ConvertUnicharLineBreaks(const PRUnichar* aSrc,
+ ELinebreakType aSrcBreaks, ELinebreakType aDestBreaks,
+ PRInt32 aSrcLen = kIgnoreLen, PRInt32* aOutLen = nsnull);
+
+
+ /* ConvertStringLineBreaks
+ * Convert line breaks in the supplied string, changing the string buffer (i.e. in-place conversion)
+ * @param ioString: the string to be converted.
+ * @param aSrcBreaks: the line breaks in the source. If unknown, pass eLinebreakAny.
+ * If known, pass the known value, as this may be more efficient.
+ * @param aDestBreaks: the line breaks you want in the output.
+ * @param aSrcLen: length of the source, in characters. If -1, the source is assumed to be a null-
+ * terminated string.
+ */
+ static nsresult ConvertStringLineBreaks(nsString& ioString, ELinebreakType aSrcBreaks, ELinebreakType aDestBreaks);
+
+
+ /* ConvertLineBreaksInSitu
+ * Convert line breaks in place if possible. NOTE: THIS MAY REALLOCATE THE BUFFER,
+ * BUT IT WON'T FREE THE OLD BUFFER (because it doesn't know how). So be prepared
+ * to keep a copy of the old pointer, and free it if this passes back a new pointer.
+ * ALSO NOTE: DON'T PASS A STATIC STRING POINTER TO THIS FUNCTION.
+ *
+ * @param ioBuffer: the source buffer. if aSrcLen == kIgnoreLen this string is assumed
+ * to be null terminated, otherwise it must be at least aSrcLen long.
+ * @param aSrcBreaks: the line breaks in the source. If unknown, pass eLinebreakAny.
+ * If known, pass the known value, as this may be more efficient.
+ * @param aDestBreaks: the line breaks you want in the output.
+ * @param aSrcLen: length of the source. If -1, the source is assumed to be a null-
+ * terminated string.
+ * @param aOutLen: used to return character length of returned buffer, if not null.
+ */
+ static nsresult ConvertLineBreaksInSitu(char **ioBuffer, ELinebreakType aSrcBreaks, ELinebreakType aDestBreaks,
+ PRInt32 aSrcLen = kIgnoreLen, PRInt32* aOutLen = nsnull);
+
+
+ /* ConvertUnicharLineBreaksInSitu
+ * Convert line breaks in place if possible. NOTE: THIS MAY REALLOCATE THE BUFFER,
+ * BUT IT WON'T FREE THE OLD BUFFER (because it doesn't know how). So be prepared
+ * to keep a copy of the old pointer, and free it if this passes back a new pointer.
+ *
+ * @param ioBuffer: the source buffer. if aSrcLen == kIgnoreLen this string is assumed
+ * to be null terminated, otherwise it must be at least aSrcLen long.
+ * @param aSrcBreaks: the line breaks in the source. If unknown, pass eLinebreakAny.
+ * If known, pass the known value, as this may be more efficient.
+ * @param aDestBreaks: the line breaks you want in the output.
+ * @param aSrcLen: length of the source in characters. If -1, the source is assumed to be a null-
+ * terminated string.
+ * @param aOutLen: used to return character length of returned buffer, if not null.
+ */
+ static nsresult ConvertUnicharLineBreaksInSitu(PRUnichar **ioBuffer, ELinebreakType aSrcBreaks, ELinebreakType aDestBreaks,
+ PRInt32 aSrcLen = kIgnoreLen, PRInt32* aOutLen = nsnull);
+
+};
+
+
+
+
+#endif // nsLinebreakConverter_h_
diff --git a/src/libs/xpcom18a4/xpcom/io/nsLocalFile.h b/src/libs/xpcom18a4/xpcom/io/nsLocalFile.h
new file mode 100644
index 00000000..8201affb
--- /dev/null
+++ b/src/libs/xpcom18a4/xpcom/io/nsLocalFile.h
@@ -0,0 +1,118 @@
+/* -*- Mode: C++; tab-width: 2; 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 client code, released
+ * March 31, 1998.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1998-1999
+ * 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 *****
+ *
+ * This Original Code has been modified by IBM Corporation. Modifications made by IBM
+ * described herein are Copyright (c) International Business Machines Corporation, 2000.
+ * Modifications to Mozilla code or documentation identified per MPL Section 3.3
+ *
+ * Date Modified by Description of modification
+ * 04/20/2000 IBM Corp. OS/2 build.
+ */
+
+#ifndef _NS_LOCAL_FILE_H_
+#define _NS_LOCAL_FILE_H_
+
+#define NS_LOCAL_FILE_CID {0x2e23e220, 0x60be, 0x11d3, {0x8c, 0x4a, 0x00, 0x00, 0x64, 0x65, 0x73, 0x74}}
+
+#define NS_DECL_NSLOCALFILE_UNICODE_METHODS \
+ nsresult AppendUnicode(const PRUnichar *aNode); \
+ nsresult GetUnicodeLeafName(PRUnichar **aLeafName); \
+ nsresult SetUnicodeLeafName(const PRUnichar *aLeafName); \
+ nsresult CopyToUnicode(nsIFile *aNewParentDir, const PRUnichar *aNewLeafName); \
+ nsresult CopyToFollowingLinksUnicode(nsIFile *aNewParentDir, const PRUnichar *aNewLeafName); \
+ nsresult MoveToUnicode(nsIFile *aNewParentDir, const PRUnichar *aNewLeafName); \
+ nsresult GetUnicodeTarget(PRUnichar **aTarget); \
+ nsresult GetUnicodePath(PRUnichar **aPath); \
+ nsresult InitWithUnicodePath(const PRUnichar *aPath); \
+ nsresult AppendRelativeUnicodePath(const PRUnichar *aRelativePath);
+
+// nsXPComInit needs to know about how we are implemented,
+// so here we will export it. Other users should not depend
+// on this.
+
+#include <errno.h>
+#include "nsILocalFile.h"
+
+#ifdef XP_WIN
+#include "nsLocalFileWin.h"
+#elif defined(XP_MACOSX) && !defined(VBOX_MACOSX_FOLLOWS_UNIX_IO)
+#include "nsLocalFileOSX.h"
+#elif defined(XP_MAC)
+#include "nsLocalFileMac.h"
+#elif defined(XP_UNIX) || defined(XP_BEOS)
+#include "nsLocalFileUnix.h"
+#elif defined(XP_OS2)
+#include "nsLocalFileOS2.h"
+#else
+#error NOT_IMPLEMENTED
+#endif
+
+#define NSRESULT_FOR_RETURN(ret) (((ret) < 0) ? NSRESULT_FOR_ERRNO() : NS_OK)
+
+inline nsresult
+nsresultForErrno(int err)
+{
+ switch (err) {
+ case 0:
+ return NS_OK;
+ case ENOENT:
+ return NS_ERROR_FILE_TARGET_DOES_NOT_EXIST;
+ case ENOTDIR:
+ return NS_ERROR_FILE_DESTINATION_NOT_DIR;
+#ifdef ENOLINK
+ case ENOLINK:
+ return NS_ERROR_FILE_UNRESOLVABLE_SYMLINK;
+#endif /* ENOLINK */
+ case EEXIST:
+ return NS_ERROR_FILE_ALREADY_EXISTS;
+#ifdef EPERM
+ case EPERM:
+#endif /* EPERM */
+ case EACCES:
+ return NS_ERROR_FILE_ACCESS_DENIED;
+ default:
+ return NS_ERROR_FAILURE;
+ }
+}
+
+#define NSRESULT_FOR_ERRNO() nsresultForErrno(errno)
+
+void NS_StartupLocalFile();
+void NS_ShutdownLocalFile();
+
+#endif
diff --git a/src/libs/xpcom18a4/xpcom/io/nsLocalFileCommon.cpp b/src/libs/xpcom18a4/xpcom/io/nsLocalFileCommon.cpp
new file mode 100644
index 00000000..664c5304
--- /dev/null
+++ b/src/libs/xpcom18a4/xpcom/io/nsLocalFileCommon.cpp
@@ -0,0 +1,278 @@
+/* -*- 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) 1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Doug Turner <dougt@netscape.com>
+ *
+ * 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 "nsLocalFile.h" // includes platform-specific headers
+#include "nsLocalFileUnicode.h"
+
+#include "nsString.h"
+#include "nsCOMPtr.h"
+#include "nsReadableUtils.h"
+#include "nsPrintfCString.h"
+#include "nsCRT.h"
+
+#ifdef XP_WIN
+#include <string.h>
+#endif
+
+
+void NS_StartupLocalFile()
+{
+ nsLocalFile::GlobalInit();
+}
+
+void NS_ShutdownLocalFile()
+{
+ nsLocalFile::GlobalShutdown();
+}
+
+#if (!defined(XP_MAC) && !defined(XP_MACOSX)) || defined(VBOX_MACOSX_FOLLOWS_UNIX_IO)
+NS_IMETHODIMP
+nsLocalFile::InitWithFile(nsILocalFile *aFile)
+{
+ NS_ENSURE_ARG(aFile);
+
+ nsCAutoString path;
+ aFile->GetNativePath(path);
+ if (path.IsEmpty())
+ return NS_ERROR_INVALID_ARG;
+ return InitWithNativePath(path);
+}
+#endif
+
+#if defined(XP_MAC)
+#define kMaxFilenameLength 31
+#else
+#define kMaxFilenameLength 255
+#endif
+
+NS_IMETHODIMP
+nsLocalFile::CreateUnique(PRUint32 type, PRUint32 attributes)
+{
+ nsresult rv = Create(type, attributes);
+
+ if (NS_SUCCEEDED(rv)) return NS_OK;
+ if (rv != NS_ERROR_FILE_ALREADY_EXISTS) return rv;
+
+ nsCAutoString leafName;
+ rv = GetNativeLeafName(leafName);
+
+ if (NS_FAILED(rv)) return rv;
+
+ const char* lastDot = strrchr(leafName.get(), '.');
+ char suffix[kMaxFilenameLength + 1] = "";
+ if (lastDot)
+ {
+ strncpy(suffix, lastDot, kMaxFilenameLength); // include '.'
+ suffix[kMaxFilenameLength] = 0; // make sure it's null terminated
+ leafName.SetLength(lastDot - leafName.get()); // strip suffix and dot.
+ }
+
+ const int maxRootLength = (kMaxFilenameLength - 4) - strlen(suffix) - 1;
+
+ if ((int)leafName.Length() > (int)maxRootLength)
+ leafName.SetLength(maxRootLength);
+
+ for (short indx = 1; indx < 10000; indx++)
+ {
+ // start with "Picture-1.jpg" after "Picture.jpg" exists
+ SetNativeLeafName(leafName +
+ nsPrintfCString("-%d", indx) +
+ nsDependentCString(suffix));
+
+ rv = Create(type, attributes);
+
+ if (NS_SUCCEEDED(rv) || rv != NS_ERROR_FILE_ALREADY_EXISTS)
+ {
+ return rv;
+ }
+ }
+
+ // The disk is full, sort of
+ return NS_ERROR_FILE_TOO_BIG;
+}
+
+#if defined(XP_MAC)
+static const PRUnichar kPathSeparatorChar = ':';
+#elif defined(XP_WIN) || defined(XP_OS2)
+static const PRUnichar kPathSeparatorChar = '\\';
+#elif defined(XP_UNIX) || defined(XP_BEOS)
+static const PRUnichar kPathSeparatorChar = '/';
+#else
+#error Need to define file path separator for your platform
+#endif
+
+#if defined(XP_MAC)
+static const char kSlashStr[] = "/";
+static const char kESCSlashStr[] = "%2F";
+#endif
+
+static PRInt32 SplitPath(PRUnichar *path, PRUnichar **nodeArray, PRInt32 arrayLen)
+{
+ if (*path == 0)
+ return 0;
+
+ PRUnichar **nodePtr = nodeArray;
+ if (*path == kPathSeparatorChar)
+ path++;
+ *nodePtr++ = path;
+
+ for (PRUnichar *cp = path; *cp != 0; cp++) {
+ if (*cp == kPathSeparatorChar) {
+ *cp++ = 0;
+ if (*cp == 0)
+ break;
+ if (nodePtr - nodeArray >= arrayLen)
+ return -1;
+ *nodePtr++ = cp;
+ }
+ }
+ return nodePtr - nodeArray;
+}
+
+
+NS_IMETHODIMP
+nsLocalFile::GetRelativeDescriptor(nsILocalFile *fromFile, nsACString& _retval)
+{
+ NS_ENSURE_ARG_POINTER(fromFile);
+ const PRInt32 kMaxNodesInPath = 32;
+
+ //
+ // _retval will be UTF-8 encoded
+ //
+
+ nsresult rv;
+ _retval.Truncate(0);
+
+ nsAutoString thisPath, fromPath;
+ PRUnichar *thisNodes[kMaxNodesInPath], *fromNodes[kMaxNodesInPath];
+ PRInt32 thisNodeCnt, fromNodeCnt, nodeIndex;
+
+ rv = GetPath(thisPath);
+ if (NS_FAILED(rv))
+ return rv;
+ rv = fromFile->GetPath(fromPath);
+ if (NS_FAILED(rv))
+ return rv;
+
+ // get raw pointer to mutable string buffer
+ PRUnichar *thisPathPtr; thisPath.BeginWriting(thisPathPtr);
+ PRUnichar *fromPathPtr; fromPath.BeginWriting(fromPathPtr);
+
+ thisNodeCnt = SplitPath(thisPathPtr, thisNodes, kMaxNodesInPath);
+ fromNodeCnt = SplitPath(fromPathPtr, fromNodes, kMaxNodesInPath);
+ if (thisNodeCnt < 0 || fromNodeCnt < 0)
+ return NS_ERROR_FAILURE;
+
+ for (nodeIndex = 0; nodeIndex < thisNodeCnt && nodeIndex < fromNodeCnt; ++nodeIndex) {
+#ifdef XP_WIN
+ if (_wcsicmp(thisNodes[nodeIndex], fromNodes[nodeIndex]))
+ break;
+#else
+ if (nsCRT::strcmp(thisNodes[nodeIndex], fromNodes[nodeIndex]))
+ break;
+#endif
+ }
+
+ PRInt32 branchIndex = nodeIndex;
+ for (nodeIndex = branchIndex; nodeIndex < fromNodeCnt; nodeIndex++)
+ _retval.AppendLiteral("../");
+ for (nodeIndex = branchIndex; nodeIndex < thisNodeCnt; nodeIndex++) {
+ NS_ConvertUCS2toUTF8 nodeStr(thisNodes[nodeIndex]);
+#ifdef XP_MAC
+ nodeStr.ReplaceSubstring(kSlashStr, kESCSlashStr);
+#endif
+ _retval.Append(nodeStr);
+ if (nodeIndex + 1 < thisNodeCnt)
+ _retval.Append('/');
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::SetRelativeDescriptor(nsILocalFile *fromFile, const nsACString& relativeDesc)
+{
+ NS_NAMED_LITERAL_CSTRING(kParentDirStr, "../");
+
+ nsCOMPtr<nsIFile> targetFile;
+ nsresult rv = fromFile->Clone(getter_AddRefs(targetFile));
+ if (NS_FAILED(rv))
+ return rv;
+
+ //
+ // relativeDesc is UTF-8 encoded
+ //
+
+ nsCString::const_iterator strBegin, strEnd;
+ relativeDesc.BeginReading(strBegin);
+ relativeDesc.EndReading(strEnd);
+
+ nsCString::const_iterator nodeBegin(strBegin), nodeEnd(strEnd);
+ nsCString::const_iterator pos(strBegin);
+
+ nsCOMPtr<nsIFile> parentDir;
+ while (FindInReadable(kParentDirStr, nodeBegin, nodeEnd)) {
+ rv = targetFile->GetParent(getter_AddRefs(parentDir));
+ if (NS_FAILED(rv))
+ return rv;
+ targetFile = parentDir;
+
+ nodeBegin = nodeEnd;
+ pos = nodeEnd;
+ nodeEnd = strEnd;
+ }
+
+ nodeBegin = nodeEnd = pos;
+ while (nodeEnd != strEnd) {
+ FindCharInReadable('/', nodeEnd, strEnd);
+#ifdef XP_MAC
+ nsCAutoString nodeString(Substring(nodeBegin, nodeEnd));
+ nodeString.ReplaceSubstring(kESCSlashStr, kSlashStr);
+ targetFile->Append(NS_ConvertUTF8toUCS2(nodeString));
+#else
+ targetFile->Append(NS_ConvertUTF8toUCS2(Substring(nodeBegin, nodeEnd)));
+#endif
+ if (nodeEnd != strEnd) // If there's more left in the string, inc over the '/' nodeEnd is on.
+ ++nodeEnd;
+ nodeBegin = nodeEnd;
+ }
+
+ nsCOMPtr<nsILocalFile> targetLocalFile(do_QueryInterface(targetFile));
+ return InitWithFile(targetLocalFile);
+}
diff --git a/src/libs/xpcom18a4/xpcom/io/nsLocalFileMac.cpp b/src/libs/xpcom18a4/xpcom/io/nsLocalFileMac.cpp
new file mode 100644
index 00000000..bebc76c1
--- /dev/null
+++ b/src/libs/xpcom18a4/xpcom/io/nsLocalFileMac.cpp
@@ -0,0 +1,3554 @@
+/* -*- 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 client code, released
+ * March 31, 1998.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1998-1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Steve Dagley <sdagley@netscape.com>
+ * John R. McMullen
+ *
+ * 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 "nsCOMPtr.h"
+#include "nsMemory.h"
+#include "nsXPIDLString.h"
+
+#include "nsLocalFile.h"
+#include "nsNativeCharsetUtils.h"
+#include "nsISimpleEnumerator.h"
+#include "nsIComponentManager.h"
+#include "nsIInternetConfigService.h"
+#include "nsIMIMEInfo.h"
+#include "prtypes.h"
+#include "prerror.h"
+
+#include "nsReadableUtils.h"
+#include "nsITimelineService.h"
+
+#ifdef XP_MACOSX
+#include "nsXPIDLString.h"
+
+#include "private/pprio.h"
+#else
+#include "pprio.h" // Include this rather than prio.h so we get def of PR_ImportFile
+#endif
+#include "prmem.h"
+#include "plbase64.h"
+
+#include "FullPath.h"
+#include "FileCopy.h"
+#include "MoreFilesExtras.h"
+#include "DirectoryCopy.h"
+#include <Script.h>
+#include <Processes.h>
+#include <StringCompare.h>
+#include <Resources.h>
+
+#include <AppleEvents.h>
+#include <AEDataModel.h>
+#include <AERegistry.h>
+#include <Gestalt.h>
+
+#include <Math64.h>
+#include <Aliases.h>
+#include <Folders.h>
+#include <Gestalt.h>
+#include "macDirectoryCopy.h"
+
+#include <limits.h>
+
+// Stupid @#$% header looks like its got extern mojo but it doesn't really
+extern "C"
+{
+#ifndef XP_MACOSX
+// BADPINK - this MSL header doesn't exist under macosx :-(
+#include <FSp_fopen.h>
+#endif
+}
+
+#if TARGET_CARBON
+#include <CodeFragments.h> // Needed for definition of kUnresolvedCFragSymbolAddress
+#include <LaunchServices.h>
+#endif
+
+#pragma mark [Constants]
+
+const OSType kDefaultCreator = 'MOSS';
+
+#pragma mark -
+#pragma mark [nsPathParser]
+
+class nsPathParser
+{
+public:
+ nsPathParser(const nsACString &path);
+
+ ~nsPathParser()
+ {
+ if (mAllocatedBuffer)
+ nsMemory::Free(mAllocatedBuffer);
+ }
+
+ const char* First()
+ {
+ return nsCRT::strtok(mBuffer, ":", &mNewString);
+ }
+ const char* Next()
+ {
+ return nsCRT::strtok(mNewString, ":", &mNewString);
+ }
+ const char* Remainder()
+ {
+ return mNewString;
+ }
+
+private:
+ char mAutoBuffer[512];
+ char *mAllocatedBuffer;
+ char *mBuffer, *mNewString;
+};
+
+nsPathParser::nsPathParser(const nsACString &inPath) :
+ mAllocatedBuffer(nsnull), mNewString(nsnull)
+{
+ PRUint32 inPathLen = inPath.Length();
+ if (inPathLen >= sizeof(mAutoBuffer)) {
+ mAllocatedBuffer = (char *)nsMemory::Alloc(inPathLen + 1);
+ mBuffer = mAllocatedBuffer;
+ }
+ else
+ mBuffer = mAutoBuffer;
+
+ // copy inPath into mBuffer
+ nsACString::const_iterator start, end;
+ inPath.BeginReading(start);
+ inPath.EndReading(end);
+
+ PRUint32 size, offset = 0;
+ for ( ; start != end; start.advance(size)) {
+ const char* buf = start.get();
+ size = start.size_forward();
+ memcpy(mBuffer + offset, buf, size);
+ offset += size;
+ }
+ mBuffer[offset] = '\0';
+}
+
+#pragma mark -
+#pragma mark [static util funcs]
+
+static inline void ClearFSSpec(FSSpec& aSpec)
+{
+ aSpec.vRefNum = 0;
+ aSpec.parID = 0;
+ aSpec.name[0] = 0;
+}
+
+
+// Simple func to map Mac OS errors into nsresults
+static nsresult MacErrorMapper(OSErr inErr)
+{
+ nsresult outErr;
+
+ switch (inErr)
+ {
+ case noErr:
+ outErr = NS_OK;
+ break;
+
+ case fnfErr:
+ outErr = NS_ERROR_FILE_NOT_FOUND;
+ break;
+
+ case dupFNErr:
+ outErr = NS_ERROR_FILE_ALREADY_EXISTS;
+ break;
+
+ case dskFulErr:
+ outErr = NS_ERROR_FILE_DISK_FULL;
+ break;
+
+ case fLckdErr:
+ outErr = NS_ERROR_FILE_IS_LOCKED;
+ break;
+
+ // Can't find good map for some
+ case bdNamErr:
+ outErr = NS_ERROR_FAILURE;
+ break;
+
+ default:
+ outErr = NS_ERROR_FAILURE;
+ break;
+ }
+ return outErr;
+}
+
+
+
+/*----------------------------------------------------------------------------
+ IsEqualFSSpec
+
+ Compare two canonical FSSpec records.
+
+ Entry: file1 = pointer to first FSSpec record.
+ file2 = pointer to second FSSpec record.
+
+ Exit: function result = true if the FSSpec records are equal.
+----------------------------------------------------------------------------*/
+
+static PRBool IsEqualFSSpec(const FSSpec& file1, const FSSpec& file2)
+{
+ return
+ file1.vRefNum == file2.vRefNum &&
+ file1.parID == file2.parID &&
+ EqualString(file1.name, file2.name, false, true);
+}
+
+
+/*----------------------------------------------------------------------------
+ GetParentFolderSpec
+
+ Given an FSSpec to a (possibly non-existent) file, get an FSSpec for its
+ parent directory.
+
+----------------------------------------------------------------------------*/
+
+static OSErr GetParentFolderSpec(const FSSpec& fileSpec, FSSpec& parentDirSpec)
+{
+ CInfoPBRec pBlock = {0};
+ OSErr err = noErr;
+
+ parentDirSpec.name[0] = 0;
+
+ pBlock.dirInfo.ioVRefNum = fileSpec.vRefNum;
+ pBlock.dirInfo.ioDrDirID = fileSpec.parID;
+ pBlock.dirInfo.ioNamePtr = (StringPtr)parentDirSpec.name;
+ pBlock.dirInfo.ioFDirIndex = -1; //get info on parID
+ err = PBGetCatInfoSync(&pBlock);
+ if (err != noErr) return err;
+
+ parentDirSpec.vRefNum = fileSpec.vRefNum;
+ parentDirSpec.parID = pBlock.dirInfo.ioDrParID;
+
+ return err;
+}
+
+
+/*----------------------------------------------------------------------------
+ VolHasDesktopDB
+
+ Check to see if a volume supports the new desktop database.
+
+ Entry: vRefNum = vol ref num of volumn
+
+ Exit: function result = error code.
+ *hasDesktop = true if volume has the new desktop database.
+----------------------------------------------------------------------------*/
+
+static OSErr VolHasDesktopDB (short vRefNum, Boolean *hasDesktop)
+{
+ HParamBlockRec pb;
+ GetVolParmsInfoBuffer info;
+ OSErr err = noErr;
+
+ pb.ioParam.ioCompletion = nil;
+ pb.ioParam.ioNamePtr = nil;
+ pb.ioParam.ioVRefNum = vRefNum;
+ pb.ioParam.ioBuffer = (Ptr)&info;
+ pb.ioParam.ioReqCount = sizeof(info);
+ err = PBHGetVolParmsSync(&pb);
+ *hasDesktop = err == noErr && (info.vMAttrib & (1L << bHasDesktopMgr)) != 0;
+ return err;
+}
+
+
+/*----------------------------------------------------------------------------
+ GetLastModDateTime
+
+ Get the last mod date and time of a file.
+
+ Entry: fSpec = pointer to file spec.
+
+ Exit: function result = error code.
+ *lastModDateTime = last mod date and time.
+----------------------------------------------------------------------------*/
+
+static OSErr GetLastModDateTime(const FSSpec *fSpec, unsigned long *lastModDateTime)
+{
+ CInfoPBRec pBlock;
+ OSErr err = noErr;
+
+ pBlock.hFileInfo.ioNamePtr = (StringPtr)fSpec->name;
+ pBlock.hFileInfo.ioVRefNum = fSpec->vRefNum;
+ pBlock.hFileInfo.ioFDirIndex = 0;
+ pBlock.hFileInfo.ioDirID = fSpec->parID;
+ err = PBGetCatInfoSync(&pBlock);
+ if (err != noErr) return err;
+ *lastModDateTime = pBlock.hFileInfo.ioFlMdDat;
+ return noErr;
+}
+
+
+/*----------------------------------------------------------------------------
+ FindAppOnVolume
+
+ Find an application on a volume.
+
+ Entry: sig = application signature.
+ vRefNum = vol ref num
+
+ Exit: function result = error code
+ = afpItemNotFound if app not found on vol.
+ *file = file spec for application on volume.
+----------------------------------------------------------------------------*/
+
+static OSErr FindAppOnVolume (OSType sig, short vRefNum, FSSpec *file)
+{
+ DTPBRec pb;
+ OSErr err = noErr;
+ short ioDTRefNum, i;
+ FInfo fInfo;
+ FSSpec candidate;
+ unsigned long lastModDateTime, maxLastModDateTime;
+
+ memset(&pb, 0, sizeof(DTPBRec));
+ pb.ioCompletion = nil;
+ pb.ioVRefNum = vRefNum;
+ pb.ioNamePtr = nil;
+ err = PBDTGetPath(&pb);
+ if (err != noErr) return err;
+ ioDTRefNum = pb.ioDTRefNum;
+
+ memset(&pb, 0, sizeof(DTPBRec));
+ pb.ioCompletion = nil;
+ pb.ioIndex = 0;
+ pb.ioFileCreator = sig;
+ pb.ioNamePtr = file->name;
+ pb.ioDTRefNum = ioDTRefNum;
+ err = PBDTGetAPPLSync(&pb);
+
+ if (err == fnfErr || err == paramErr) return afpItemNotFound;
+ if (err != noErr) return err;
+
+ file->vRefNum = vRefNum;
+ file->parID = pb.ioAPPLParID;
+
+ err = FSpGetFInfo(file, &fInfo);
+ if (err == noErr) return noErr;
+
+ i = 1;
+ maxLastModDateTime = 0;
+ while (true)
+ {
+ memset(&pb, 0, sizeof(DTPBRec));
+ pb.ioCompletion = nil;
+ pb.ioIndex = i;
+ pb.ioFileCreator = sig;
+ pb.ioNamePtr = candidate.name;
+ pb.ioDTRefNum = ioDTRefNum;
+ err = PBDTGetAPPLSync(&pb);
+ if (err != noErr) break;
+ candidate.vRefNum = vRefNum;
+ candidate.parID = pb.ioAPPLParID;
+ err = GetLastModDateTime(file, &lastModDateTime);
+ if (err == noErr) {
+ if (lastModDateTime > maxLastModDateTime) {
+ maxLastModDateTime = lastModDateTime;
+ *file = candidate;
+ }
+ }
+ i++;
+ }
+
+ return maxLastModDateTime > 0 ? noErr : afpItemNotFound;
+}
+
+
+/*----------------------------------------------------------------------------
+ GetIndVolume
+
+ Get a volume reference number by volume index.
+
+ Entry: index = volume index
+
+ Exit: function result = error code.
+ *vRefNum = vol ref num of indexed volume.
+----------------------------------------------------------------------------*/
+
+static OSErr GetIndVolume(short index, short *vRefNum)
+{
+ HParamBlockRec pb;
+ Str63 volumeName;
+ OSErr err = noErr;
+
+ pb.volumeParam.ioCompletion = nil;
+ pb.volumeParam.ioNamePtr = volumeName;
+ pb.volumeParam.ioVolIndex = index;
+
+ err = PBHGetVInfoSync(&pb);
+
+ *vRefNum = pb.volumeParam.ioVRefNum;
+ return err;
+}
+
+
+// Private NSPR functions
+static unsigned long gJanuaryFirst1970Seconds = 0;
+/*
+ * The geographic location and time zone information of a Mac
+ * are stored in extended parameter RAM. The ReadLocation
+ * produdure uses the geographic location record, MachineLocation,
+ * to read the geographic location and time zone information in
+ * extended parameter RAM.
+ *
+ * Because serial port and SLIP conflict with ReadXPram calls,
+ * we cache the call here.
+ *
+ * Caveat: this caching will give the wrong result if a session
+ * extend across the DST changeover time.
+ */
+
+static void MyReadLocation(MachineLocation *loc)
+{
+ static MachineLocation storedLoc;
+ static Boolean didReadLocation = false;
+
+ if (!didReadLocation) {
+ ReadLocation(&storedLoc);
+ didReadLocation = true;
+ }
+ *loc = storedLoc;
+}
+
+static long GMTDelta(void)
+{
+ MachineLocation loc;
+ long gmtDelta;
+
+ MyReadLocation(&loc);
+ gmtDelta = loc.u.gmtDelta & 0x00ffffff;
+ if (gmtDelta & 0x00800000) { /* test sign extend bit */
+ gmtDelta |= 0xff000000;
+ }
+ return gmtDelta;
+}
+
+static void MacintoshInitializeTime(void)
+{
+ /*
+ * The NSPR epoch is midnight, Jan. 1, 1970 GMT.
+ *
+ * At midnight Jan. 1, 1970 GMT, the local time was
+ * midnight Jan. 1, 1970 + GMTDelta().
+ *
+ * Midnight Jan. 1, 1970 is 86400 * (365 * (1970 - 1904) + 17)
+ * = 2082844800 seconds since the Mac epoch.
+ * (There were 17 leap years from 1904 to 1970.)
+ *
+ * So the NSPR epoch is 2082844800 + GMTDelta() seconds since
+ * the Mac epoch. Whew! :-)
+ */
+ gJanuaryFirst1970Seconds = 2082844800 + GMTDelta();
+}
+
+static nsresult ConvertMacTimeToMilliseconds( PRInt64* aLastModifiedTime, PRUint32 timestamp )
+{
+ if ( gJanuaryFirst1970Seconds == 0)
+ MacintoshInitializeTime();
+ timestamp -= gJanuaryFirst1970Seconds;
+ PRTime usecPerSec, dateInMicroSeconds;
+ LL_I2L(dateInMicroSeconds, timestamp);
+ LL_I2L(usecPerSec, PR_MSEC_PER_SEC);
+ LL_MUL(*aLastModifiedTime, usecPerSec, dateInMicroSeconds);
+ return NS_OK;
+}
+
+static nsresult ConvertMillisecondsToMacTime(PRInt64 aTime, PRUint32 *aOutMacTime)
+{
+ NS_ENSURE_ARG( aOutMacTime );
+
+ PRTime usecPerSec, dateInSeconds;
+ dateInSeconds = LL_ZERO;
+
+ LL_I2L(usecPerSec, PR_MSEC_PER_SEC);
+ LL_DIV(dateInSeconds, aTime, usecPerSec); // dateInSeconds = aTime/1,000
+ LL_L2UI(*aOutMacTime, dateInSeconds);
+ *aOutMacTime += 2082844800; // date + Mac epoch
+
+ return NS_OK;
+}
+
+static void myPLstrcpy(Str255 dst, const char* src)
+{
+ int srcLength = strlen(src);
+ NS_ASSERTION(srcLength <= 255, "Oops, Str255 can't hold >255 chars");
+ if (srcLength > 255)
+ srcLength = 255;
+ dst[0] = srcLength;
+ memcpy(&dst[1], src, srcLength);
+}
+
+static void myPLstrncpy(Str255 dst, const char* src, int inMax)
+{
+ int srcLength = strlen(src);
+ if (srcLength > inMax)
+ srcLength = inMax;
+ dst[0] = srcLength;
+ memcpy(&dst[1], src, srcLength);
+}
+
+/*
+ NS_TruncNodeName
+
+ Utility routine to do a mid-trunc on a potential file name so that it is
+ no longer than 31 characters. Until we move to the HFS+ APIs we need this
+ to come up with legal Mac file names.
+
+ Entry: aNode = initial file name
+ outBuf = scratch buffer for the truncated name (MUST be >= 32 characters)
+
+ Exit: function result = pointer to truncated name. Will be either aNode or outBuf.
+
+*/
+const char* NS_TruncNodeName(const char *aNode, char *outBuf)
+{
+ PRUint32 nodeLen;
+ if ((nodeLen = strlen(aNode)) > 31)
+ {
+ static PRBool sInitialized = PR_FALSE;
+ static CharByteTable sTable;
+ // Init to "..." in case we fail to get the ellipsis token
+ static char sEllipsisTokenStr[4] = { '.', '.', '.', 0 };
+ static PRUint8 sEllipsisTokenLen = 3;
+
+ if (!sInitialized)
+ {
+ // Entries in the table are:
+ // 0 == 1 byte char
+ // 1 == 2 byte char
+ FillParseTable(sTable, smSystemScript);
+
+ Handle itl4ResHandle = nsnull;
+ long offset, len;
+ ::GetIntlResourceTable(smSystemScript, smUnTokenTable, &itl4ResHandle, &offset, &len);
+ if (itl4ResHandle)
+ {
+ UntokenTable *untokenTableRec = (UntokenTable *)(*itl4ResHandle + offset);
+ if (untokenTableRec->lastToken >= tokenEllipsis)
+ {
+ offset += untokenTableRec->index[tokenEllipsis];
+ char *tokenStr = (*itl4ResHandle + offset);
+ sEllipsisTokenLen = tokenStr[0];
+ memcpy(sEllipsisTokenStr, &tokenStr[1], sEllipsisTokenLen);
+ }
+ ::ReleaseResource(itl4ResHandle);
+ }
+ sInitialized = PR_TRUE;
+ }
+
+ PRInt32 halfLen = (31 - sEllipsisTokenLen) / 2;
+ PRInt32 charSize = 0, srcPos, destPos;
+ for (srcPos = 0; srcPos + charSize <= halfLen; srcPos += charSize)
+ charSize = sTable[aNode[srcPos]] ? 2 : 1;
+
+ memcpy(outBuf, aNode, srcPos);
+ memcpy(outBuf + srcPos, sEllipsisTokenStr, sEllipsisTokenLen);
+ destPos = srcPos + sEllipsisTokenLen;
+
+ for (; srcPos < nodeLen - halfLen; srcPos += charSize)
+ charSize = sTable[aNode[srcPos]] ? 2 : 1;
+
+ memcpy(outBuf + destPos, aNode + srcPos, nodeLen - srcPos);
+ destPos += (nodeLen - srcPos);
+ outBuf[destPos] = '\0';
+ return outBuf;
+ }
+ return aNode;
+}
+
+/**
+ * HFSPlusGetRawPath returns the path for an FSSpec as a unicode string.
+ *
+ * The reason for this routine instead of just calling FSRefMakePath is
+ * (1) inSpec does not have to exist
+ * (2) FSRefMakePath uses '/' as the separator under OSX and ':' under OS9
+ */
+static OSErr HFSPlusGetRawPath(const FSSpec& inSpec, nsAString& outStr)
+{
+ OSErr err;
+ nsAutoString ucPathString;
+
+ outStr.Truncate(0);
+
+ FSRef nodeRef;
+ FSCatalogInfo catalogInfo;
+ catalogInfo.parentDirID = 0;
+ err = ::FSpMakeFSRef(&inSpec, &nodeRef);
+
+ if (err == fnfErr) {
+ FSSpec parentDirSpec;
+ err = GetParentFolderSpec(inSpec, parentDirSpec);
+ if (err == noErr) {
+ const char *startPtr = (const char*)&inSpec.name[1];
+ NS_CopyNativeToUnicode(Substring(startPtr, startPtr + PRUint32(inSpec.name[0])), outStr);
+ err = ::FSpMakeFSRef(&parentDirSpec, &nodeRef);
+ }
+ }
+
+ while (err == noErr && catalogInfo.parentDirID != fsRtParID) {
+ HFSUniStr255 nodeName;
+ FSRef parentRef;
+ err = ::FSGetCatalogInfo(&nodeRef,
+ kFSCatInfoNodeFlags + kFSCatInfoParentDirID,
+ &catalogInfo,
+ &nodeName,
+ nsnull,
+ &parentRef);
+ if (err == noErr)
+ {
+ if (catalogInfo.nodeFlags & kFSNodeIsDirectoryMask)
+ nodeName.unicode[nodeName.length++] = PRUnichar(':');
+ const PRUnichar* nodeNameUni = (const PRUnichar*) nodeName.unicode;
+ outStr.Insert(Substring(nodeNameUni, nodeNameUni + nodeName.length), 0);
+ nodeRef = parentRef;
+ }
+ }
+ return err;
+}
+
+
+// The R**co FSSpec resolver -
+// it slices, it dices, it juliannes fries and it even creates FSSpecs out of whatever you feed it
+// This function will take a path and a starting FSSpec and generate a FSSpec to represent
+// the target of the two. If the intial FSSpec is null the path alone will be resolved
+static OSErr ResolvePathAndSpec(const char * filePath, FSSpec *inSpec, PRBool createDirs, FSSpec *outSpec)
+{
+ OSErr err = noErr;
+ size_t inLength = strlen(filePath);
+ Boolean isRelative = (filePath && inSpec);
+ FSSpec tempSpec;
+ Str255 ppath;
+ Boolean isDirectory;
+
+ if (isRelative && inSpec)
+ {
+ outSpec->vRefNum = inSpec->vRefNum;
+ outSpec->parID = inSpec->parID;
+
+ if (inSpec->name[0] != 0)
+ {
+ long theDirID;
+
+ err = FSpGetDirectoryID(inSpec, &theDirID, &isDirectory);
+
+ if (err == noErr && isDirectory)
+ outSpec->parID = theDirID;
+ else if (err == fnfErr && createDirs)
+ {
+ err = FSpDirCreate(inSpec, smCurrentScript, &theDirID);
+ if (err == noErr)
+ outSpec->parID = theDirID;
+ else if (err == fnfErr)
+ err = dirNFErr;
+ }
+ }
+ }
+ else
+ {
+ outSpec->vRefNum = 0;
+ outSpec->parID = 0;
+ }
+
+ if (err)
+ return err;
+
+ // Try making an FSSpec from the path
+ if (inLength < 255)
+ {
+ // Use tempSpec as dest because if FSMakeFSSpec returns dirNFErr, it
+ // will reset the dest spec and we'll lose what we determined above.
+
+ myPLstrcpy(ppath, filePath);
+ err = ::FSMakeFSSpec(outSpec->vRefNum, outSpec->parID, ppath, &tempSpec);
+ if (err == noErr || err == fnfErr)
+ *outSpec = tempSpec;
+ }
+ else if (!isRelative)
+ {
+ err = ::FSpLocationFromFullPath(inLength, filePath, outSpec);
+ }
+ else
+ { // If the path is relative and >255 characters we need to manually walk the
+ // path appending each node to the initial FSSpec so to reach that code we
+ // set the err to bdNamErr and fall into the code below
+ err = bdNamErr;
+ }
+
+ // If we successfully created a spec then leave
+ if (err == noErr)
+ return err;
+
+ // We get here when the directory hierarchy needs to be created or we're resolving
+ // a relative path >255 characters long
+ if (err == dirNFErr || err == bdNamErr)
+ {
+ const char* path = filePath;
+
+ if (!isRelative) // If path is relative, we still need vRefNum & parID.
+ {
+ outSpec->vRefNum = 0;
+ outSpec->parID = 0;
+ }
+
+ do
+ {
+ // Locate the colon that terminates the node.
+ // But if we've a partial path (starting with a colon), find the second one.
+ const char* nextColon = strchr(path + (*path == ':'), ':');
+ // Well, if there are no more colons, point to the end of the string.
+ if (!nextColon)
+ nextColon = path + strlen(path);
+
+ // Make a pascal string out of this node. Include initial
+ // and final colon, if any!
+ myPLstrncpy(ppath, path, nextColon - path + 1);
+
+ // Use this string as a relative path using the directory created
+ // on the previous round (or directory 0,0 on the first round).
+ err = ::FSMakeFSSpec(outSpec->vRefNum, outSpec->parID, ppath, outSpec);
+
+ // If this was the leaf node, then we are done.
+ if (!*nextColon)
+ break;
+
+ // Since there's more to go, we have to get the directory ID, which becomes
+ // the parID for the next round.
+ if (err == noErr)
+ {
+ // The directory (or perhaps a file) exists. Find its dirID.
+ long dirID;
+ err = ::FSpGetDirectoryID(outSpec, &dirID, &isDirectory);
+ if (!isDirectory)
+ err = dupFNErr; // oops! a file exists with that name.
+ if (err != noErr)
+ break; // bail if we've got an error
+ outSpec->parID = dirID;
+ }
+ else if ((err == fnfErr) && createDirs)
+ {
+ // If we got "file not found" and we're allowed to create directories
+ // then we need to create one
+ err = ::FSpDirCreate(outSpec, smCurrentScript, &outSpec->parID);
+ // For some reason, this usually returns fnfErr, even though it works.
+ if (err == fnfErr)
+ err = noErr;
+ }
+ if (err != noErr)
+ break;
+ path = nextColon; // next round
+ } while (true);
+ }
+
+ return err;
+}
+
+
+#pragma mark -
+#pragma mark [StFollowLinksState]
+class StFollowLinksState
+{
+ public:
+ StFollowLinksState(nsILocalFile *aFile) :
+ mFile(aFile)
+ {
+ NS_ASSERTION(mFile, "StFollowLinksState passed a NULL file.");
+ if (mFile)
+ mFile->GetFollowLinks(&mSavedState);
+ }
+
+ StFollowLinksState(nsILocalFile *aFile, PRBool followLinksState) :
+ mFile(aFile)
+ {
+ NS_ASSERTION(mFile, "StFollowLinksState passed a NULL file.");
+ if (mFile) {
+ mFile->GetFollowLinks(&mSavedState);
+ mFile->SetFollowLinks(followLinksState);
+ }
+ }
+
+ ~StFollowLinksState()
+ {
+ if (mFile)
+ mFile->SetFollowLinks(mSavedState);
+ }
+
+ private:
+ nsCOMPtr<nsILocalFile> mFile;
+ PRBool mSavedState;
+};
+
+#pragma mark -
+#pragma mark [nsDirEnumerator]
+class nsDirEnumerator : public nsISimpleEnumerator
+{
+ public:
+
+ NS_DECL_ISUPPORTS
+
+ nsDirEnumerator()
+ {
+ }
+
+ nsresult Init(nsILocalFileMac* parent)
+ {
+ NS_ENSURE_ARG(parent);
+ nsresult rv;
+ FSSpec fileSpec;
+
+ rv = parent->GetFSSpec(&fileSpec);
+ if (NS_FAILED(rv))
+ return rv;
+
+ OSErr err;
+ Boolean isDirectory;
+
+ err = ::FSpGetDirectoryID(&fileSpec, &mDirID, &isDirectory);
+ if (err || !isDirectory)
+ return NS_ERROR_FILE_NOT_DIRECTORY;
+
+ mCatInfo.hFileInfo.ioNamePtr = mItemName;
+ mCatInfo.hFileInfo.ioVRefNum = fileSpec.vRefNum;
+ mItemIndex = 1;
+
+ return NS_OK;
+ }
+
+ NS_IMETHOD HasMoreElements(PRBool *result)
+ {
+ nsresult rv = NS_OK;
+ if (mNext == nsnull)
+ {
+ mItemName[0] = 0;
+ mCatInfo.dirInfo.ioFDirIndex = mItemIndex;
+ mCatInfo.dirInfo.ioDrDirID = mDirID;
+
+ OSErr err = ::PBGetCatInfoSync(&mCatInfo);
+ if (err == fnfErr)
+ {
+ // end of dir entries
+ *result = PR_FALSE;
+ return NS_OK;
+ }
+
+ // Make a new nsILocalFile for the new element
+ FSSpec tempSpec;
+ tempSpec.vRefNum = mCatInfo.hFileInfo.ioVRefNum;
+ tempSpec.parID = mDirID;
+ ::BlockMoveData(mItemName, tempSpec.name, mItemName[0] + 1);
+
+ rv = NS_NewLocalFileWithFSSpec(&tempSpec, PR_TRUE, getter_AddRefs(mNext));
+ if (NS_FAILED(rv))
+ return rv;
+ }
+ *result = mNext != nsnull;
+ return NS_OK;
+ }
+
+ NS_IMETHOD GetNext(nsISupports **result)
+ {
+ NS_ENSURE_ARG_POINTER(result);
+ *result = nsnull;
+
+ nsresult rv;
+ PRBool hasMore;
+ rv = HasMoreElements(&hasMore);
+ if (NS_FAILED(rv)) return rv;
+
+ *result = mNext; // might return nsnull
+ NS_IF_ADDREF(*result);
+
+ mNext = nsnull;
+ ++mItemIndex;
+ return NS_OK;
+ }
+
+ private:
+ ~nsDirEnumerator() {}
+
+ protected:
+ nsCOMPtr<nsILocalFileMac> mNext;
+
+ CInfoPBRec mCatInfo;
+ short mItemIndex;
+ long mDirID;
+ Str63 mItemName;
+};
+
+NS_IMPL_ISUPPORTS1(nsDirEnumerator, nsISimpleEnumerator)
+
+#pragma mark -
+
+OSType nsLocalFile::sCurrentProcessSignature = 0;
+PRBool nsLocalFile::sHasHFSPlusAPIs = PR_FALSE;
+PRBool nsLocalFile::sRunningOSX = PR_FALSE;
+
+#pragma mark [CTOR/DTOR]
+nsLocalFile::nsLocalFile() :
+ mFollowLinks(PR_TRUE),
+ mFollowLinksDirty(PR_TRUE),
+ mSpecDirty(PR_TRUE),
+ mCatInfoDirty(PR_TRUE),
+ mType('TEXT'),
+ mCreator(kDefaultCreator)
+{
+ ClearFSSpec(mSpec);
+ ClearFSSpec(mTargetSpec);
+
+ InitClassStatics();
+ if (sCurrentProcessSignature != 0)
+ mCreator = sCurrentProcessSignature;
+}
+
+nsLocalFile::nsLocalFile(const nsLocalFile& srcFile)
+{
+ *this = srcFile;
+}
+
+nsLocalFile::nsLocalFile(const FSSpec& aSpec, const nsACString& aAppendedPath) :
+ mFollowLinks(PR_TRUE),
+ mFollowLinksDirty(PR_TRUE),
+ mSpecDirty(PR_TRUE),
+ mSpec(aSpec),
+ mAppendedPath(aAppendedPath),
+ mCatInfoDirty(PR_TRUE),
+ mType('TEXT'),
+ mCreator(kDefaultCreator)
+{
+ ClearFSSpec(mTargetSpec);
+
+ InitClassStatics();
+ if (sCurrentProcessSignature != 0)
+ mCreator = sCurrentProcessSignature;
+}
+
+nsLocalFile& nsLocalFile::operator=(const nsLocalFile& rhs)
+{
+ mFollowLinks = rhs.mFollowLinks;
+ mFollowLinksDirty = rhs.mFollowLinksDirty;
+ mSpecDirty = rhs.mSpecDirty;
+ mSpec = rhs.mSpec;
+ mAppendedPath = rhs.mAppendedPath;
+ mTargetSpec = rhs.mTargetSpec;
+ mCatInfoDirty = rhs.mCatInfoDirty;
+ mType = rhs.mType;
+ mCreator = rhs.mCreator;
+
+ if (!rhs.mCatInfoDirty)
+ mCachedCatInfo = rhs.mCachedCatInfo;
+
+ return *this;
+}
+
+#pragma mark -
+#pragma mark [nsISupports interface implementation]
+
+NS_IMPL_THREADSAFE_ISUPPORTS3(nsLocalFile,
+ nsILocalFileMac,
+ nsILocalFile,
+ nsIFile)
+
+NS_METHOD
+nsLocalFile::nsLocalFileConstructor(nsISupports* outer, const nsIID& aIID, void* *aInstancePtr)
+{
+ NS_ENSURE_ARG_POINTER(aInstancePtr);
+ NS_ENSURE_NO_AGGREGATION(outer);
+
+ nsLocalFile* inst = new nsLocalFile();
+ if (inst == NULL)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ nsresult rv = inst->QueryInterface(aIID, aInstancePtr);
+ if (NS_FAILED(rv))
+ {
+ delete inst;
+ return rv;
+ }
+ return NS_OK;
+}
+
+// This function resets any cached information about the file.
+void
+nsLocalFile::MakeDirty()
+{
+ mSpecDirty = PR_TRUE;
+ mFollowLinksDirty = PR_TRUE;
+ mCatInfoDirty = PR_TRUE;
+
+ ClearFSSpec(mTargetSpec);
+}
+
+
+/* attribute PRBool followLinks; */
+NS_IMETHODIMP
+nsLocalFile::GetFollowLinks(PRBool *aFollowLinks)
+{
+ NS_ENSURE_ARG_POINTER(aFollowLinks);
+ *aFollowLinks = mFollowLinks;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::SetFollowLinks(PRBool aFollowLinks)
+{
+ if (aFollowLinks != mFollowLinks)
+ {
+ mFollowLinks = aFollowLinks;
+ mFollowLinksDirty = PR_TRUE;
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::ResolveAndStat()
+{
+ OSErr err = noErr;
+ FSSpec resolvedSpec;
+
+ // fnfErr means target spec is valid but doesn't exist.
+ // If the end result is fnfErr, we're cleanly resolved.
+
+ if (mSpecDirty)
+ {
+ if (mAppendedPath.Length())
+ {
+ err = ResolvePathAndSpec(mAppendedPath.get(), &mSpec, PR_FALSE, &resolvedSpec);
+ if (err == noErr)
+ mAppendedPath.Truncate(0);
+ }
+ else
+ err = ::FSMakeFSSpec(mSpec.vRefNum, mSpec.parID, mSpec.name, &resolvedSpec);
+ if (err == noErr)
+ {
+ mSpec = resolvedSpec;
+ mSpecDirty = PR_FALSE;
+ }
+ mFollowLinksDirty = PR_TRUE;
+ }
+ if (mFollowLinksDirty && (err == noErr))
+ {
+ if (mFollowLinks)
+ {
+ // Resolve the alias to the original file.
+ resolvedSpec = mSpec;
+ Boolean resolvedWasFolder, resolvedWasAlias;
+ err = ::ResolveAliasFile(&resolvedSpec, TRUE, &resolvedWasFolder, &resolvedWasAlias);
+ if (err == noErr || err == fnfErr) {
+ err = noErr;
+ mTargetSpec = resolvedSpec;
+ mFollowLinksDirty = PR_FALSE;
+ }
+ }
+ else
+ {
+ mTargetSpec = mSpec;
+ mFollowLinksDirty = PR_FALSE;
+ }
+ mCatInfoDirty = PR_TRUE;
+ }
+
+ return (MacErrorMapper(err));
+}
+
+
+NS_IMETHODIMP
+nsLocalFile::Clone(nsIFile **file)
+{
+ // Just copy-construct ourselves
+ *file = new nsLocalFile(*this);
+ if (!*file)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ NS_ADDREF(*file);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::InitWithNativePath(const nsACString &filePath)
+{
+ // The incoming path must be a FULL path
+
+ if (filePath.IsEmpty())
+ return NS_ERROR_INVALID_ARG;
+
+ MakeDirty();
+
+ // If it starts with a colon, it's invalid
+ if (filePath.First() == ':')
+ return NS_ERROR_FILE_UNRECOGNIZED_PATH;
+
+ nsPathParser parser(filePath);
+ OSErr err;
+ Str255 pascalNode;
+ FSSpec nodeSpec;
+
+ const char *root = parser.First();
+ if (root == nsnull)
+ return NS_ERROR_FILE_UNRECOGNIZED_PATH;
+
+ // The first component must be an existing volume
+ myPLstrcpy(pascalNode, root);
+ pascalNode[++pascalNode[0]] = ':';
+ err = ::FSMakeFSSpec(0, 0, pascalNode, &nodeSpec);
+ if (err)
+ return NS_ERROR_FILE_UNRECOGNIZED_PATH;
+
+ // Build as much of a spec as possible from the rest of the path
+ // What doesn't exist will be left over in mAppendedPath
+ const char *nextNode;
+ while ((nextNode = parser.Next()) != nsnull) {
+ long dirID;
+ Boolean isDir;
+ err = ::FSpGetDirectoryID(&nodeSpec, &dirID, &isDir);
+ if (err || !isDir)
+ break;
+ myPLstrcpy(pascalNode, nextNode);
+ err = ::FSMakeFSSpec(nodeSpec.vRefNum, dirID, pascalNode, &nodeSpec);
+ if (err == fnfErr)
+ break;
+ }
+ mSpec = nodeSpec;
+ mAppendedPath = parser.Remainder();
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::InitWithPath(const nsAString &filePath)
+{
+ nsresult rv;
+ nsCAutoString fsStr;
+
+ if (NS_SUCCEEDED(rv = NS_CopyUnicodeToNative(filePath, fsStr)))
+ rv = InitWithNativePath(fsStr);
+
+ return rv;
+}
+
+NS_IMETHODIMP
+nsLocalFile::InitWithFile(nsILocalFile *aFile)
+{
+ NS_ENSURE_ARG(aFile);
+ nsLocalFile *asLocalFile = dynamic_cast<nsLocalFile*>(aFile);
+ if (!asLocalFile)
+ return NS_ERROR_NO_INTERFACE; // Well, sort of.
+ *this = *asLocalFile;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::OpenNSPRFileDesc(PRInt32 flags, PRInt32 mode, PRFileDesc **_retval)
+{
+// Macintosh doesn't really have mode bits, just drop them
+#pragma unused (mode)
+
+ NS_ENSURE_ARG(_retval);
+
+ nsresult rv = NS_OK;
+ FSSpec spec;
+ OSErr err = noErr;
+
+ rv = ResolveAndStat();
+ if (rv == NS_ERROR_FILE_NOT_FOUND && (flags & PR_CREATE_FILE))
+ rv = NS_OK;
+
+ if (flags & PR_CREATE_FILE) {
+ rv = Create(nsIFile::NORMAL_FILE_TYPE, 0);
+ /* If opening with the PR_EXCL flag the existence of the file prior to opening is an error */
+ if ((flags & PR_EXCL) && (rv == NS_ERROR_FILE_ALREADY_EXISTS))
+ return rv;
+ }
+
+ rv = GetFSSpec(&spec);
+ if (NS_FAILED(rv))
+ return rv;
+
+ SInt8 perm;
+ if (flags & PR_RDWR)
+ perm = fsRdWrPerm;
+ else if (flags & PR_WRONLY)
+ perm = fsWrPerm;
+ else
+ perm = fsRdPerm;
+
+ short refnum;
+ err = ::FSpOpenDF(&spec, perm, &refnum);
+
+ if (err == noErr && (flags & PR_TRUNCATE))
+ err = ::SetEOF(refnum, 0);
+ if (err == noErr && (flags & PR_APPEND))
+ err = ::SetFPos(refnum, fsFromLEOF, 0);
+ if (err != noErr)
+ return MacErrorMapper(err);
+
+ if ((*_retval = PR_ImportFile(refnum)) == 0)
+ return NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_FILES,(PR_GetError() & 0xFFFF));
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::OpenANSIFileDesc(const char *mode, FILE * *_retval)
+{
+ NS_ENSURE_ARG(mode);
+ NS_ENSURE_ARG_POINTER(_retval);
+
+ nsresult rv;
+ FSSpec spec;
+
+ rv = ResolveAndStat();
+ if (NS_FAILED(rv) && rv != NS_ERROR_FILE_NOT_FOUND)
+ return rv;
+
+ if (mode[0] == 'w' || mode[0] == 'a') // Create if it doesn't exist
+ {
+ if (rv == NS_ERROR_FILE_NOT_FOUND) {
+ mType = (mode[1] == 'b') ? 'BiNA' : 'TEXT';
+ rv = Create(nsIFile::NORMAL_FILE_TYPE, 0);
+ if (NS_FAILED(rv))
+ return rv;
+ }
+ }
+
+ rv = GetFSSpec(&spec);
+ if (NS_FAILED(rv))
+ return rv;
+
+#ifdef MACOSX
+ // FSp_fopen() doesn't exist under macosx :-(
+ nsXPIDLCString ourPath;
+ rv = GetPath(getter_Copies(ourPath));
+ if (NS_FAILED(rv))
+ return rv;
+ *_retval = fopen(ourPath, mode);
+#else
+ *_retval = FSp_fopen(&spec, mode);
+#endif
+
+ if (*_retval)
+ return NS_OK;
+
+ return NS_ERROR_FAILURE;
+}
+
+
+NS_IMETHODIMP
+nsLocalFile::Create(PRUint32 type, PRUint32 attributes)
+{
+ OSErr err;
+
+ if (type != NORMAL_FILE_TYPE && type != DIRECTORY_TYPE)
+ return NS_ERROR_FILE_UNKNOWN_TYPE;
+
+ FSSpec newSpec;
+
+ if (mAppendedPath.Length())
+ { // We've got an FSSpec and an appended path so pass 'em both to ResolvePathAndSpec
+ err = ResolvePathAndSpec(mAppendedPath.get(), &mSpec, PR_TRUE, &newSpec);
+ }
+ else
+ {
+ err = ::FSMakeFSSpec(mSpec.vRefNum, mSpec.parID, mSpec.name, &newSpec);
+ }
+
+ if (err != noErr && err != fnfErr)
+ return (MacErrorMapper(err));
+
+ switch (type)
+ {
+ case NORMAL_FILE_TYPE:
+ SetOSTypeAndCreatorFromExtension();
+ err = ::FSpCreate(&newSpec, mCreator, mType, smCurrentScript);
+ break;
+
+ case DIRECTORY_TYPE:
+ {
+ long newDirID;
+ err = ::FSpDirCreate(&newSpec, smCurrentScript, &newDirID);
+ // For some reason, this usually returns fnfErr, even though it works.
+ if (err == fnfErr)
+ err = noErr;
+ }
+ break;
+
+ default:
+ return NS_ERROR_FILE_UNKNOWN_TYPE;
+ break;
+ }
+
+ if (err == noErr)
+ {
+ mSpec = mTargetSpec = newSpec;
+ mAppendedPath.Truncate(0);
+ }
+
+ return (MacErrorMapper(err));
+}
+
+NS_IMETHODIMP
+nsLocalFile::AppendNative(const nsACString &aNode)
+{
+ if (aNode.IsEmpty())
+ return NS_OK;
+
+ nsACString::const_iterator start, end;
+ aNode.BeginReading(start);
+ aNode.EndReading(end);
+ if (FindCharInReadable(':', start, end))
+ return NS_ERROR_FILE_UNRECOGNIZED_PATH;
+
+ MakeDirty();
+
+ char truncBuffer[32];
+ const char *node = NS_TruncNodeName(PromiseFlatCString(aNode).get(), truncBuffer);
+
+ if (!mAppendedPath.Length())
+ {
+ OSErr err;
+ Boolean resolvedWasFolder, resolvedWasAlias;
+ err = ::ResolveAliasFile(&mSpec, TRUE, &resolvedWasFolder, &resolvedWasAlias);
+ if (err == noErr)
+ {
+ long dirID;
+ Boolean isDir;
+
+ if (!resolvedWasFolder)
+ return NS_ERROR_FILE_NOT_DIRECTORY;
+ if ((err = ::FSpGetDirectoryID(&mSpec, &dirID, &isDir)) != noErr)
+ return MacErrorMapper(err);
+
+ FSSpec childSpec;
+ Str255 pascalNode;
+ myPLstrcpy(pascalNode, node);
+ err = ::FSMakeFSSpec(mSpec.vRefNum, dirID, pascalNode, &childSpec);
+ if (err && err != fnfErr)
+ return MacErrorMapper(err);
+ mSpec = childSpec;
+ }
+ else if (err == fnfErr)
+ mAppendedPath.Assign(node);
+ else
+ return MacErrorMapper(err);
+ }
+ else
+ {
+ if (mAppendedPath.First() != ':')
+ mAppendedPath.Insert(':', 0);
+ mAppendedPath.Append(":");
+ mAppendedPath.Append(node);
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::Append(const nsAString &node)
+{
+ nsresult rv;
+ nsCAutoString fsStr;
+
+ if (NS_SUCCEEDED(rv = NS_CopyUnicodeToNative(node, fsStr)))
+ rv = AppendNative(fsStr);
+
+ return rv;
+}
+
+NS_IMETHODIMP
+nsLocalFile::AppendRelativeNativePath(const nsACString &relPath)
+{
+ if (relPath.IsEmpty())
+ return NS_ERROR_INVALID_ARG;
+
+ nsresult rv;
+ nsPathParser parser(relPath);
+ const char* node = parser.First();
+
+ while (node)
+ {
+ if (NS_FAILED(rv = AppendNative(nsDependentCString(node))))
+ return rv;
+ node = parser.Next();
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::AppendRelativePath(const nsAString &relPath)
+{
+ nsresult rv;
+ nsCAutoString fsStr;
+
+ if (NS_SUCCEEDED(rv = NS_CopyUnicodeToNative(relPath, fsStr)))
+ rv = AppendRelativeNativePath(fsStr);
+
+ return rv;
+}
+
+NS_IMETHODIMP
+nsLocalFile::GetNativeLeafName(nsACString &aLeafName)
+{
+ aLeafName.Truncate();
+
+ // See if we've had a path appended
+ if (mAppendedPath.Length())
+ {
+ const char* temp = mAppendedPath.get();
+ if (temp == nsnull)
+ return NS_ERROR_FILE_UNRECOGNIZED_PATH;
+
+ const char* leaf = strrchr(temp, ':');
+
+ // if the working path is just a node without any directory delimeters.
+ if (leaf == nsnull)
+ leaf = temp;
+ else
+ leaf++;
+
+ aLeafName = leaf;
+ }
+ else
+ {
+ // We don't have an appended path so grab the leaf name from the FSSpec
+ // Convert the Pascal string to a C string
+ PRInt32 len = mSpec.name[0];
+ char* leafName = (char *)malloc(len + 1);
+ if (!leafName) return NS_ERROR_OUT_OF_MEMORY;
+ ::BlockMoveData(&mSpec.name[1], leafName, len);
+ leafName[len] = '\0';
+ aLeafName = leafName;
+ free(leafName);
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::GetLeafName(nsAString &aLeafName)
+{
+ nsresult rv;
+ nsCAutoString fsStr;
+
+ if (NS_SUCCEEDED(rv = GetNativeLeafName(fsStr)))
+ rv = NS_CopyNativeToUnicode(fsStr, aLeafName);
+ return rv;
+}
+
+NS_IMETHODIMP
+nsLocalFile::SetNativeLeafName(const nsACString &aLeafName)
+{
+ if (aLeafName.IsEmpty())
+ return NS_ERROR_INVALID_ARG;
+
+ MakeDirty();
+
+ char truncBuffer[32];
+ const char *leafName = NS_TruncNodeName(PromiseFlatCString(aLeafName).get(), truncBuffer);
+
+ if (mAppendedPath.Length())
+ { // Lop off the end of the appended path and replace it with the new leaf name
+ PRInt32 offset = mAppendedPath.RFindChar(':');
+ if (offset || ((!offset) && (1 < mAppendedPath.Length())))
+ {
+ mAppendedPath.Truncate(offset + 1);
+ }
+ mAppendedPath.Append(leafName);
+ }
+ else
+ {
+ // We don't have an appended path so directly modify the FSSpec
+ myPLstrcpy(mSpec.name, leafName);
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::SetLeafName(const nsAString &aLeafName)
+{
+ nsresult rv;
+ nsCAutoString fsStr;
+
+ if (NS_SUCCEEDED(rv = NS_CopyUnicodeToNative(aLeafName, fsStr)))
+ rv = SetNativeLeafName(fsStr);
+
+ return rv;
+}
+
+NS_IMETHODIMP
+nsLocalFile::GetNativePath(nsACString &_retval)
+{
+ _retval.Truncate();
+
+ nsCAutoString fsCharSetPathStr;
+
+#if TARGET_CARBON
+ if (sHasHFSPlusAPIs) // should always be true under Carbon, but in case...
+ {
+ OSErr err;
+ nsresult rv;
+ nsAutoString ucPathString;
+
+ if ((err = HFSPlusGetRawPath(mSpec, ucPathString)) != noErr)
+ return MacErrorMapper(err);
+ rv = NS_CopyUnicodeToNative(ucPathString, fsCharSetPathStr);
+ if (NS_FAILED(rv))
+ return rv;
+ }
+ else
+#endif
+ {
+ // Now would be a good time to call the code that makes an FSSpec into a path
+ short fullPathLen;
+ Handle fullPathHandle;
+ (void)::FSpGetFullPath(&mSpec, &fullPathLen, &fullPathHandle);
+ if (!fullPathHandle)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ ::HLock(fullPathHandle);
+ fsCharSetPathStr.Assign(*fullPathHandle, fullPathLen);
+ ::DisposeHandle(fullPathHandle);
+ }
+
+ // We need to make sure that even if we have a path to a
+ // directory we don't return the trailing colon. It breaks
+ // the component manager. (Bugzilla bug #26102)
+ if (fsCharSetPathStr.Last() == ':')
+ fsCharSetPathStr.Truncate(fsCharSetPathStr.Length() - 1);
+
+ // Now, tack on mAppendedPath. It never ends in a colon.
+ if (mAppendedPath.Length())
+ {
+ if (mAppendedPath.First() != ':')
+ fsCharSetPathStr.Append(":");
+ fsCharSetPathStr.Append(mAppendedPath);
+ }
+
+ _retval = fsCharSetPathStr;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::GetPath(nsAString &_retval)
+{
+ nsresult rv = NS_OK;
+
+#if TARGET_CARBON
+ if (sHasHFSPlusAPIs) // should always be true under Carbon, but in case...
+ {
+ OSErr err;
+ nsAutoString ucPathString;
+
+ if ((err = HFSPlusGetRawPath(mSpec, ucPathString)) != noErr)
+ return MacErrorMapper(err);
+
+ // We need to make sure that even if we have a path to a
+ // directory we don't return the trailing colon. It breaks
+ // the component manager. (Bugzilla bug #26102)
+ if (ucPathString.Last() == PRUnichar(':'))
+ ucPathString.Truncate(ucPathString.Length() - 1);
+
+ // Now, tack on mAppendedPath. It never ends in a colon.
+ if (mAppendedPath.Length())
+ {
+ nsAutoString ucAppendage;
+ if (mAppendedPath.First() != ':')
+ ucPathString.Append(PRUnichar(':'));
+ rv = NS_CopyNativeToUnicode(mAppendedPath, ucAppendage);
+ if (NS_FAILED(rv))
+ return rv;
+ ucPathString.Append(ucAppendage);
+ }
+
+ _retval = ucPathString;
+ }
+ else
+#endif
+ {
+ nsCAutoString fsStr;
+
+ if (NS_SUCCEEDED(rv = GetNativePath(fsStr))) {
+ rv = NS_CopyNativeToUnicode(fsStr, _retval);
+ }
+ }
+ return rv;
+}
+
+nsresult nsLocalFile::MoveCopy( nsIFile* newParentDir, const nsACString &newName, PRBool isCopy, PRBool followLinks )
+{
+ OSErr macErr;
+ FSSpec srcSpec;
+ Str255 newPascalName;
+ nsresult rv;
+
+ StFollowLinksState srcFollowState(this, followLinks);
+ rv = GetFSSpec(&srcSpec);
+ if ( NS_FAILED( rv ) )
+ return rv;
+
+ // If newParentDir == nsnull, it's a simple rename
+ if ( !newParentDir )
+ {
+ myPLstrncpy( newPascalName, PromiseFlatCString(newName).get(), 255 );
+ macErr = ::FSpRename( &srcSpec, newPascalName );
+ return MacErrorMapper( macErr );
+ }
+
+ nsCOMPtr<nsILocalFileMac> destDir(do_QueryInterface( newParentDir ));
+ StFollowLinksState destFollowState(destDir, followLinks);
+ FSSpec destSpec;
+ rv = destDir->GetFSSpec(&destSpec);
+ if ( NS_FAILED( rv ) )
+ return rv;
+
+ long dirID;
+ Boolean isDirectory;
+ macErr = ::FSpGetDirectoryID(&destSpec, &dirID, &isDirectory);
+ if ( macErr || !isDirectory )
+ return NS_ERROR_FILE_DESTINATION_NOT_DIR;
+
+ if ( !newName.IsEmpty() )
+ myPLstrncpy( newPascalName, PromiseFlatCString(newName).get(), 255);
+ else
+ memcpy(newPascalName, srcSpec.name, srcSpec.name[0] + 1);
+ if ( isCopy )
+ {
+ macErr = ::FSpGetDirectoryID(&srcSpec, &dirID, &isDirectory);
+ if (macErr == noErr)
+ {
+ const PRInt32 kCopyBufferSize = (1024 * 512); // allocate our own buffer to speed file copies. Bug #103202
+ OSErr tempErr;
+ Handle copyBufferHand = ::TempNewHandle(kCopyBufferSize, &tempErr);
+ void* copyBuffer = nsnull;
+ PRInt32 copyBufferSize = 0;
+
+ // it's OK if the allocated failed; FSpFileCopy will just fall back on its own internal 16k buffer
+ if (copyBufferHand)
+ {
+ ::HLock(copyBufferHand);
+ copyBuffer = *copyBufferHand;
+ copyBufferSize = kCopyBufferSize;
+ }
+
+ if ( isDirectory )
+ macErr = MacFSpDirectoryCopyRename( &srcSpec, &destSpec, newPascalName, copyBuffer, copyBufferSize, true, NULL );
+ else
+ macErr = ::FSpFileCopy( &srcSpec, &destSpec, newPascalName, copyBuffer, copyBufferSize, true );
+
+ if (copyBufferHand)
+ ::DisposeHandle(copyBufferHand);
+ }
+ }
+ else
+ {
+ macErr= ::FSpMoveRenameCompat(&srcSpec, &destSpec, newPascalName);
+ if ( macErr == diffVolErr)
+ {
+ // On a different Volume so go for Copy and then delete
+ rv = CopyToNative( newParentDir, newName );
+ if ( NS_FAILED ( rv ) )
+ return rv;
+ return Remove( PR_TRUE );
+ }
+ }
+ return MacErrorMapper( macErr );
+}
+
+NS_IMETHODIMP
+nsLocalFile::CopyToNative(nsIFile *newParentDir, const nsACString &newName)
+{
+ return MoveCopy( newParentDir, newName, PR_TRUE, PR_FALSE );
+}
+
+NS_IMETHODIMP
+nsLocalFile::CopyTo(nsIFile *newParentDir, const nsAString &newName)
+{
+ if (newName.IsEmpty())
+ return CopyToNative(newParentDir, nsCString());
+
+ nsresult rv;
+ nsCAutoString fsStr;
+ if (NS_SUCCEEDED(rv = NS_CopyUnicodeToNative(newName, fsStr)))
+ rv = CopyToNative(newParentDir, fsStr);
+ return rv;
+}
+
+NS_IMETHODIMP
+nsLocalFile::CopyToFollowingLinksNative(nsIFile *newParentDir, const nsACString &newName)
+{
+ return MoveCopy( newParentDir, newName, PR_TRUE, PR_TRUE );
+}
+
+NS_IMETHODIMP
+nsLocalFile::CopyToFollowingLinks(nsIFile *newParentDir, const nsAString &newName)
+{
+ if (newName.IsEmpty())
+ return CopyToFollowingLinksNative(newParentDir, nsCString());
+
+ nsresult rv;
+ nsCAutoString fsStr;
+ if (NS_SUCCEEDED(rv = NS_CopyUnicodeToNative(newName, fsStr)))
+ rv = CopyToFollowingLinksNative(newParentDir, fsStr);
+ return rv;
+}
+
+NS_IMETHODIMP
+nsLocalFile::MoveToNative(nsIFile *newParentDir, const nsACString &newName)
+{
+ return MoveCopy( newParentDir, newName, PR_FALSE, PR_FALSE );
+}
+
+NS_IMETHODIMP
+nsLocalFile::MoveTo(nsIFile *newParentDir, const nsAString &newName)
+{
+ if (newName.IsEmpty())
+ return MoveToNative(newParentDir, nsCString());
+
+ nsresult rv;
+ nsCAutoString fsStr;
+ if (NS_SUCCEEDED(rv = NS_CopyUnicodeToNative(newName, fsStr)))
+ rv = MoveToNative(newParentDir, fsStr);
+ return rv;
+}
+
+NS_IMETHODIMP
+nsLocalFile::Load(PRLibrary * *_retval)
+{
+ PRBool isFile;
+ nsresult rv = IsFile(&isFile);
+
+ if (NS_FAILED(rv))
+ return rv;
+
+ if (! isFile)
+ return NS_ERROR_FILE_IS_DIRECTORY;
+
+ NS_TIMELINE_START_TIMER("PR_LoadLibrary");
+
+#if !TARGET_CARBON
+ // This call to SystemTask is here to give the OS time to grow its
+ // FCB (file control block) list, which it seems to be unable to
+ // do unless we yield some time to the OS. See bugs 64978 & 70543
+ // for the whole story.
+ ::SystemTask();
+#endif
+
+ // Use the new PR_LoadLibraryWithFlags which allows us to use a FSSpec
+ PRLibSpec libSpec;
+ libSpec.type = PR_LibSpec_MacIndexedFragment;
+ libSpec.value.mac_indexed_fragment.fsspec = &mTargetSpec;
+ libSpec.value.mac_indexed_fragment.index = 0;
+ *_retval = PR_LoadLibraryWithFlags(libSpec, 0);
+
+ NS_TIMELINE_STOP_TIMER("PR_LoadLibrary");
+ NS_TIMELINE_MARK_TIMER("PR_LoadLibrary");
+
+ if (*_retval)
+ return NS_OK;
+
+ return NS_ERROR_NULL_POINTER;
+}
+
+NS_IMETHODIMP
+nsLocalFile::Remove(PRBool recursive)
+{
+ OSErr err;
+ nsresult rv;
+ FSSpec specToDelete;
+ PRBool isDir;
+
+ StFollowLinksState(this, PR_FALSE);
+
+ rv = IsDirectory(&isDir); // Calls ResolveAndStat()
+ if (NS_FAILED(rv))
+ return rv;
+ rv = GetFSSpec(&specToDelete);
+ if (NS_FAILED(rv))
+ return rv;
+
+ if (isDir && recursive)
+ err = ::DeleteDirectory( specToDelete.vRefNum, specToDelete.parID, specToDelete.name );
+ else
+ err = ::HDelete( specToDelete.vRefNum, specToDelete.parID, specToDelete.name );
+
+ return MacErrorMapper( err );
+}
+
+NS_IMETHODIMP
+nsLocalFile::GetLastModifiedTime(PRInt64 *aLastModifiedTime)
+{
+ NS_ENSURE_ARG(aLastModifiedTime);
+ *aLastModifiedTime = 0;
+
+ nsresult rv = ResolveAndStat();
+ if ( NS_FAILED( rv ) )
+ return rv;
+ rv = UpdateCachedCatInfo(PR_TRUE);
+ if ( NS_FAILED( rv ) )
+ return rv;
+
+ // The mod date is in the same spot for files and dirs.
+ return ConvertMacTimeToMilliseconds( aLastModifiedTime, mCachedCatInfo.hFileInfo.ioFlMdDat );
+}
+
+NS_IMETHODIMP
+nsLocalFile::SetLastModifiedTime(PRInt64 aLastModifiedTime)
+{
+ nsresult rv = ResolveAndStat();
+ if ( NS_FAILED(rv) )
+ return rv;
+
+ PRUint32 macTime = 0;
+ OSErr err = noErr;
+
+ ConvertMillisecondsToMacTime(aLastModifiedTime, &macTime);
+
+ if (NS_SUCCEEDED(rv = UpdateCachedCatInfo(PR_TRUE)))
+ {
+ if (mCachedCatInfo.hFileInfo.ioFlAttrib & ioDirMask)
+ {
+ mCachedCatInfo.dirInfo.ioDrMdDat = macTime;
+ mCachedCatInfo.dirInfo.ioDrParID = mFollowLinks ? mTargetSpec.parID : mSpec.parID;
+ }
+ else
+ {
+ mCachedCatInfo.hFileInfo.ioFlMdDat = macTime;
+ mCachedCatInfo.hFileInfo.ioDirID = mFollowLinks ? mTargetSpec.parID : mSpec.parID;
+ }
+
+ err = ::PBSetCatInfoSync(&mCachedCatInfo);
+ if (err != noErr)
+ return MacErrorMapper(err);
+ }
+
+ return rv;
+}
+
+NS_IMETHODIMP
+nsLocalFile::GetLastModifiedTimeOfLink(PRInt64 *aLastModifiedTime)
+{
+ NS_ENSURE_ARG(aLastModifiedTime);
+
+ nsresult rv;
+ PRBool isLink;
+
+ rv = IsSymlink(&isLink);
+ if (NS_FAILED(rv))
+ return rv;
+ if (!isLink)
+ return NS_ERROR_FAILURE;
+
+ StFollowLinksState followState(this, PR_FALSE);
+ return GetLastModifiedTime(aLastModifiedTime);
+}
+
+NS_IMETHODIMP
+nsLocalFile::SetLastModifiedTimeOfLink(PRInt64 aLastModifiedTime)
+{
+ nsresult rv;
+ PRBool isLink;
+
+ rv = IsSymlink(&isLink);
+ if (NS_FAILED(rv))
+ return rv;
+ if (!isLink)
+ return NS_ERROR_FAILURE;
+
+ StFollowLinksState followState(this, PR_FALSE);
+ return SetLastModifiedTime(aLastModifiedTime);
+}
+
+
+NS_IMETHODIMP
+nsLocalFile::GetFileSize(PRInt64 *aFileSize)
+{
+ NS_ENSURE_ARG(aFileSize);
+ nsresult rv;
+
+ *aFileSize = LL_Zero();
+
+ if (NS_SUCCEEDED(rv = ResolveAndStat()) && NS_SUCCEEDED(rv = UpdateCachedCatInfo(PR_TRUE)))
+ {
+ if (!(mCachedCatInfo.hFileInfo.ioFlAttrib & ioDirMask))
+ {
+ long dataSize = mCachedCatInfo.hFileInfo.ioFlLgLen;
+ long resSize = mCachedCatInfo.hFileInfo.ioFlRLgLen;
+
+ // For now we've only got 32 bits of file size info
+ PRInt64 dataInt64 = LL_Zero();
+ PRInt64 resInt64 = LL_Zero();
+
+ // WARNING!!!!!!
+ //
+ // For now we do NOT add the data and resource fork sizes as there are several
+ // assumptions in the code (notably in form submit) that only the data fork is
+ // used.
+ // LL_I2L(resInt64, resSize);
+
+ LL_I2L(dataInt64, dataSize);
+
+ LL_ADD((*aFileSize), dataInt64, resInt64);
+ }
+ // leave size at zero for dirs
+ }
+
+ return rv;
+}
+
+
+NS_IMETHODIMP
+nsLocalFile::SetFileSize(PRInt64 aFileSize)
+{
+ nsresult rv = ResolveAndStat();
+ if (NS_FAILED(rv))
+ return rv;
+
+ short refNum;
+ OSErr err;
+ PRInt32 aNewLength;
+
+ LL_L2I(aNewLength, aFileSize);
+
+ // Need to open the file to set the size
+ if (::FSpOpenDF(&mTargetSpec, fsWrPerm, &refNum) != noErr)
+ return NS_ERROR_FILE_ACCESS_DENIED;
+
+ err = ::SetEOF(refNum, aNewLength);
+
+ // Close the file unless we got an error that it was already closed
+ if (err != fnOpnErr)
+ (void)::FSClose(refNum);
+
+ if (err != noErr)
+ return MacErrorMapper(err);
+
+ return MacErrorMapper(err);
+}
+
+NS_IMETHODIMP
+nsLocalFile::GetFileSizeOfLink(PRInt64 *aFileSize)
+{
+ NS_ENSURE_ARG(aFileSize);
+
+ StFollowLinksState followState(this, PR_FALSE);
+ return GetFileSize(aFileSize);
+}
+
+NS_IMETHODIMP
+nsLocalFile::GetDiskSpaceAvailable(PRInt64 *aDiskSpaceAvailable)
+{
+ NS_ENSURE_ARG(aDiskSpaceAvailable);
+
+ PRInt64 space64Bits;
+
+ LL_I2L(space64Bits , LONG_MAX);
+
+ nsresult rv = ResolveAndStat();
+ if (NS_FAILED(rv))
+ return rv;
+
+ XVolumeParam pb;
+ pb.ioCompletion = nsnull;
+ pb.ioVolIndex = 0;
+ pb.ioNamePtr = nsnull;
+ pb.ioVRefNum = mFollowLinks ? mTargetSpec.vRefNum : mSpec.vRefNum;
+
+ // we should check if this call is available
+ OSErr err = ::PBXGetVolInfoSync(&pb);
+
+ if (err == noErr)
+ {
+ const UnsignedWide& freeBytes = UInt64ToUnsignedWide(pb.ioVFreeBytes);
+#ifdef HAVE_LONG_LONG
+ space64Bits = UnsignedWideToUInt64(freeBytes);
+#else
+ space64Bits.lo = freeBytes.lo;
+ space64Bits.hi = freeBytes.hi;
+#endif
+ }
+
+ *aDiskSpaceAvailable = space64Bits;
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::GetParent(nsIFile * *aParent)
+{
+ NS_ENSURE_ARG_POINTER(aParent);
+ *aParent = nsnull;
+
+ nsresult rv = NS_OK;
+ PRInt32 offset;
+
+ nsCOMPtr<nsILocalFileMac> localFile;
+ PRInt32 appendedLen = mAppendedPath.Length();
+ OSErr err;
+
+ if (!appendedLen || (appendedLen == 1 && mAppendedPath.CharAt(0) == ':'))
+ {
+ rv = ResolveAndStat();
+ //if the file does not exist, does not mean that the parent does not.
+ if (NS_FAILED(rv) && rv != NS_ERROR_FILE_NOT_FOUND)
+ return rv;
+
+ CInfoPBRec pBlock = {0};
+ FSSpec parentFolderSpec;
+ parentFolderSpec.name[0] = 0;
+
+ pBlock.dirInfo.ioVRefNum = mSpec.vRefNum;
+ pBlock.dirInfo.ioDrDirID = mSpec.parID;
+ pBlock.dirInfo.ioNamePtr = (StringPtr)parentFolderSpec.name;
+ pBlock.dirInfo.ioFDirIndex = -1; //get info on parID
+ err = PBGetCatInfoSync(&pBlock);
+ if (err != noErr)
+ return MacErrorMapper(err);
+ parentFolderSpec.vRefNum = mSpec.vRefNum;
+ parentFolderSpec.parID = pBlock.dirInfo.ioDrParID;
+
+ localFile = new nsLocalFile;
+ if (!localFile)
+ return NS_ERROR_OUT_OF_MEMORY;
+ rv = localFile->InitWithFSSpec(&parentFolderSpec);
+ if (NS_FAILED(rv))
+ return rv;
+ }
+ else
+ {
+ // trim off the last component of the appended path
+ // construct a new file from our spec + trimmed path
+
+ nsCAutoString parentAppendage(mAppendedPath);
+
+ if (parentAppendage.Last() == ':')
+ parentAppendage.Truncate(appendedLen - 1);
+ if ((offset = parentAppendage.RFindChar(':')) != -1)
+ parentAppendage.Truncate(offset);
+ else
+ parentAppendage.Truncate(0);
+
+ localFile = new nsLocalFile(mSpec, parentAppendage);
+ if (!localFile)
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ *aParent = localFile;
+ NS_ADDREF(*aParent);
+
+ return rv;
+}
+
+
+NS_IMETHODIMP
+nsLocalFile::Exists(PRBool *_retval)
+{
+ NS_ENSURE_ARG(_retval);
+ *_retval = PR_FALSE;
+
+ nsresult rv = ResolveAndStat();
+ if (NS_SUCCEEDED(rv)) {
+ if (NS_SUCCEEDED(UpdateCachedCatInfo(PR_TRUE)))
+ *_retval = PR_TRUE;
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::IsPackage(PRBool *outIsPackage)
+{
+ NS_ENSURE_ARG(outIsPackage);
+ *outIsPackage = PR_FALSE;
+
+ // Note: IsDirectory() calls ResolveAndStat() & UpdateCachedCatInfo
+ PRBool isDir;
+ nsresult rv = IsDirectory(&isDir);
+ if (NS_FAILED(rv)) return rv;
+
+ *outIsPackage = ((mCachedCatInfo.dirInfo.ioFlAttrib & kioFlAttribDirMask) &&
+ (mCachedCatInfo.dirInfo.ioDrUsrWds.frFlags & kHasBundle));
+
+ if ((!*outIsPackage) && isDir)
+ {
+ // Believe it or not, folders ending with ".app" are also considered
+ // to be packages, even if the top-level folder doesn't have bundle set
+ nsCAutoString name;
+ if (NS_SUCCEEDED(rv = GetNativeLeafName(name)))
+ {
+ const char *extPtr = strrchr(name.get(), '.');
+ if (extPtr)
+ {
+ if (!nsCRT::strcasecmp(extPtr, ".app"))
+ {
+ *outIsPackage = PR_TRUE;
+ }
+ }
+ }
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::IsWritable(PRBool *outIsWritable)
+{
+ NS_ENSURE_ARG(outIsWritable);
+ *outIsWritable = PR_TRUE;
+
+ nsresult rv = ResolveAndStat();
+ if (NS_FAILED(rv)) return rv;
+
+ rv = UpdateCachedCatInfo(PR_TRUE);
+ if (NS_FAILED(rv)) return rv;
+
+ *outIsWritable = !(mCachedCatInfo.hFileInfo.ioFlAttrib & kioFlAttribLockedMask);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::IsReadable(PRBool *_retval)
+{
+ NS_ENSURE_ARG(_retval);
+
+ // is it ever not readable on Mac?
+ *_retval = PR_TRUE;
+ return NS_OK;
+}
+
+
+NS_IMETHODIMP
+nsLocalFile::IsExecutable(PRBool *outIsExecutable)
+{
+ NS_ENSURE_ARG(outIsExecutable);
+ *outIsExecutable = PR_FALSE; // Assume failure
+
+ nsresult rv = ResolveAndStat();
+ if (NS_FAILED(rv)) return rv;
+
+#if TARGET_CARBON
+ // If we're running under OS X ask LaunchServices if we're executable
+ if (sRunningOSX)
+ {
+ if ( (UInt32)LSCopyItemInfoForRef != (UInt32)kUnresolvedCFragSymbolAddress )
+ {
+ FSRef theRef;
+ LSRequestedInfo theInfoRequest = kLSRequestAllInfo;
+ LSItemInfoRecord theInfo;
+
+ if (::FSpMakeFSRef(&mTargetSpec, &theRef) == noErr)
+ {
+ if (::LSCopyItemInfoForRef(&theRef, theInfoRequest, &theInfo) == noErr)
+ {
+ if ((theInfo.flags & kLSItemInfoIsApplication) != 0)
+ *outIsExecutable = PR_TRUE;
+ }
+ }
+ }
+ }
+ else
+#endif
+ {
+ OSType fileType;
+ rv = GetFileType(&fileType);
+ if (NS_FAILED(rv)) return rv;
+
+ *outIsExecutable = (fileType == 'APPL' || fileType == 'appe' || fileType == 'FNDR');
+ }
+
+ return NS_OK;
+}
+
+
+NS_IMETHODIMP
+nsLocalFile::IsDirectory(PRBool *outIsDir)
+{
+ NS_ENSURE_ARG(outIsDir);
+ *outIsDir = PR_FALSE;
+
+ nsresult rv = ResolveAndStat();
+ if (NS_FAILED(rv)) return rv;
+
+ rv = UpdateCachedCatInfo(PR_FALSE);
+ if (NS_FAILED(rv)) return rv;
+
+ *outIsDir = (mCachedCatInfo.hFileInfo.ioFlAttrib & ioDirMask) != 0;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::IsFile(PRBool *outIsFile)
+{
+ NS_ENSURE_ARG(outIsFile);
+ *outIsFile = PR_FALSE;
+
+ nsresult rv = ResolveAndStat();
+ if (NS_FAILED(rv)) return rv;
+
+ rv = UpdateCachedCatInfo(PR_FALSE);
+ if (NS_FAILED(rv)) return rv;
+
+ *outIsFile = (mCachedCatInfo.hFileInfo.ioFlAttrib & ioDirMask) == 0;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::IsHidden(PRBool *_retval)
+{
+ NS_ENSURE_ARG(_retval);
+ *_retval = PR_FALSE;
+
+ nsresult rv = ResolveAndStat();
+ if (NS_FAILED(rv)) return rv;
+
+ rv = UpdateCachedCatInfo(PR_FALSE);
+ if (NS_FAILED(rv)) return rv;
+
+ *_retval = (mCachedCatInfo.hFileInfo.ioFlFndrInfo.fdFlags & kIsInvisible) != 0;
+
+ if (sRunningOSX)
+ {
+ // on Mac OS X, also follow Unix "convention" where files
+ // beginning with a period are considered to be hidden
+ nsCAutoString name;
+ if (NS_SUCCEEDED(rv = GetNativeLeafName(name)))
+ {
+ if (name.First() == '.')
+ {
+ *_retval = PR_TRUE;
+ }
+ }
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::IsSymlink(PRBool *_retval)
+{
+ NS_ENSURE_ARG(_retval);
+ *_retval = PR_FALSE;
+
+ nsresult rv = ResolveAndStat();
+ if (NS_FAILED(rv)) return rv;
+
+ Boolean isAlias, isFolder;
+ if (::IsAliasFile(&mSpec, &isAlias, &isFolder) == noErr)
+ *_retval = isAlias;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::Equals(nsIFile *inFile, PRBool *_retval)
+{
+ NS_ENSURE_ARG(inFile);
+ NS_ENSURE_ARG(_retval);
+ *_retval = PR_FALSE;
+
+ // Building paths is expensive. If we can get the FSSpecs of
+ // both (they or their parents exist) just compare the specs.
+ nsCOMPtr<nsILocalFileMac> inMacFile(do_QueryInterface(inFile));
+ FSSpec fileSpec, inFileSpec;
+ if (NS_SUCCEEDED(GetFSSpec(&fileSpec)) && inMacFile && NS_SUCCEEDED(inMacFile->GetFSSpec(&inFileSpec)))
+ *_retval = IsEqualFSSpec(fileSpec, inFileSpec);
+ else
+ {
+ nsCAutoString filePath;
+ GetNativePath(filePath);
+
+ nsXPIDLCString inFilePath;
+ inFile->GetNativePath(inFilePath);
+
+ if (nsCRT::strcasecmp(inFilePath.get(), filePath.get()) == 0)
+ *_retval = PR_TRUE;
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::Contains(nsIFile *inFile, PRBool recur, PRBool *outContains)
+{
+ /* Note here that we make no attempt to deal with the problem
+ of folder aliases. Doing a 'Contains' test and dealing with
+ folder aliases is Hard. Think about it.
+ */
+ *outContains = PR_FALSE;
+
+ PRBool isDir;
+ nsresult rv = IsDirectory(&isDir); // need to cache this
+ if (NS_FAILED(rv)) return rv;
+ if (!isDir) return NS_OK; // must be a dir to contain someone
+
+ nsCOMPtr<nsILocalFileMac> macFile(do_QueryInterface(inFile));
+ if (!macFile) return NS_OK; // trying to compare non-local with local file
+
+ FSSpec mySpec = mSpec;
+ FSSpec compareSpec;
+
+ // NOTE: we're not resolving inFile if it was an alias
+ StFollowLinksState followState(macFile, PR_FALSE);
+ rv = macFile->GetFSSpec(&compareSpec);
+ if (NS_FAILED(rv)) return rv;
+
+ // if they are on different volumes, bail
+ if (mSpec.vRefNum != compareSpec.vRefNum)
+ return NS_OK;
+
+ // if recur == true, test every parent, otherwise just test the first one
+ // (yes, recur does not get set in this loop)
+ OSErr err = noErr;
+ do
+ {
+ FSSpec parentFolderSpec;
+ err = GetParentFolderSpec(compareSpec, parentFolderSpec);
+ if (err != noErr) break; // we reached the top
+
+ if (IsEqualFSSpec(parentFolderSpec, mySpec))
+ {
+ *outContains = PR_TRUE;
+ break;
+ }
+
+ compareSpec = parentFolderSpec;
+ } while (recur);
+
+ return NS_OK;
+}
+
+
+
+NS_IMETHODIMP
+nsLocalFile::GetNativeTarget(nsACString &_retval)
+{
+ _retval.Truncate();
+
+ PRBool symLink;
+
+ nsresult rv = IsSymlink(&symLink);
+ if (NS_FAILED(rv))
+ return rv;
+
+ if (!symLink)
+ return NS_ERROR_FILE_INVALID_PATH;
+
+ StFollowLinksState followState(this, PR_TRUE);
+ return GetNativePath(_retval);
+}
+
+NS_IMETHODIMP
+nsLocalFile::GetTarget(nsAString &_retval)
+{
+ nsresult rv;
+ nsCAutoString fsStr;
+
+ if (NS_SUCCEEDED(rv = GetNativeTarget(fsStr))) {
+ rv = NS_CopyNativeToUnicode(fsStr, _retval);
+ }
+ return rv;
+}
+
+NS_IMETHODIMP
+nsLocalFile::GetDirectoryEntries(nsISimpleEnumerator * *entries)
+{
+ nsresult rv;
+
+ *entries = nsnull;
+
+ PRBool isDir;
+ rv = IsDirectory(&isDir);
+ if (NS_FAILED(rv))
+ return rv;
+ if (!isDir)
+ return NS_ERROR_FILE_NOT_DIRECTORY;
+
+ nsDirEnumerator* dirEnum = new nsDirEnumerator();
+ if (dirEnum == nsnull)
+ return NS_ERROR_OUT_OF_MEMORY;
+ NS_ADDREF(dirEnum);
+ rv = dirEnum->Init(this);
+ if (NS_FAILED(rv))
+ {
+ NS_RELEASE(dirEnum);
+ return rv;
+ }
+
+ *entries = dirEnum;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::GetPersistentDescriptor(nsACString &aPersistentDescriptor)
+{
+ aPersistentDescriptor.Truncate();
+
+ nsresult rv = ResolveAndStat();
+ if ( NS_FAILED( rv ) )
+ return rv;
+
+ AliasHandle aliasH;
+ OSErr err = ::NewAlias(nil, &mTargetSpec, &aliasH);
+ if (err != noErr)
+ return MacErrorMapper(err);
+
+ PRUint32 bytes = ::GetHandleSize((Handle) aliasH);
+ HLock((Handle) aliasH);
+ char* buf = PL_Base64Encode((const char*)*aliasH, bytes, nsnull); // Passing nsnull for dest makes NULL-term string
+ ::DisposeHandle((Handle) aliasH);
+ NS_ENSURE_TRUE(buf, NS_ERROR_OUT_OF_MEMORY);
+
+ aPersistentDescriptor = buf;
+ PR_Free(buf);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::SetPersistentDescriptor(const nsACString &aPersistentDescriptor)
+{
+ if (aPersistentDescriptor.IsEmpty())
+ return NS_ERROR_INVALID_ARG;
+
+ nsresult rv = NS_OK;
+
+ PRUint32 dataSize = aPersistentDescriptor.Length();
+ char* decodedData = PL_Base64Decode(PromiseFlatCString(aPersistentDescriptor).get(), dataSize, nsnull);
+ // Cast to an alias record and resolve.
+ AliasHandle aliasH = nsnull;
+ if (::PtrToHand(decodedData, &(Handle)aliasH, (dataSize * 3) / 4) != noErr)
+ rv = NS_ERROR_OUT_OF_MEMORY;
+ PR_Free(decodedData);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ Boolean changed;
+ FSSpec resolvedSpec;
+ OSErr err;
+ err = ::ResolveAlias(nsnull, aliasH, &resolvedSpec, &changed);
+ if (err == fnfErr) // resolvedSpec is valid in this case
+ err = noErr;
+ rv = MacErrorMapper(err);
+ DisposeHandle((Handle) aliasH);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return InitWithFSSpec(&resolvedSpec);
+}
+
+#pragma mark -
+
+// a stack-based, exception safe class for an AEDesc
+
+#pragma mark
+class StAEDesc: public AEDesc
+{
+public:
+ StAEDesc()
+ {
+ descriptorType = typeNull;
+ dataHandle = nil;
+ }
+
+ ~StAEDesc()
+ {
+ ::AEDisposeDesc(this);
+ }
+
+ void Clear()
+ {
+ ::AEDisposeDesc(this);
+ descriptorType = typeNull;
+ dataHandle = nil;
+ }
+
+private:
+ // disallow copies and assigns
+ StAEDesc(const StAEDesc& rhs); // copy constructor
+ StAEDesc& operator= (const StAEDesc&rhs); // throws OSErrs
+
+};
+
+#pragma mark -
+#pragma mark [Utility methods]
+
+
+nsresult nsLocalFile::UpdateCachedCatInfo(PRBool forceUpdate)
+{
+ if (!mCatInfoDirty && !forceUpdate)
+ return NS_OK;
+
+ FSSpec spectoUse = mFollowLinks ? mTargetSpec : mSpec;
+ mCachedCatInfo.hFileInfo.ioCompletion = nsnull;
+ mCachedCatInfo.hFileInfo.ioFDirIndex = 0; // use dirID and name
+ mCachedCatInfo.hFileInfo.ioVRefNum = spectoUse.vRefNum;
+ mCachedCatInfo.hFileInfo.ioDirID = spectoUse.parID;
+ mCachedCatInfo.hFileInfo.ioNamePtr = spectoUse.name;
+
+ OSErr err = ::PBGetCatInfoSync(&mCachedCatInfo);
+ if (err == noErr)
+ {
+ mCatInfoDirty = PR_FALSE;
+ return NS_OK;
+ }
+ return MacErrorMapper(err);
+}
+
+
+nsresult nsLocalFile::FindRunningAppBySignature (OSType aAppSig, FSSpec& outSpec, ProcessSerialNumber& outPsn)
+{
+ ProcessInfoRec info;
+ FSSpec tempFSSpec;
+ OSErr err = noErr;
+
+ outPsn.highLongOfPSN = 0;
+ outPsn.lowLongOfPSN = kNoProcess;
+
+ while (PR_TRUE)
+ {
+ err = ::GetNextProcess(&outPsn);
+ if (err == procNotFound) break;
+ if (err != noErr) return NS_ERROR_FAILURE;
+ info.processInfoLength = sizeof(ProcessInfoRec);
+ info.processName = nil;
+ info.processAppSpec = &tempFSSpec;
+ err = ::GetProcessInformation(&outPsn, &info);
+ if (err != noErr) return NS_ERROR_FAILURE;
+
+ if (info.processSignature == aAppSig)
+ {
+ outSpec = tempFSSpec;
+ return NS_OK;
+ }
+ }
+
+ return NS_ERROR_FILE_NOT_FOUND; // really process not found
+}
+
+
+nsresult nsLocalFile::FindRunningAppByFSSpec(const FSSpec& appSpec, ProcessSerialNumber& outPsn)
+{
+ ProcessInfoRec info;
+ FSSpec tempFSSpec;
+ OSErr err = noErr;
+
+ outPsn.highLongOfPSN = 0;
+ outPsn.lowLongOfPSN = kNoProcess;
+
+ while (PR_TRUE)
+ {
+ err = ::GetNextProcess(&outPsn);
+ if (err == procNotFound) break;
+ if (err != noErr) return NS_ERROR_FAILURE;
+ info.processInfoLength = sizeof(ProcessInfoRec);
+ info.processName = nil;
+ info.processAppSpec = &tempFSSpec;
+ err = ::GetProcessInformation(&outPsn, &info);
+ if (err != noErr) return NS_ERROR_FAILURE;
+
+ if (IsEqualFSSpec(appSpec, *info.processAppSpec))
+ {
+ return NS_OK;
+ }
+ }
+
+ return NS_ERROR_FILE_NOT_FOUND; // really process not found
+}
+
+
+nsresult nsLocalFile::FindAppOnLocalVolumes(OSType sig, FSSpec &outSpec)
+{
+ OSErr err;
+
+ // get the system volume
+ long systemFolderDirID;
+ short sysVRefNum;
+ err = FindFolder(kOnSystemDisk, kSystemFolderType, false, &sysVRefNum, &systemFolderDirID);
+ if (err != noErr) return NS_ERROR_FAILURE;
+
+ short vRefNum = sysVRefNum;
+ short index = 0;
+
+ while (true)
+ {
+ if (index == 0 || vRefNum != sysVRefNum)
+ {
+ // should we avoid AppleShare volumes?
+
+ Boolean hasDesktopDB;
+ err = VolHasDesktopDB(vRefNum, &hasDesktopDB);
+ if (err != noErr) return err;
+ if (hasDesktopDB)
+ {
+ err = FindAppOnVolume(sig, vRefNum, &outSpec);
+ if (err != afpItemNotFound) return err;
+ }
+ }
+ index++;
+ err = GetIndVolume(index, &vRefNum);
+ if (err == nsvErr) return fnfErr;
+ if (err != noErr) return err;
+ }
+
+ return NS_OK;
+}
+
+#define aeSelectionKeyword 'fsel'
+#define kAEOpenSelection 'sope'
+#define kAERevealSelection 'srev'
+#define kFinderType 'FNDR'
+
+NS_IMETHODIMP nsLocalFile::Launch()
+{
+ AppleEvent aeEvent = {0, nil};
+ AppleEvent aeReply = {0, nil};
+ StAEDesc aeDirDesc, listElem, myAddressDesc, fileList;
+ FSSpec dirSpec, appSpec;
+ AliasHandle DirAlias, FileAlias;
+ OSErr errorResult = noErr;
+ ProcessSerialNumber process;
+
+ // for launching a file, we'll use mTargetSpec (which is both a resolved spec and a resolved alias)
+ ResolveAndStat();
+
+#if TARGET_CARBON
+ if (sRunningOSX)
+ { // We're running under Mac OS X, LaunchServices here we come
+
+ // First we make sure the LaunchServices routine we want is implemented
+ if ( (UInt32)LSOpenFSRef != (UInt32)kUnresolvedCFragSymbolAddress )
+ {
+ FSRef theRef;
+ if (::FSpMakeFSRef(&mTargetSpec, &theRef) == noErr)
+ {
+ (void)::LSOpenFSRef(&theRef, NULL);
+ }
+ }
+ }
+ else
+#endif
+ { // We're running under Mac OS 8.x/9.x, use the Finder Luke
+ nsresult rv = FindRunningAppBySignature ('MACS', appSpec, process);
+ if (NS_SUCCEEDED(rv))
+ {
+ errorResult = AECreateDesc(typeProcessSerialNumber, (Ptr)&process, sizeof(process), &myAddressDesc);
+ if (errorResult == noErr)
+ {
+ /* Create the FinderEvent */
+ errorResult = AECreateAppleEvent(kFinderType, kAEOpenSelection, &myAddressDesc, kAutoGenerateReturnID, kAnyTransactionID,
+ &aeEvent);
+ if (errorResult == noErr)
+ {
+ errorResult = FSMakeFSSpec(mTargetSpec.vRefNum, mTargetSpec.parID, nil, &dirSpec);
+ NewAlias(nil, &dirSpec, &DirAlias);
+ /* Create alias for file */
+ NewAlias(nil, &mTargetSpec, &FileAlias);
+
+ /* Create the file list */
+ errorResult = AECreateList(nil, 0, false, &fileList);
+ /* create the folder descriptor */
+ HLock((Handle)DirAlias);
+ errorResult = AECreateDesc(typeAlias, (Ptr)*DirAlias, GetHandleSize((Handle)DirAlias), &aeDirDesc);
+ HUnlock((Handle)DirAlias);
+ if (errorResult == noErr)
+ {
+ errorResult = AEPutParamDesc(&aeEvent, keyDirectObject, &aeDirDesc);
+ if ( errorResult == noErr)
+ {
+ /* create the file descriptor and add to aliasList */
+ HLock((Handle)FileAlias);
+ errorResult = AECreateDesc(typeAlias, (Ptr)*FileAlias, GetHandleSize((Handle)FileAlias), &listElem);
+ HLock((Handle)FileAlias);
+ if (errorResult == noErr)
+ {
+ errorResult = AEPutDesc(&fileList, 0, &listElem);
+ if (errorResult == noErr)
+ {
+ /* Add the file alias list to the event */
+ errorResult = AEPutParamDesc(&aeEvent, aeSelectionKeyword, &fileList);
+ if (errorResult == noErr)
+ AESend(&aeEvent, &aeReply, kAEWaitReply + kAENeverInteract
+ + kAECanSwitchLayer, kAEHighPriority, kAEDefaultTimeout, nil, nil);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsLocalFile::Reveal()
+{
+ FSSpec specToReveal;
+ AppleEvent aeEvent = {0, nil};
+ AppleEvent aeReply = {0, nil};
+ StAEDesc aeDirDesc, listElem, myAddressDesc, fileList;
+ OSErr errorResult = noErr;
+ ProcessSerialNumber process;
+ FSSpec appSpec;
+
+ nsresult rv = ResolveAndStat();
+ if (NS_FAILED(rv))
+ return rv;
+ rv = GetFSSpec(&specToReveal); // Pay attention to followLinks
+ if (NS_FAILED(rv))
+ return rv;
+
+ rv = FindRunningAppBySignature ('MACS', appSpec, process);
+ if (NS_SUCCEEDED(rv))
+ {
+ errorResult = AECreateDesc(typeProcessSerialNumber, (Ptr)&process, sizeof(process), &myAddressDesc);
+ if (errorResult == noErr)
+ {
+ /* Create the FinderEvent */
+#if TARGET_CARBON
+ // The Finder under OS X uses a different event to reveal
+ if (sRunningOSX)
+ errorResult = AECreateAppleEvent(kAEMiscStandards, kAEMakeObjectsVisible, &myAddressDesc, kAutoGenerateReturnID, kAnyTransactionID,
+ &aeEvent);
+ else
+#endif
+ errorResult = AECreateAppleEvent(kFinderType, kAERevealSelection, &myAddressDesc, kAutoGenerateReturnID, kAnyTransactionID,
+ &aeEvent);
+ if (errorResult == noErr)
+ {
+ /* Create the file list */
+ errorResult = AECreateList(nil, 0, false, &fileList);
+ if (errorResult == noErr)
+ {
+ errorResult = AEPutPtr(&fileList, 0, typeFSS, &specToReveal, sizeof(FSSpec));
+
+ if (errorResult == noErr)
+ {
+#if TARGET_CARBON
+ // When we're sending the event under OS X the FSSpec must be a keyDirectObject
+ if (sRunningOSX)
+ errorResult = AEPutParamDesc(&aeEvent, keyDirectObject, &fileList);
+ else
+#endif
+ errorResult = AEPutParamDesc(&aeEvent,keySelection, &fileList);
+
+ if (errorResult == noErr)
+ {
+ errorResult = AESend(&aeEvent, &aeReply, kAENoReply, kAENormalPriority, kAEDefaultTimeout, nil, nil);
+ if (errorResult == noErr)
+ SetFrontProcess(&process);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return NS_OK;
+}
+
+nsresult nsLocalFile::MyLaunchAppWithDoc(const FSSpec& appSpec, const FSSpec* aDocToLoad, PRBool aLaunchInBackground)
+{
+ ProcessSerialNumber thePSN = {0};
+ StAEDesc target;
+ StAEDesc docDesc;
+ StAEDesc launchDesc;
+ StAEDesc docList;
+ AppleEvent theEvent = {0, nil};
+ AppleEvent theReply = {0, nil};
+ OSErr err = noErr;
+ Boolean autoParamValue = false;
+ Boolean running = false;
+ nsresult rv = NS_OK;
+
+#if TARGET_CARBON
+ if (sRunningOSX)
+ { // Under Mac OS X we'll use LaunchServices
+
+ // First we make sure the LaunchServices routine we want is implemented
+ if ( (UInt32)LSOpenFromRefSpec != (UInt32)kUnresolvedCFragSymbolAddress )
+ {
+ FSRef appRef;
+ FSRef docRef;
+ LSLaunchFlags theLaunchFlags = kLSLaunchDefaults;
+ LSLaunchFSRefSpec thelaunchSpec;
+
+ if (::FSpMakeFSRef(&appSpec, &appRef) != noErr)
+ return NS_ERROR_FAILURE;
+
+ if (aDocToLoad)
+ if (::FSpMakeFSRef(aDocToLoad, &docRef) != noErr)
+ return NS_ERROR_FAILURE;
+
+ if (aLaunchInBackground)
+ theLaunchFlags |= kLSLaunchDontSwitch;
+
+ memset(&thelaunchSpec, 0, sizeof(LSLaunchFSRefSpec));
+
+ thelaunchSpec.appRef = &appRef;
+ if (aDocToLoad)
+ {
+ thelaunchSpec.numDocs = 1;
+ thelaunchSpec.itemRefs = &docRef;
+ }
+ thelaunchSpec.launchFlags = theLaunchFlags;
+
+ err = ::LSOpenFromRefSpec(&thelaunchSpec, NULL);
+ NS_ASSERTION((err != noErr), "Error calling LSOpenFromRefSpec");
+ if (err != noErr) return NS_ERROR_FAILURE;
+ }
+ }
+ else
+#endif
+ { // The old fashioned way for Mac OS 8.x/9.x
+ rv = FindRunningAppByFSSpec(appSpec, thePSN);
+ running = NS_SUCCEEDED(rv);
+
+ err = AECreateDesc(typeProcessSerialNumber, &thePSN, sizeof(thePSN), &target);
+ if (err != noErr) return NS_ERROR_FAILURE;
+
+ err = AECreateAppleEvent(kCoreEventClass, aDocToLoad ? kAEOpenDocuments : kAEOpenApplication, &target,
+ kAutoGenerateReturnID, kAnyTransactionID, &theEvent);
+ if (err != noErr) return NS_ERROR_FAILURE;
+
+ if (aDocToLoad)
+ {
+ err = AECreateList(nil, 0, false, &docList);
+ if (err != noErr) return NS_ERROR_FAILURE;
+
+ err = AECreateDesc(typeFSS, aDocToLoad, sizeof(FSSpec), &docDesc);
+ if (err != noErr) return NS_ERROR_FAILURE;
+
+ err = AEPutDesc(&docList, 0, &docDesc);
+ if (err != noErr) return NS_ERROR_FAILURE;
+
+ err = AEPutParamDesc(&theEvent, keyDirectObject, &docList);
+ if (err != noErr) return NS_ERROR_FAILURE;
+ }
+
+ if (running)
+ {
+ err = AESend(&theEvent, &theReply, kAENoReply, kAENormalPriority, kNoTimeOut, nil, nil);
+ if (err != noErr) return NS_ERROR_FAILURE;
+
+ if (!aLaunchInBackground)
+ {
+ err = ::SetFrontProcess(&thePSN);
+ if (err != noErr) return NS_ERROR_FAILURE;
+ }
+ }
+ else
+ {
+ LaunchParamBlockRec launchThis = {0};
+ PRUint16 launchControlFlags = (launchContinue | launchNoFileFlags);
+ if (aLaunchInBackground)
+ launchControlFlags |= launchDontSwitch;
+
+ err = AECoerceDesc(&theEvent, typeAppParameters, &launchDesc);
+ if (err != noErr) return NS_ERROR_FAILURE;
+
+ launchThis.launchAppSpec = (FSSpecPtr)&appSpec;
+#if TARGET_CARBON && ACCESSOR_CALLS_ARE_FUNCTIONS
+ ::AEGetDescData(&launchDesc, &launchThis.launchAppParameters, sizeof(launchThis.launchAppParameters));
+#else
+ // no need to lock this handle.
+ launchThis.launchAppParameters = (AppParametersPtr) *(launchDesc.dataHandle);
+#endif
+ launchThis.launchBlockID = extendedBlock;
+ launchThis.launchEPBLength = extendedBlockLen;
+ launchThis.launchFileFlags = 0;
+ launchThis.launchControlFlags = launchControlFlags;
+ err = ::LaunchApplication(&launchThis);
+ if (err != noErr) return NS_ERROR_FAILURE;
+
+ // let's be nice and wait until it's running
+ const PRUint32 kMaxTimeToWait = 60; // wait 1 sec max
+ PRUint32 endTicks = ::TickCount() + kMaxTimeToWait;
+
+ PRBool foundApp = PR_FALSE;
+
+ do
+ {
+ EventRecord theEvent;
+ (void)WaitNextEvent(nullEvent, &theEvent, 1, NULL);
+
+ ProcessSerialNumber psn;
+ foundApp = NS_SUCCEEDED(FindRunningAppByFSSpec(appSpec, psn));
+
+ } while (!foundApp && (::TickCount() <= endTicks));
+
+ NS_ASSERTION(foundApp, "Failed to find app after launching it");
+ }
+
+ if (theEvent.dataHandle != nil) AEDisposeDesc(&theEvent);
+ if (theReply.dataHandle != nil) AEDisposeDesc(&theReply);
+ }
+
+ return NS_OK;
+}
+
+
+#pragma mark -
+#pragma mark [Methods that will not be implemented on Mac]
+
+NS_IMETHODIMP
+nsLocalFile::Normalize()
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsLocalFile::GetPermissions(PRUint32 *aPermissions)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsLocalFile::GetPermissionsOfLink(PRUint32 *aPermissionsOfLink)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+
+NS_IMETHODIMP
+nsLocalFile::SetPermissions(PRUint32 aPermissions)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsLocalFile::SetPermissionsOfLink(PRUint32 aPermissions)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsLocalFile::IsSpecial(PRBool *_retval)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+#pragma mark -
+#pragma mark [nsILocalFileMac]
+// Implementation of Mac specific finctions from nsILocalFileMac
+
+
+NS_IMETHODIMP nsLocalFile::InitWithCFURL(CFURLRef aCFURL)
+{
+ nsresult rv = NS_ERROR_FAILURE;
+
+#if TARGET_CARBON
+ NS_ENSURE_ARG(aCFURL);
+
+ // CFURLGetFSRef can only succeed if the entire path exists.
+ FSRef fsRef;
+ if (::CFURLGetFSRef(aCFURL, &fsRef) == PR_TRUE)
+ rv = InitWithFSRef(&fsRef);
+ else
+ {
+ CFURLRef parentURL = ::CFURLCreateCopyDeletingLastPathComponent(NULL, aCFURL);
+ if (!parentURL)
+ return NS_ERROR_FAILURE;
+
+ // Get the FSRef from the parent and the FSSpec from that
+ FSRef parentFSRef;
+ FSSpec parentFSSpec;
+ if ((::CFURLGetFSRef(parentURL, &parentFSRef) == PR_TRUE) &&
+ (::FSGetCatalogInfo(&parentFSRef, kFSCatInfoNone,
+ nsnull, nsnull, &parentFSSpec, nsnull) == noErr))
+ {
+ // Get the leaf name of the file and turn it into a string HFS can use.
+ CFStringRef fileNameRef = ::CFURLCopyLastPathComponent(aCFURL);
+ if (fileNameRef)
+ {
+ TextEncoding theEncoding;
+ if (::UpgradeScriptInfoToTextEncoding(smSystemScript,
+ kTextLanguageDontCare,
+ kTextRegionDontCare,
+ NULL,
+ &theEncoding) != noErr)
+ theEncoding = kTextEncodingMacRoman;
+
+ char origName[256];
+ char truncBuf[32];
+ if (::CFStringGetCString(fileNameRef, origName, sizeof(origName), theEncoding))
+ {
+ MakeDirty();
+ mSpec = parentFSSpec;
+ mAppendedPath = NS_TruncNodeName(origName, truncBuf);
+ rv = NS_OK;
+ }
+ ::CFRelease(fileNameRef);
+ }
+ }
+ ::CFRelease(parentURL);
+ }
+#endif
+
+ return rv;
+}
+
+
+NS_IMETHODIMP nsLocalFile::InitWithFSRef(const FSRef * aFSRef)
+{
+ nsresult rv = NS_ERROR_FAILURE;
+
+#if TARGET_CARBON
+ NS_ENSURE_ARG(aFSRef);
+
+ FSSpec fsSpec;
+ OSErr err = ::FSGetCatalogInfo(aFSRef, kFSCatInfoNone, nsnull,
+ nsnull, &fsSpec, nsnull);
+ if (err == noErr)
+ rv = InitWithFSSpec(&fsSpec);
+ else
+ rv = MacErrorMapper(err);
+#endif
+
+ return rv;
+}
+
+
+NS_IMETHODIMP nsLocalFile::InitWithFSSpec(const FSSpec *fileSpec)
+{
+ MakeDirty();
+ mSpec = *fileSpec;
+ mTargetSpec = *fileSpec;
+ mAppendedPath = "";
+ return NS_OK;
+}
+
+
+NS_IMETHODIMP nsLocalFile::InitToAppWithCreatorCode(OSType aAppCreator)
+{
+ FSSpec appSpec;
+ ProcessSerialNumber psn;
+
+#if TARGET_CARBON
+ if (sRunningOSX)
+ { // If we're running under OS X use LaunchServices to determine the app
+ // corresponding to the creator code
+ if ( (UInt32)LSFindApplicationForInfo != (UInt32)kUnresolvedCFragSymbolAddress )
+ {
+ FSRef theRef;
+ if (::LSFindApplicationForInfo(aAppCreator, NULL, NULL, &theRef, NULL) == noErr)
+ {
+ FSCatalogInfoBitmap whichInfo = kFSCatInfoNone;
+
+ if (::FSGetCatalogInfo(&theRef, whichInfo, NULL, NULL, &appSpec, NULL) == noErr)
+ return InitWithFSSpec(&appSpec);
+ }
+
+ // If we get here we didn't find an app
+ return NS_ERROR_FILE_NOT_FOUND;
+ }
+ }
+#endif
+
+ // is the app running?
+ nsresult rv = FindRunningAppBySignature(aAppCreator, appSpec, psn);
+ if (rv == NS_ERROR_FILE_NOT_FOUND)
+ {
+ // we have to look on disk
+ rv = FindAppOnLocalVolumes(aAppCreator, appSpec);
+ if (NS_FAILED(rv)) return rv;
+ }
+ else if (NS_FAILED(rv))
+ return rv;
+
+ // init with the spec here
+ return InitWithFSSpec(&appSpec);
+}
+
+NS_IMETHODIMP nsLocalFile::GetCFURL(CFURLRef *_retval)
+{
+ nsresult rv = NS_ERROR_FAILURE;
+
+#if TARGET_CARBON
+ NS_ENSURE_ARG_POINTER(_retval);
+ *_retval = nsnull;
+
+ PRBool exists;
+ if (NS_SUCCEEDED(Exists(&exists)) && exists)
+ {
+ FSRef fsRef;
+ FSSpec fsSpec = mFollowLinks ? mTargetSpec : mSpec;
+ if (::FSpMakeFSRef(&fsSpec, &fsRef) == noErr)
+ {
+ *_retval = ::CFURLCreateFromFSRef(NULL, &fsRef);
+ if (*_retval)
+ return NS_OK;
+ }
+ }
+ else
+ {
+ nsCAutoString tempPath;
+ if (NS_SUCCEEDED(GetNativePath(tempPath)))
+ {
+ CFStringRef pathStrRef = ::CFStringCreateWithCString(NULL, tempPath.get(), kCFStringEncodingMacRoman);
+ if (!pathStrRef)
+ return NS_ERROR_FAILURE;
+ *_retval = ::CFURLCreateWithFileSystemPath(NULL, pathStrRef, kCFURLHFSPathStyle, false);
+ ::CFRelease(pathStrRef);
+ if (*_retval)
+ return NS_OK;
+ }
+ }
+#endif
+
+ return rv;
+}
+
+NS_IMETHODIMP nsLocalFile::GetFSRef(FSRef *_retval)
+{
+ nsresult rv = NS_ERROR_FAILURE;
+
+#if TARGET_CARBON
+ NS_ENSURE_ARG_POINTER(_retval);
+
+ FSSpec fsSpec;
+ rv = GetFSSpec(&fsSpec);
+ if (NS_SUCCEEDED(rv))
+ rv = MacErrorMapper(::FSpMakeFSRef(&fsSpec, _retval));
+#endif
+
+ return rv;
+}
+
+NS_IMETHODIMP nsLocalFile::GetFSSpec(FSSpec *fileSpec)
+{
+ NS_ENSURE_ARG(fileSpec);
+ nsresult rv = ResolveAndStat();
+ if (rv == NS_ERROR_FILE_NOT_FOUND)
+ rv = NS_OK;
+ if (NS_SUCCEEDED(rv))
+ *fileSpec = mFollowLinks ? mTargetSpec : mSpec;
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsLocalFile::GetFileType(OSType *aFileType)
+{
+ NS_ENSURE_ARG(aFileType);
+
+ FSSpec fileSpec;
+ (void)GetFSSpec(&fileSpec);
+
+ FInfo info;
+ OSErr err = ::FSpGetFInfo(&fileSpec, &info);
+ if (err != noErr)
+ {
+ *aFileType = mType;
+ return NS_ERROR_FILE_NOT_FOUND;
+ }
+
+ *aFileType = info.fdType;
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsLocalFile::SetFileType(OSType aFileType)
+{
+ mType = aFileType;
+
+ FSSpec fileSpec;
+ (void)GetFSSpec(&fileSpec);
+
+ FInfo info;
+ OSErr err = ::FSpGetFInfo(&fileSpec, &info);
+ if (err != noErr)
+ return NS_ERROR_FILE_NOT_FOUND;
+
+ info.fdType = aFileType;
+ err = ::FSpSetFInfo(&fileSpec, &info);
+ if (err != noErr)
+ return NS_ERROR_FILE_ACCESS_DENIED;
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsLocalFile::GetFileCreator(OSType *aCreator)
+{
+ NS_ENSURE_ARG(aCreator);
+
+ FSSpec fileSpec;
+ (void)GetFSSpec(&fileSpec);
+
+ FInfo info;
+ OSErr err = ::FSpGetFInfo(&fileSpec, &info);
+ if (err != noErr)
+ {
+ *aCreator = mCreator;
+ return NS_ERROR_FILE_NOT_FOUND;
+ }
+
+ *aCreator = info.fdCreator;
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsLocalFile::SetFileCreator(OSType aCreator)
+{
+ if (aCreator == CURRENT_PROCESS_CREATOR)
+ aCreator = sCurrentProcessSignature;
+
+ mCreator = aCreator;
+
+ FSSpec fileSpec;
+ (void)GetFSSpec(&fileSpec);
+
+ FInfo info;
+ OSErr err = ::FSpGetFInfo(&fileSpec, &info);
+ if (err != noErr)
+ return NS_ERROR_FILE_NOT_FOUND;
+
+ info.fdCreator = aCreator;
+ err = ::FSpSetFInfo(&fileSpec, &info);
+ if (err != noErr)
+ return NS_ERROR_FILE_ACCESS_DENIED;
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsLocalFile::SetFileTypeAndCreatorFromExtension(const char *aExtension)
+{
+ NS_ENSURE_ARG(aExtension);
+ return SetOSTypeAndCreatorFromExtension(aExtension);
+}
+
+NS_IMETHODIMP nsLocalFile::SetFileTypeAndCreatorFromMIMEType(const char *aMIMEType)
+{
+ NS_ENSURE_ARG(aMIMEType);
+
+ nsresult rv;
+ nsCOMPtr<nsIInternetConfigService> icService(do_GetService
+ (NS_INTERNETCONFIGSERVICE_CONTRACTID, &rv));
+
+ if (NS_SUCCEEDED(rv))
+ {
+ nsCOMPtr<nsIMIMEInfo> mimeInfo;
+ PRUint32 fileType = 'TEXT';
+ PRUint32 fileCreator = nsILocalFileMac::CURRENT_PROCESS_CREATOR;
+
+ rv = icService->FillInMIMEInfo(aMIMEType,
+ nsnull, getter_AddRefs(mimeInfo));
+ if (NS_SUCCEEDED(rv))
+ rv = mimeInfo->GetMacType(&fileType);
+ if (NS_SUCCEEDED(rv))
+ rv = mimeInfo->GetMacCreator(&fileCreator);
+ if (NS_SUCCEEDED(rv))
+ rv = SetFileType(fileType);
+ if (NS_SUCCEEDED(rv))
+ rv = SetFileCreator(fileCreator);
+ }
+
+ return rv;
+}
+
+NS_IMETHODIMP
+nsLocalFile::GetFileSizeWithResFork(PRInt64 *aFileSize)
+{
+ NS_ENSURE_ARG(aFileSize);
+
+ *aFileSize = LL_Zero();
+
+ ResolveAndStat();
+
+ long dataSize = 0;
+ long resSize = 0;
+
+ OSErr err = FSpGetFileSize(&mTargetSpec, &dataSize, &resSize);
+
+ if (err != noErr)
+ return MacErrorMapper(err);
+
+ // For now we've only got 32 bits of file size info
+ PRInt64 dataInt64 = LL_Zero();
+ PRInt64 resInt64 = LL_Zero();
+
+ // Combine the size of the resource and data forks
+ LL_I2L(resInt64, resSize);
+ LL_I2L(dataInt64, dataSize);
+ LL_ADD((*aFileSize), dataInt64, resInt64);
+
+ return NS_OK;
+}
+
+
+// this nsLocalFile points to the app. We want to launch it, optionally with the document.
+NS_IMETHODIMP
+nsLocalFile::LaunchWithDoc(nsILocalFile* aDocToLoad, PRBool aLaunchInBackground)
+{
+ // are we launchable?
+ PRBool isExecutable;
+ nsresult rv = IsExecutable(&isExecutable);
+ if (NS_FAILED(rv)) return rv;
+ if (!isExecutable) return NS_ERROR_FILE_EXECUTION_FAILED;
+
+ FSSpec docSpec;
+ FSSpecPtr docSpecPtr = nsnull;
+
+ nsCOMPtr<nsILocalFileMac> macDoc = do_QueryInterface(aDocToLoad);
+ if (macDoc)
+ {
+ rv = macDoc->GetFSSpec(&docSpec); // XXX GetTargetFSSpec
+ if (NS_FAILED(rv)) return rv;
+
+ docSpecPtr = &docSpec;
+ }
+
+ FSSpec appSpec;
+ rv = GetFSSpec(&appSpec); // XXX GetResolvedFSSpec
+ if (NS_FAILED(rv)) return rv;
+
+ rv = MyLaunchAppWithDoc(appSpec, docSpecPtr, aLaunchInBackground);
+ return rv;
+}
+
+
+NS_IMETHODIMP
+nsLocalFile::OpenDocWithApp(nsILocalFile* aAppToOpenWith, PRBool aLaunchInBackground)
+{
+ // if aAppToOpenWith is nil, we have to find the app from the creator code
+ // of the document
+ nsresult rv = NS_OK;
+
+ FSSpec appSpec;
+
+ if (aAppToOpenWith)
+ {
+ nsCOMPtr<nsILocalFileMac> appFileMac = do_QueryInterface(aAppToOpenWith, &rv);
+ if (!appFileMac) return rv;
+
+ rv = appFileMac->GetFSSpec(&appSpec); // XXX GetTargetFSSpec
+ if (NS_FAILED(rv)) return rv;
+
+ // is it launchable?
+ PRBool isExecutable;
+ rv = aAppToOpenWith->IsExecutable(&isExecutable);
+ if (NS_FAILED(rv)) return rv;
+ if (!isExecutable) return NS_ERROR_FILE_EXECUTION_FAILED;
+ }
+ else
+ {
+ // look for one
+ OSType fileCreator;
+ rv = GetFileCreator(&fileCreator);
+ if (NS_FAILED(rv)) return rv;
+
+ // just make one on the stack
+ nsLocalFile localAppFile;
+ rv = localAppFile.InitToAppWithCreatorCode(fileCreator);
+ if (NS_FAILED(rv)) return rv;
+
+ rv = localAppFile.GetFSSpec(&appSpec); // GetTargetFSSpec
+ if (NS_FAILED(rv)) return rv;
+ }
+
+ FSSpec docSpec;
+ rv = GetFSSpec(&docSpec); // XXX GetResolvedFSSpec
+ if (NS_FAILED(rv)) return rv;
+
+ rv = MyLaunchAppWithDoc(appSpec, &docSpec, aLaunchInBackground);
+ return rv;
+}
+
+nsresult nsLocalFile::SetOSTypeAndCreatorFromExtension(const char* extension)
+{
+ nsresult rv;
+
+ nsCAutoString localExtBuf;
+ const char *extPtr;
+
+ if (!extension)
+ {
+ rv = GetNativeLeafName(localExtBuf);
+ extPtr = strrchr(localExtBuf.get(), '.');
+ if (!extPtr)
+ return NS_ERROR_FAILURE;
+ ++extPtr;
+ }
+ else
+ {
+ extPtr = extension;
+ if (*extPtr == '.')
+ ++extPtr;
+ }
+
+ nsCOMPtr<nsIInternetConfigService> icService =
+ do_GetService(NS_INTERNETCONFIGSERVICE_CONTRACTID, &rv);
+ if (NS_SUCCEEDED(rv))
+ {
+ nsCOMPtr<nsIMIMEInfo> mimeInfo;
+ rv = icService->GetMIMEInfoFromExtension(extPtr, getter_AddRefs(mimeInfo));
+ if (NS_SUCCEEDED(rv))
+ {
+ PRUint32 osType;
+ rv = mimeInfo->GetMacType(&osType);
+ if (NS_SUCCEEDED(rv))
+ mType = osType;
+ PRBool skip;
+ rv = ExtensionIsOnExceptionList(extPtr, &skip);
+ if (NS_SUCCEEDED(rv) && !skip)
+ {
+ rv = mimeInfo->GetMacCreator(&osType);
+ if (NS_SUCCEEDED(rv))
+ mCreator = osType;
+ }
+ }
+ }
+ return rv;
+}
+
+nsresult nsLocalFile::ExtensionIsOnExceptionList(const char *extension, PRBool *onList)
+{
+ // Probably want to make a global list somewhere in the future
+ // for now, just check for "html" and "htm"
+
+ *onList = PR_FALSE;
+
+ if (!nsCRT::strcasecmp(extension, "html") ||
+ !nsCRT::strcasecmp(extension, "htm"))
+ *onList = PR_TRUE;
+ return NS_OK;
+}
+
+
+void nsLocalFile::InitClassStatics()
+{
+ OSErr err;
+
+
+ if (sCurrentProcessSignature == 0)
+ {
+ ProcessSerialNumber psn;
+ ProcessInfoRec info;
+
+ psn.highLongOfPSN = 0;
+ psn.lowLongOfPSN = kCurrentProcess;
+
+ info.processInfoLength = sizeof(ProcessInfoRec);
+ info.processName = nil;
+ info.processAppSpec = nil;
+ err = ::GetProcessInformation(&psn, &info);
+ if (err == noErr)
+ sCurrentProcessSignature = info.processSignature;
+ // Try again next time if error
+ }
+
+ static PRBool didHFSPlusCheck = PR_FALSE;
+ if (!didHFSPlusCheck)
+ {
+ long response;
+ err = ::Gestalt(gestaltFSAttr, &response);
+ sHasHFSPlusAPIs = (err == noErr && (response & (1 << gestaltHasHFSPlusAPIs)) != 0);
+ didHFSPlusCheck = PR_TRUE;
+ }
+
+ static PRBool didOSXCheck = PR_FALSE;
+ if (!didOSXCheck)
+ {
+ long version;
+ sRunningOSX = (::Gestalt(gestaltSystemVersion, &version) == noErr && version >= 0x00001000);
+ didOSXCheck = PR_TRUE;
+ }
+}
+
+
+#pragma mark -
+
+// Handy dandy utility create routine for something or the other
+nsresult
+NS_NewNativeLocalFile(const nsACString &path, PRBool followLinks, nsILocalFile* *result)
+{
+ nsLocalFile* file = new nsLocalFile();
+ if (file == nsnull)
+ return NS_ERROR_OUT_OF_MEMORY;
+ NS_ADDREF(file);
+
+ file->SetFollowLinks(followLinks);
+
+ if (!path.IsEmpty()) {
+ nsresult rv = file->InitWithNativePath(path);
+ if (NS_FAILED(rv)) {
+ NS_RELEASE(file);
+ return rv;
+ }
+ }
+ *result = file;
+ return NS_OK;
+}
+
+nsresult
+NS_NewLocalFile(const nsAString &path, PRBool followLinks, nsILocalFile* *result)
+{
+ nsCAutoString fsCharSetStr;
+ nsresult rv = NS_CopyUnicodeToNative(path, fsCharSetStr);
+ if (NS_FAILED(rv))
+ return rv;
+ return NS_NewNativeLocalFile(fsCharSetStr, followLinks, result);
+}
+
+nsresult
+NS_NewLocalFileWithFSSpec(const FSSpec* inSpec, PRBool followLinks, nsILocalFileMac* *result)
+{
+ nsLocalFile* file = new nsLocalFile();
+ if (file == nsnull)
+ return NS_ERROR_OUT_OF_MEMORY;
+ NS_ADDREF(file);
+
+ file->SetFollowLinks(followLinks);
+
+ nsresult rv = file->InitWithFSSpec(inSpec);
+ if (NS_FAILED(rv)) {
+ NS_RELEASE(file);
+ return rv;
+ }
+ *result = file;
+ return NS_OK;
+}
+
+void
+nsLocalFile::GlobalInit()
+{
+}
+
+void
+nsLocalFile::GlobalShutdown()
+{
+}
diff --git a/src/libs/xpcom18a4/xpcom/io/nsLocalFileMac.h b/src/libs/xpcom18a4/xpcom/io/nsLocalFileMac.h
new file mode 100644
index 00000000..42eb2556
--- /dev/null
+++ b/src/libs/xpcom18a4/xpcom/io/nsLocalFileMac.h
@@ -0,0 +1,137 @@
+/* -*- 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 client code, released
+ * March 31, 1998.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1998-1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Steve Dagley <sdagley@netscape.com>
+ *
+ * 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 ***** */
+
+#ifndef _nsLocalFileMAC_H_
+#define _nsLocalFileMAC_H_
+
+#include "nscore.h"
+#include "nsError.h"
+#include "nsString.h"
+#include "nsCRT.h"
+#include "nsIFile.h"
+#include "nsILocalFileMac.h"
+#include "nsIFactory.h"
+
+#include <Processes.h>
+
+class NS_COM nsLocalFile : public nsILocalFileMac
+{
+public:
+ NS_DEFINE_STATIC_CID_ACCESSOR(NS_LOCAL_FILE_CID)
+
+ nsLocalFile();
+
+ static NS_METHOD nsLocalFileConstructor(nsISupports* outer, const nsIID& aIID, void* *aInstancePtr);
+
+ // nsISupports interface
+ NS_DECL_ISUPPORTS
+
+ // nsIFile interface
+ NS_DECL_NSIFILE
+
+ // nsILocalFile interface
+ NS_DECL_NSILOCALFILE
+
+ // nsILocalFileMac interface
+ NS_DECL_NSILOCALFILEMAC
+
+public:
+
+ static void GlobalInit();
+ static void GlobalShutdown();
+
+private:
+ ~nsLocalFile() {}
+
+protected:
+ void MakeDirty();
+ nsresult ResolveAndStat();
+ nsresult UpdateCachedCatInfo(PRBool forceUpdate);
+
+ nsresult FindAppOnLocalVolumes(OSType sig, FSSpec &outSpec);
+
+ nsresult FindRunningAppBySignature(OSType sig, FSSpec& outSpec, ProcessSerialNumber& outPsn);
+ nsresult FindRunningAppByFSSpec(const FSSpec& appSpec, ProcessSerialNumber& outPsn);
+
+ nsresult MyLaunchAppWithDoc(const FSSpec& appSpec, const FSSpec* aDocToLoad, PRBool aLaunchInBackground);
+
+ nsresult TestFinderFlag(PRUint16 flagMask, PRBool *outFlagSet, PRBool testTargetSpec = PR_TRUE);
+
+ nsresult MoveCopy( nsIFile* newParentDir, const nsACString &newName, PRBool isCopy, PRBool followLinks );
+
+ // Passing nsnull for the extension uses leaf name
+ nsresult SetOSTypeAndCreatorFromExtension(const char* extension = nsnull);
+
+ nsresult ExtensionIsOnExceptionList(const char *extension, PRBool *onList);
+
+private:
+ nsLocalFile(const nsLocalFile& srcFile);
+ nsLocalFile(const FSSpec& aSpec, const nsACString& aAppendedPath);
+
+ // Copies all members except mRefCnt, copies mCachedCatInfo only if it's valid.
+ nsLocalFile& operator=(const nsLocalFile& rhs);
+
+ PRPackedBool mFollowLinks;
+ PRPackedBool mFollowLinksDirty;
+
+ // The object we specify is always: mSpec + mAppendedPath.
+ // mSpec is a valid spec in that its parent is known to exist.
+ // Appending nodes with Append() will update mSpec immediately
+ // if the appended node exists. If not, appended nodes are stored
+ // in mAppendedPath.
+
+ PRPackedBool mSpecDirty;
+ FSSpec mSpec;
+ nsCString mAppendedPath;
+ FSSpec mTargetSpec; // If mSpec is an alias, this is the resolved alias.
+
+ CInfoPBRec mCachedCatInfo; // cached file info, for the GetFSSpec() spec
+ PRPackedBool mCatInfoDirty; // does this need to be updated?
+
+ OSType mType, mCreator;
+
+ static void InitClassStatics();
+
+ static OSType sCurrentProcessSignature;
+ static PRBool sHasHFSPlusAPIs;
+ static PRBool sRunningOSX;
+};
+
+#endif
+
diff --git a/src/libs/xpcom18a4/xpcom/io/nsLocalFileOS2.cpp b/src/libs/xpcom18a4/xpcom/io/nsLocalFileOS2.cpp
new file mode 100644
index 00000000..269a13d0
--- /dev/null
+++ b/src/libs/xpcom18a4/xpcom/io/nsLocalFileOS2.cpp
@@ -0,0 +1,1742 @@
+/* -*- 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, released
+ * March 31, 1998.
+ *
+ * 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):
+ * Henry Sobotka <sobotka@axess.com>
+ * IBM Corp.
+ *
+ * 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 "nsCOMPtr.h"
+#include "nsMemory.h"
+
+#include "nsLocalFile.h"
+#include "nsNativeCharsetUtils.h"
+
+#include "nsISimpleEnumerator.h"
+#include "nsIComponentManager.h"
+#include "prtypes.h"
+#include "prio.h"
+
+#include <ctype.h> // needed for toupper
+#include <string.h>
+
+#include "nsXPIDLString.h"
+#include "nsReadableUtils.h"
+#include "prproces.h"
+#include "prthread.h"
+
+
+static unsigned char* PR_CALLBACK
+_mbschr( const unsigned char* stringToSearch, int charToSearchFor);
+extern unsigned char*
+_mbsrchr( const unsigned char* stringToSearch, int charToSearchFor);
+static nsresult PR_CALLBACK
+CreateDirectoryA( PSZ path, PEAOP2 ppEABuf);
+static int isleadbyte(int c);
+
+#include <unistd.h>
+#include <io.h>
+
+
+static nsresult ConvertOS2Error(int err)
+{
+ nsresult rv;
+
+ switch (err)
+ {
+ case ERROR_FILE_NOT_FOUND:
+ case ERROR_PATH_NOT_FOUND:
+ case ERROR_INVALID_DRIVE:
+ rv = NS_ERROR_FILE_NOT_FOUND;
+ break;
+ case ERROR_ACCESS_DENIED:
+ case ERROR_NOT_SAME_DEVICE:
+ rv = NS_ERROR_FILE_ACCESS_DENIED;
+ break;
+ case ERROR_NOT_ENOUGH_MEMORY:
+ case ERROR_INVALID_BLOCK:
+ case ERROR_INVALID_HANDLE:
+ case ERROR_ARENA_TRASHED:
+ rv = NS_ERROR_OUT_OF_MEMORY;
+ break;
+ case ERROR_CURRENT_DIRECTORY:
+ rv = NS_ERROR_FILE_DIR_NOT_EMPTY;
+ break;
+ case ERROR_WRITE_PROTECT:
+ rv = NS_ERROR_FILE_READ_ONLY;
+ break;
+ case ERROR_HANDLE_DISK_FULL:
+ rv = NS_ERROR_FILE_TOO_BIG;
+ break;
+ case ERROR_FILE_EXISTS:
+ case ERROR_ALREADY_EXISTS:
+ case ERROR_CANNOT_MAKE:
+ rv = NS_ERROR_FILE_ALREADY_EXISTS;
+ break;
+ case 0:
+ rv = NS_OK;
+ break;
+ default:
+ rv = NS_ERROR_FAILURE;
+ break;
+ }
+ return rv;
+}
+
+static void
+myLL_L2II(PRInt64 result, PRInt32 *hi, PRInt32 *lo )
+{
+ PRInt64 a64, b64; // probably could have been done with
+ // only one PRInt64, but these are macros,
+ // and I am a wimp.
+
+ // shift the hi word to the low word, then push it into a long.
+ LL_SHR(a64, result, 32);
+ LL_L2I(*hi, a64);
+
+ // shift the low word to the hi word first, then shift it back.
+ LL_SHL(b64, result, 32);
+ LL_SHR(a64, b64, 32);
+ LL_L2I(*lo, a64);
+}
+
+
+class nsDirEnumerator : public nsISimpleEnumerator
+{
+ public:
+
+ NS_DECL_ISUPPORTS
+
+ nsDirEnumerator() : mDir(nsnull)
+ {
+ }
+
+ nsresult Init(nsILocalFile* parent)
+ {
+ nsCAutoString filepath;
+ parent->GetNativeTarget(filepath);
+
+ if (filepath.IsEmpty())
+ {
+ parent->GetNativePath(filepath);
+ }
+
+ if (filepath.IsEmpty())
+ {
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ mDir = PR_OpenDir(filepath.get());
+ if (mDir == nsnull) // not a directory?
+ return NS_ERROR_FAILURE;
+
+ mParent = parent;
+ return NS_OK;
+ }
+
+ NS_IMETHOD HasMoreElements(PRBool *result)
+ {
+ nsresult rv;
+ if (mNext == nsnull && mDir)
+ {
+ PRDirEntry* entry = PR_ReadDir(mDir, PR_SKIP_BOTH);
+ if (entry == nsnull)
+ {
+ // end of dir entries
+
+ PRStatus status = PR_CloseDir(mDir);
+ if (status != PR_SUCCESS)
+ return NS_ERROR_FAILURE;
+ mDir = nsnull;
+
+ *result = PR_FALSE;
+ return NS_OK;
+ }
+
+ nsCOMPtr<nsIFile> file;
+ rv = mParent->Clone(getter_AddRefs(file));
+ if (NS_FAILED(rv))
+ return rv;
+
+ rv = file->AppendNative(nsDependentCString(entry->name));
+ if (NS_FAILED(rv))
+ return rv;
+
+ // make sure the thing exists. If it does, try the next one.
+ PRBool exists;
+ rv = file->Exists(&exists);
+ if (NS_FAILED(rv) || !exists)
+ {
+ return HasMoreElements(result);
+ }
+
+ mNext = do_QueryInterface(file);
+ }
+ *result = mNext != nsnull;
+ return NS_OK;
+ }
+
+ NS_IMETHOD GetNext(nsISupports **result)
+ {
+ nsresult rv;
+ PRBool hasMore;
+ rv = HasMoreElements(&hasMore);
+ if (NS_FAILED(rv)) return rv;
+
+ *result = mNext; // might return nsnull
+ NS_IF_ADDREF(*result);
+
+ mNext = nsnull;
+ return NS_OK;
+ }
+
+ private:
+ ~nsDirEnumerator()
+ {
+ if (mDir)
+ {
+ PRStatus status = PR_CloseDir(mDir);
+ NS_ASSERTION(status == PR_SUCCESS, "close failed");
+ }
+ }
+
+ protected:
+ PRDir* mDir;
+ nsCOMPtr<nsILocalFile> mParent;
+ nsCOMPtr<nsILocalFile> mNext;
+};
+
+NS_IMPL_ISUPPORTS1(nsDirEnumerator, nsISimpleEnumerator)
+
+
+nsLocalFile::nsLocalFile()
+{
+ MakeDirty();
+}
+
+nsLocalFile::nsLocalFile(const nsLocalFile& other)
+ : mDirty(other.mDirty)
+ , mWorkingPath(other.mWorkingPath)
+ , mFileInfo64(other.mFileInfo64)
+{
+}
+
+/* nsISupports interface implementation. */
+NS_IMPL_THREADSAFE_ISUPPORTS2(nsLocalFile, nsILocalFile, nsIFile)
+
+NS_METHOD
+nsLocalFile::nsLocalFileConstructor(nsISupports* outer, const nsIID& aIID, void* *aInstancePtr)
+{
+ NS_ENSURE_ARG_POINTER(aInstancePtr);
+ NS_ENSURE_NO_AGGREGATION(outer);
+
+ nsLocalFile* inst = new nsLocalFile();
+ if (inst == NULL)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ nsresult rv = inst->QueryInterface(aIID, aInstancePtr);
+ if (NS_FAILED(rv))
+ {
+ delete inst;
+ return rv;
+ }
+ return NS_OK;
+}
+
+// This function resets any cached information about the file.
+void
+nsLocalFile::MakeDirty()
+{
+ mDirty = PR_TRUE;
+}
+
+nsresult
+nsLocalFile::Stat()
+{
+ if (!mDirty)
+ return NS_OK;
+
+ char temp[4];
+ const char* workingFilePath = mWorkingPath.get();
+ const char* nsprPath = workingFilePath;
+
+ if (mWorkingPath.Length() == 2 && mWorkingPath.CharAt(1) == ':') {
+ temp[0] = workingFilePath[0];
+ temp[1] = workingFilePath[1];
+ temp[2] = '\\';
+ temp[3] = '\0';
+ nsprPath = temp;
+ }
+
+ DosError(FERR_DISABLEHARDERR);
+ PRStatus status = PR_GetFileInfo64(nsprPath, &mFileInfo64);
+ DosError(FERR_ENABLEHARDERR);
+ if ( status == PR_SUCCESS )
+ return NS_OK;
+
+ return NS_ERROR_FILE_NOT_FOUND;
+}
+
+NS_IMETHODIMP
+nsLocalFile::Clone(nsIFile **file)
+{
+ // Just copy-construct ourselves
+ *file = new nsLocalFile(*this);
+ if (!*file)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ NS_ADDREF(*file);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::InitWithNativePath(const nsACString &filePath)
+{
+ MakeDirty();
+
+ nsACString::const_iterator begin, end;
+ filePath.BeginReading(begin);
+ filePath.EndReading(end);
+
+ // input string must not be empty
+ if (begin == end)
+ return NS_ERROR_FAILURE;
+
+ char firstChar = *begin;
+ char secondChar = *(++begin);
+
+ // just do a sanity check. if it has any forward slashes, it is not a Native path
+ // on windows. Also, it must have a colon at after the first char.
+
+ char *path = nsnull;
+ PRInt32 pathLen = 0;
+
+ if ( ( (secondChar == ':') && !FindCharInReadable('/', begin, end) ) || // normal path
+ ( (firstChar == '\\') && (secondChar == '\\') ) ) // network path
+ {
+ // This is a native path
+ path = ToNewCString(filePath);
+ pathLen = filePath.Length();
+ }
+
+ if (path == nsnull)
+ return NS_ERROR_FILE_UNRECOGNIZED_PATH;
+
+ // kill any trailing '\' provided it isn't the second char of DBCS
+ PRInt32 len = pathLen - 1;
+ if (path[len] == '\\' && !::isleadbyte(path[len-1]))
+ {
+ path[len] = '\0';
+ pathLen = len;
+ }
+
+ mWorkingPath.Adopt(path, pathLen);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::OpenNSPRFileDesc(PRInt32 flags, PRInt32 mode, PRFileDesc **_retval)
+{
+ nsresult rv = Stat();
+ if (NS_FAILED(rv) && rv != NS_ERROR_FILE_NOT_FOUND)
+ return rv;
+
+ *_retval = PR_Open(mWorkingPath.get(), flags, mode);
+
+ if (*_retval)
+ return NS_OK;
+
+ return NS_ErrorAccordingToNSPR();
+}
+
+
+NS_IMETHODIMP
+nsLocalFile::OpenANSIFileDesc(const char *mode, FILE * *_retval)
+{
+ nsresult rv = Stat();
+ if (NS_FAILED(rv) && rv != NS_ERROR_FILE_NOT_FOUND)
+ return rv;
+
+ *_retval = fopen(mWorkingPath.get(), mode);
+
+ if (*_retval)
+ return NS_OK;
+
+ return NS_ERROR_FAILURE;
+}
+
+
+
+NS_IMETHODIMP
+nsLocalFile::Create(PRUint32 type, PRUint32 attributes)
+{
+ if (type != NORMAL_FILE_TYPE && type != DIRECTORY_TYPE)
+ return NS_ERROR_FILE_UNKNOWN_TYPE;
+
+ nsresult rv = Stat();
+ if (NS_FAILED(rv) && rv != NS_ERROR_FILE_NOT_FOUND)
+ return rv;
+
+ // create nested directories to target
+ unsigned char* slash = _mbschr((const unsigned char*) mWorkingPath.get(), '\\');
+
+
+ if (slash)
+ {
+ // skip the first '\\'
+ ++slash;
+ slash = _mbschr(slash, '\\');
+
+ while (slash)
+ {
+ *slash = '\0';
+
+ rv = CreateDirectoryA(NS_CONST_CAST(char*, mWorkingPath.get()), NULL);
+ if (rv) {
+ rv = ConvertOS2Error(rv);
+ if (rv != NS_ERROR_FILE_ALREADY_EXISTS) return rv;
+ }
+ *slash = '\\';
+ ++slash;
+ slash = _mbschr(slash, '\\');
+ }
+ }
+
+ if (type == NORMAL_FILE_TYPE)
+ {
+ PRFileDesc* file = PR_Open(mWorkingPath.get(), PR_RDONLY | PR_CREATE_FILE | PR_APPEND | PR_EXCL, attributes);
+ if (!file) return NS_ERROR_FILE_ALREADY_EXISTS;
+
+ PR_Close(file);
+ return NS_OK;
+ }
+
+ if (type == DIRECTORY_TYPE)
+ {
+ rv = CreateDirectoryA(NS_CONST_CAST(char*, mWorkingPath.get()), NULL);
+ if (rv)
+ return ConvertOS2Error(rv);
+ else
+ return NS_OK;
+ }
+
+ return NS_ERROR_FILE_UNKNOWN_TYPE;
+}
+
+NS_IMETHODIMP
+nsLocalFile::AppendNative(const nsACString &node)
+{
+ if (node.IsEmpty())
+ return NS_OK;
+
+ // Append only one component. Check for subdirs.
+ // XXX can we avoid the PromiseFlatCString call?
+ if (_mbschr((const unsigned char*) PromiseFlatCString(node).get(), '\\') != nsnull)
+ return NS_ERROR_FILE_UNRECOGNIZED_PATH;
+
+ return AppendRelativeNativePath(node);
+}
+
+NS_IMETHODIMP
+nsLocalFile::AppendRelativeNativePath(const nsACString &node)
+{
+ // Cannot start with a / or have .. or have / anywhere
+ nsACString::const_iterator begin, end;
+ node.BeginReading(begin);
+ node.EndReading(end);
+ if (node.IsEmpty() || FindCharInReadable('/', begin, end))
+ {
+ return NS_ERROR_FILE_UNRECOGNIZED_PATH;
+ }
+ MakeDirty();
+ mWorkingPath.Append(NS_LITERAL_CSTRING("\\") + node);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::Normalize()
+{
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::GetNativeLeafName(nsACString &aLeafName)
+{
+ aLeafName.Truncate();
+
+ const char* temp = mWorkingPath.get();
+ if(temp == nsnull)
+ return NS_ERROR_FILE_UNRECOGNIZED_PATH;
+
+ const char* leaf = (const char*) _mbsrchr((const unsigned char*) temp, '\\');
+
+ // if the working path is just a node without any lashes.
+ if (leaf == nsnull)
+ leaf = temp;
+ else
+ leaf++;
+
+ aLeafName.Assign(leaf);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::SetNativeLeafName(const nsACString &aLeafName)
+{
+ MakeDirty();
+
+ const unsigned char* temp = (const unsigned char*) mWorkingPath.get();
+ if(temp == nsnull)
+ return NS_ERROR_FILE_UNRECOGNIZED_PATH;
+
+ // cannot use nsCString::RFindChar() due to 0x5c problem
+ PRInt32 offset = (PRInt32) (_mbsrchr(temp, '\\') - temp);
+ if (offset)
+ {
+ mWorkingPath.Truncate(offset+1);
+ }
+ mWorkingPath.Append(aLeafName);
+
+ return NS_OK;
+}
+
+
+NS_IMETHODIMP
+nsLocalFile::GetNativePath(nsACString &_retval)
+{
+ _retval = mWorkingPath;
+ return NS_OK;
+}
+
+nsresult
+nsLocalFile::CopySingleFile(nsIFile *sourceFile, nsIFile *destParent, const nsACString &newName, PRBool move)
+{
+ nsresult rv;
+ nsCAutoString filePath;
+
+ nsCAutoString destPath;
+ destParent->GetNativeTarget(destPath);
+
+ destPath.Append("\\");
+
+ if (newName.IsEmpty())
+ {
+ nsCAutoString aFileName;
+ sourceFile->GetNativeLeafName(aFileName);
+ destPath.Append(aFileName);
+ }
+ else
+ {
+ destPath.Append(newName);
+ }
+
+ rv = sourceFile->GetNativePath(filePath);
+
+ if (NS_FAILED(rv))
+ return rv;
+
+ APIRET rc = NO_ERROR;
+
+ if( move )
+ {
+ rc = DosMove(filePath.get(), (PSZ)NS_CONST_CAST(char*, destPath.get()));
+ }
+
+ if (!move || rc == ERROR_NOT_SAME_DEVICE || rc == ERROR_ACCESS_DENIED) {
+ /* will get an error if the destination and source files aren't on the
+ * same drive. "MoveFile()" on Windows will go ahead and move the
+ * file without error, so we need to do the same IBM-AKR
+ */
+ do {
+ rc = DosCopy(filePath.get(), (PSZ)NS_CONST_CAST(char*, destPath.get()), DCPY_EXISTING);
+ if (rc == ERROR_TOO_MANY_OPEN_FILES) {
+ ULONG CurMaxFH = 0;
+ LONG ReqCount = 20;
+ APIRET rc2;
+ rc2 = DosSetRelMaxFH(&ReqCount, &CurMaxFH);
+ if (rc2 != NO_ERROR) {
+ break;
+ }
+ }
+ } while (rc == ERROR_TOO_MANY_OPEN_FILES);
+
+ /* WSOD2 HACK */
+ if (rc == 65) { // NETWORK_ACCESS_DENIED
+ CHAR achProgram[CCHMAXPATH]; // buffer for program name, parameters
+ RESULTCODES rescResults; // buffer for results of dosexecpgm
+
+ strcpy(achProgram, "CMD.EXE /C ");
+ strcat(achProgram, """COPY ");
+ strcat(achProgram, filePath.get());
+ strcat(achProgram, " ");
+ strcat(achProgram, (PSZ)NS_CONST_CAST(char*, destPath.get()));
+ strcat(achProgram, """");
+ achProgram[strlen(achProgram) + 1] = '\0';
+ achProgram[7] = '\0';
+ DosExecPgm(NULL, 0,
+ EXEC_SYNC, achProgram, (PSZ)NULL,
+ &rescResults, achProgram);
+ rc = 0; // Assume it worked
+
+ } /* rc == 65 */
+ /* moving the file is supposed to act like a rename, so delete the
+ * original file if we got this far without error
+ */
+ if( move && (rc == NO_ERROR) )
+ {
+ DosDelete( filePath.get() );
+ }
+ } /* !move or ERROR */
+
+ if (rc)
+ rv = ConvertOS2Error(rc);
+
+ return rv;
+}
+
+
+nsresult
+nsLocalFile::CopyMove(nsIFile *aParentDir, const nsACString &newName, PRBool move)
+{
+ nsCOMPtr<nsIFile> newParentDir = aParentDir;
+
+ nsresult rv = Stat();
+ if (NS_FAILED(rv))
+ return rv;
+
+ if (!newParentDir)
+ {
+ // no parent was specified. We must rename.
+
+ if (newName.IsEmpty())
+ return NS_ERROR_INVALID_ARG;
+
+ rv = GetParent(getter_AddRefs(newParentDir));
+ if (NS_FAILED(rv))
+ return rv;
+ }
+
+ if (!newParentDir)
+ return NS_ERROR_FILE_DESTINATION_NOT_DIR;
+
+ // make sure it exists and is a directory. Create it if not there.
+ PRBool exists;
+ newParentDir->Exists(&exists);
+ if (exists == PR_FALSE)
+ {
+ rv = newParentDir->Create(DIRECTORY_TYPE, 0644); // TODO, what permissions should we use
+ if (NS_FAILED(rv))
+ return rv;
+ }
+ else
+ {
+ PRBool isDir;
+ newParentDir->IsDirectory(&isDir);
+ if (isDir == PR_FALSE)
+ {
+ return NS_ERROR_FILE_DESTINATION_NOT_DIR;
+ }
+ }
+
+ // check to see if we are a directory, if so enumerate it.
+
+ PRBool isDir;
+ IsDirectory(&isDir);
+
+ if (!isDir)
+ {
+ rv = CopySingleFile(this, newParentDir, newName, move);
+ if (NS_FAILED(rv))
+ return rv;
+ }
+ else
+ {
+ // create a new target destination in the new parentDir;
+ nsCOMPtr<nsIFile> target;
+ rv = newParentDir->Clone(getter_AddRefs(target));
+
+ if (NS_FAILED(rv))
+ return rv;
+
+ nsCAutoString allocatedNewName;
+ if (newName.IsEmpty())
+ {
+ GetNativeLeafName(allocatedNewName);// this should be the leaf name of the
+ }
+ else
+ {
+ allocatedNewName = newName;
+ }
+
+ rv = target->AppendNative(allocatedNewName);
+ if (NS_FAILED(rv))
+ return rv;
+
+ allocatedNewName.Truncate();
+
+ target->Create(DIRECTORY_TYPE, 0644); // TODO, what permissions should we use
+ if (NS_FAILED(rv))
+ return rv;
+
+ nsDirEnumerator* dirEnum = new nsDirEnumerator();
+ if (!dirEnum)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ rv = dirEnum->Init(this);
+
+ nsCOMPtr<nsISimpleEnumerator> iterator = do_QueryInterface(dirEnum);
+
+ PRBool more;
+ iterator->HasMoreElements(&more);
+ while (more)
+ {
+ nsCOMPtr<nsISupports> item;
+ nsCOMPtr<nsIFile> file;
+ iterator->GetNext(getter_AddRefs(item));
+ file = do_QueryInterface(item);
+ PRBool isDir, isLink;
+
+ file->IsDirectory(&isDir);
+
+ if (move)
+ {
+ rv = file->MoveToNative(target, nsCString());
+ }
+ else
+ {
+ rv = file->CopyToNative(target, nsCString());
+ }
+
+ iterator->HasMoreElements(&more);
+ }
+ // we've finished moving all the children of this directory
+ // in the new directory. so now delete the directory
+ // note, we don't need to do a recursive delete.
+ // MoveTo() is recursive. At this point,
+ // we've already moved the children of the current folder
+ // to the new location. nothing should be left in the folder.
+ if (move)
+ {
+ rv = Remove(PR_FALSE);
+ NS_ENSURE_SUCCESS(rv,rv);
+ }
+ }
+
+
+ // If we moved, we want to adjust this.
+ if (move)
+ {
+ MakeDirty();
+
+ nsCAutoString newParentPath;
+ newParentDir->GetNativePath(newParentPath);
+
+ if (newParentPath.IsEmpty())
+ return NS_ERROR_FAILURE;
+
+ if (newName.IsEmpty())
+ {
+ nsCAutoString aFileName;
+ GetNativeLeafName(aFileName);
+
+ InitWithNativePath(newParentPath);
+ AppendNative(aFileName);
+ }
+ else
+ {
+ InitWithNativePath(newParentPath);
+ AppendNative(newName);
+ }
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::CopyToNative(nsIFile *newParentDir, const nsACString &newName)
+{
+ return CopyMove(newParentDir, newName, PR_FALSE);
+}
+
+NS_IMETHODIMP
+nsLocalFile::CopyToFollowingLinksNative(nsIFile *newParentDir, const nsACString &newName)
+{
+ return CopyMove(newParentDir, newName, PR_FALSE);
+}
+
+NS_IMETHODIMP
+nsLocalFile::MoveToNative(nsIFile *newParentDir, const nsACString &newName)
+{
+ return CopyMove(newParentDir, newName, PR_TRUE);
+}
+
+NS_IMETHODIMP
+nsLocalFile::Load(PRLibrary * *_retval)
+{
+ PRBool isFile;
+ nsresult rv = IsFile(&isFile);
+
+ if (NS_FAILED(rv))
+ return rv;
+
+ if (! isFile)
+ return NS_ERROR_FILE_IS_DIRECTORY;
+
+ *_retval = PR_LoadLibrary(mWorkingPath.get());
+
+ if (*_retval)
+ return NS_OK;
+
+ return NS_ERROR_NULL_POINTER;
+}
+
+NS_IMETHODIMP
+nsLocalFile::Remove(PRBool recursive)
+{
+ PRBool isDir;
+
+ nsresult rv = IsDirectory(&isDir);
+ if (NS_FAILED(rv))
+ return rv;
+
+ const char *filePath = mWorkingPath.get();
+
+ if (isDir)
+ {
+ if (recursive)
+ {
+ nsDirEnumerator* dirEnum = new nsDirEnumerator();
+ if (dirEnum == nsnull)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ rv = dirEnum->Init(this);
+
+ nsCOMPtr<nsISimpleEnumerator> iterator = do_QueryInterface(dirEnum);
+
+ PRBool more;
+ iterator->HasMoreElements(&more);
+ while (more)
+ {
+ nsCOMPtr<nsISupports> item;
+ nsCOMPtr<nsIFile> file;
+ iterator->GetNext(getter_AddRefs(item));
+ file = do_QueryInterface(item);
+
+ file->Remove(recursive);
+
+ iterator->HasMoreElements(&more);
+ }
+ }
+ rv = rmdir(filePath) == -1 ? NSRESULT_FOR_ERRNO() : NS_OK;
+ }
+ else
+ {
+ rv = remove(filePath) == -1 ? NSRESULT_FOR_ERRNO() : NS_OK;
+ }
+
+ MakeDirty();
+ return rv;
+}
+
+NS_IMETHODIMP
+nsLocalFile::GetLastModifiedTime(PRInt64 *aLastModifiedTime)
+{
+ NS_ENSURE_ARG(aLastModifiedTime);
+
+ *aLastModifiedTime = 0;
+
+ nsresult rv = Stat();
+
+ if (NS_FAILED(rv))
+ return rv;
+
+ // microseconds -> milliseconds
+ *aLastModifiedTime = mFileInfo64.modifyTime / PR_USEC_PER_MSEC;
+ return NS_OK;
+}
+
+
+NS_IMETHODIMP
+nsLocalFile::GetLastModifiedTimeOfLink(PRInt64 *aLastModifiedTime)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+
+NS_IMETHODIMP
+nsLocalFile::SetLastModifiedTime(PRInt64 aLastModifiedTime)
+{
+ return nsLocalFile::SetModDate(aLastModifiedTime);
+}
+
+
+NS_IMETHODIMP
+nsLocalFile::SetLastModifiedTimeOfLink(PRInt64 aLastModifiedTime)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+nsresult
+nsLocalFile::SetModDate(PRInt64 aLastModifiedTime)
+{
+ nsresult rv = Stat();
+
+ if (NS_FAILED(rv))
+ return rv;
+
+ const char *filePath = mWorkingPath.get();
+
+ PRExplodedTime pret;
+ FILESTATUS3 pathInfo;
+
+ rv = DosQueryPathInfo(filePath,
+ FIL_STANDARD, // Level 1 info
+ &pathInfo,
+ sizeof(pathInfo));
+
+ if (NS_FAILED(rv))
+ {
+ rv = ConvertOS2Error(rv);
+ return rv;
+ }
+
+ // PR_ExplodeTime expects usecs...
+ PR_ExplodeTime(aLastModifiedTime * PR_USEC_PER_MSEC, PR_LocalTimeParameters, &pret);
+ /* fdateLastWrite.year is based off of 1980 */
+ if (pret.tm_year >= 1980)
+ pathInfo.fdateLastWrite.year = pret.tm_year-1980;
+ else
+ pathInfo.fdateLastWrite.year = pret.tm_year;
+ pathInfo.fdateLastWrite.month = pret.tm_month + 1; // Convert start offset -- Win32: Jan=1; NSPR: Jan=0
+// ???? OS2TODO st.wDayOfWeek = pret.tm_wday;
+ pathInfo.fdateLastWrite.day = pret.tm_mday;
+ pathInfo.ftimeLastWrite.hours = pret.tm_hour;
+ pathInfo.ftimeLastWrite.minutes = pret.tm_min;
+ pathInfo.ftimeLastWrite.twosecs = pret.tm_sec / 2; // adjust for twosecs?
+// ??? OS2TODO st.wMilliseconds = pret.tm_usec/1000;
+
+ rv = DosSetPathInfo(filePath,
+ FIL_STANDARD, // Level 1 info
+ &pathInfo,
+ sizeof(pathInfo),
+ 0UL);
+
+ if (NS_FAILED(rv))
+ return rv;
+
+ MakeDirty();
+ return rv;
+}
+
+NS_IMETHODIMP
+nsLocalFile::GetPermissions(PRUint32 *aPermissions)
+{
+ nsresult rv = Stat();
+
+ if (NS_FAILED(rv))
+ return rv;
+
+ const char *filePath = mWorkingPath.get();
+
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::GetPermissionsOfLink(PRUint32 *aPermissionsOfLink)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+
+NS_IMETHODIMP
+nsLocalFile::SetPermissions(PRUint32 aPermissions)
+{
+ nsresult rv = Stat();
+
+ if (NS_FAILED(rv))
+ return rv;
+
+ const char *filePath = mWorkingPath.get();
+ if( chmod(filePath, aPermissions) == -1 )
+ return NS_ERROR_FAILURE;
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::SetPermissionsOfLink(PRUint32 aPermissions)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+
+NS_IMETHODIMP
+nsLocalFile::GetFileSize(PRInt64 *aFileSize)
+{
+ NS_ENSURE_ARG(aFileSize);
+
+ *aFileSize = 0;
+
+ nsresult rv = Stat();
+
+ if (NS_FAILED(rv))
+ return rv;
+
+
+ *aFileSize = mFileInfo64.size;
+ return NS_OK;
+}
+
+
+NS_IMETHODIMP
+nsLocalFile::SetFileSize(PRInt64 aFileSize)
+{
+
+ nsresult rv = Stat();
+
+ if (NS_FAILED(rv))
+ return rv;
+
+ const char *filePath = mWorkingPath.get();
+
+
+ APIRET rc;
+ HFILE hFile;
+ ULONG actionTaken;
+
+ rc = DosOpen(filePath,
+ &hFile,
+ &actionTaken,
+ 0,
+ FILE_NORMAL,
+ OPEN_ACTION_FAIL_IF_NEW | OPEN_ACTION_OPEN_IF_EXISTS,
+ OPEN_SHARE_DENYREADWRITE | OPEN_ACCESS_READWRITE,
+ NULL);
+
+ if (rc != NO_ERROR)
+ {
+ MakeDirty();
+ return NS_ERROR_FAILURE;
+ }
+
+ // Seek to new, desired end of file
+ PRInt32 hi, lo;
+ myLL_L2II(aFileSize, &hi, &lo );
+
+ rc = DosSetFileSize(hFile, lo);
+ if (rc == NO_ERROR)
+ DosClose(hFile);
+ else
+ goto error;
+
+ MakeDirty();
+ return NS_OK;
+
+ error:
+ MakeDirty();
+ DosClose(hFile);
+ return NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+nsLocalFile::GetFileSizeOfLink(PRInt64 *aFileSize)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsLocalFile::GetDiskSpaceAvailable(PRInt64 *aDiskSpaceAvailable)
+{
+ NS_ENSURE_ARG(aDiskSpaceAvailable);
+
+ ULONG ulDriveNo = toupper(mWorkingPath.CharAt(0)) + 1 - 'A';
+ FSALLOCATE fsAllocate;
+ APIRET rc = DosQueryFSInfo(ulDriveNo,
+ FSIL_ALLOC,
+ &fsAllocate,
+ sizeof(fsAllocate));
+
+ if (rc != NO_ERROR)
+ return NS_ERROR_FAILURE;
+
+ *aDiskSpaceAvailable = fsAllocate.cUnitAvail;
+ *aDiskSpaceAvailable *= fsAllocate.cSectorUnit;
+ *aDiskSpaceAvailable *= fsAllocate.cbSector;
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::GetParent(nsIFile * *aParent)
+{
+ NS_ENSURE_ARG_POINTER(aParent);
+
+ nsCAutoString parentPath(mWorkingPath);
+
+ // cannot use nsCString::RFindChar() due to 0x5c problem
+ PRInt32 offset = (PRInt32) (_mbsrchr((const unsigned char *) parentPath.get(), '\\')
+ - (const unsigned char *) parentPath.get());
+ if (offset < 0)
+ return NS_ERROR_FILE_UNRECOGNIZED_PATH;
+
+ parentPath.Truncate(offset);
+
+ nsCOMPtr<nsILocalFile> localFile;
+ nsresult rv = NS_NewNativeLocalFile(parentPath, PR_TRUE, getter_AddRefs(localFile));
+
+ if(NS_SUCCEEDED(rv) && localFile)
+ {
+ return CallQueryInterface(localFile, aParent);
+ }
+ return rv;
+}
+
+NS_IMETHODIMP
+nsLocalFile::Exists(PRBool *_retval)
+{
+ NS_ENSURE_ARG(_retval);
+
+ MakeDirty();
+ *_retval = NS_SUCCEEDED(Stat());
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::IsWritable(PRBool *_retval)
+{
+ NS_ENSURE_ARG(_retval);
+ *_retval = PR_FALSE;
+
+ nsresult rv = Stat();
+
+ if (NS_FAILED(rv))
+ return rv;
+
+ const char *workingFilePath = mWorkingPath.get();
+
+ APIRET rc;
+ FILESTATUS3 pathInfo;
+
+ rc = DosQueryPathInfo(workingFilePath,
+ FIL_STANDARD, // Level 1 info
+ &pathInfo,
+ sizeof(pathInfo));
+
+ if (rc != NO_ERROR)
+ {
+ rc = ConvertOS2Error(rc);
+ return rc;
+ }
+
+ *_retval = !((pathInfo.attrFile & FILE_READONLY) != 0);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::IsReadable(PRBool *_retval)
+{
+ NS_ENSURE_ARG(_retval);
+ *_retval = PR_FALSE;
+
+ nsresult rv = Stat();
+ if (NS_FAILED(rv))
+ return rv;
+
+ *_retval = PR_TRUE;
+ return NS_OK;
+}
+
+
+NS_IMETHODIMP
+nsLocalFile::IsExecutable(PRBool *_retval)
+{
+ NS_ENSURE_ARG(_retval);
+ *_retval = PR_FALSE;
+
+ nsresult rv = Stat();
+ if (NS_FAILED(rv))
+ return rv;
+
+ nsCAutoString path;
+ GetNativeTarget(path);
+
+ const char* leaf = (const char*) _mbsrchr((const unsigned char*) path.get(), '\\');
+
+ if ( (strstr(leaf, ".bat") != nsnull) ||
+ (strstr(leaf, ".exe") != nsnull) ||
+ (strstr(leaf, ".cmd") != nsnull) ||
+ (strstr(leaf, ".com") != nsnull) ) {
+ *_retval = PR_TRUE;
+ } else {
+ *_retval = PR_FALSE;
+ }
+
+ return NS_OK;
+}
+
+
+NS_IMETHODIMP
+nsLocalFile::IsDirectory(PRBool *_retval)
+{
+ NS_ENSURE_ARG(_retval);
+ *_retval = PR_FALSE;
+
+ nsresult rv = Stat();
+
+ if (NS_FAILED(rv))
+ return rv;
+
+ *_retval = (mFileInfo64.type == PR_FILE_DIRECTORY);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::IsFile(PRBool *_retval)
+{
+ NS_ENSURE_ARG(_retval);
+ *_retval = PR_FALSE;
+
+ nsresult rv = Stat();
+
+ if (NS_FAILED(rv))
+ return rv;
+
+ *_retval = (mFileInfo64.type == PR_FILE_FILE);
+ return rv;
+}
+
+NS_IMETHODIMP
+nsLocalFile::IsHidden(PRBool *_retval)
+{
+ NS_ENSURE_ARG(_retval);
+ *_retval = PR_FALSE;
+
+ nsresult rv = Stat();
+
+ if (NS_FAILED(rv))
+ return rv;
+
+ const char *workingFilePath = mWorkingPath.get();
+
+ APIRET rc;
+ FILESTATUS3 pathInfo;
+
+ rc = DosQueryPathInfo(workingFilePath,
+ FIL_STANDARD, // Level 1 info
+ &pathInfo,
+ sizeof(pathInfo));
+
+ if (rc != NO_ERROR)
+ {
+ rc = ConvertOS2Error(rc);
+ return rc;
+ }
+
+ *_retval = ((pathInfo.attrFile & FILE_HIDDEN) != 0);
+
+ return NS_OK;
+}
+
+
+NS_IMETHODIMP
+nsLocalFile::IsSymlink(PRBool *_retval)
+{
+ NS_ENSURE_ARG_POINTER(_retval);
+ // No Symlinks on OS/2
+ *_retval = PR_FALSE;
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::IsSpecial(PRBool *_retval)
+{
+ NS_ENSURE_ARG(_retval);
+ *_retval = PR_FALSE;
+
+ nsresult rv = Stat();
+
+ if (NS_FAILED(rv))
+ return rv;
+
+ const char *workingFilePath = mWorkingPath.get();
+
+ APIRET rc;
+ FILESTATUS3 pathInfo;
+
+ rc = DosQueryPathInfo(workingFilePath,
+ FIL_STANDARD, // Level 1 info
+ &pathInfo,
+ sizeof(pathInfo));
+
+ if (rc != NO_ERROR)
+ {
+ rc = ConvertOS2Error(rc);
+ return rc;
+ }
+
+ *_retval = ((pathInfo.attrFile & FILE_SYSTEM) != 0);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::Equals(nsIFile *inFile, PRBool *_retval)
+{
+ NS_ENSURE_ARG(inFile);
+ NS_ENSURE_ARG(_retval);
+ *_retval = PR_FALSE;
+
+ nsCAutoString inFilePath;
+ inFile->GetNativePath(inFilePath);
+
+ *_retval = inFilePath.Equals(mWorkingPath);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::Contains(nsIFile *inFile, PRBool recur, PRBool *_retval)
+{
+ *_retval = PR_FALSE;
+
+ nsCAutoString myFilePath;
+ if ( NS_FAILED(GetNativeTarget(myFilePath)))
+ GetNativePath(myFilePath);
+
+ PRInt32 myFilePathLen = myFilePath.Length();
+
+ nsCAutoString inFilePath;
+ if ( NS_FAILED(inFile->GetNativeTarget(inFilePath)))
+ inFile->GetNativePath(inFilePath);
+
+ if ( strnicmp( myFilePath.get(), inFilePath.get(), myFilePathLen) == 0)
+ {
+ // now make sure that the |inFile|'s path has a trailing
+ // separator.
+
+ if (inFilePath[myFilePathLen] == '\\')
+ {
+ *_retval = PR_TRUE;
+ }
+
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::GetNativeTarget(nsACString &_retval)
+{
+ _retval = mWorkingPath;
+ return NS_OK;
+}
+
+/* attribute PRBool followLinks; */
+NS_IMETHODIMP
+nsLocalFile::GetFollowLinks(PRBool *aFollowLinks)
+{
+ *aFollowLinks = PR_TRUE;
+ return NS_OK;
+}
+NS_IMETHODIMP
+nsLocalFile::SetFollowLinks(PRBool aFollowLinks)
+{
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::GetDirectoryEntries(nsISimpleEnumerator * *entries)
+{
+ nsresult rv;
+
+ *entries = nsnull;
+
+ PRBool isDir;
+ rv = IsDirectory(&isDir);
+ if (NS_FAILED(rv))
+ return rv;
+ if (!isDir)
+ return NS_ERROR_FILE_NOT_DIRECTORY;
+
+ nsDirEnumerator* dirEnum = new nsDirEnumerator();
+ if (dirEnum == nsnull)
+ return NS_ERROR_OUT_OF_MEMORY;
+ NS_ADDREF(dirEnum);
+ rv = dirEnum->Init(this);
+ if (NS_FAILED(rv))
+ {
+ NS_RELEASE(dirEnum);
+ return rv;
+ }
+
+ *entries = dirEnum;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::GetPersistentDescriptor(nsACString &aPersistentDescriptor)
+{
+ return GetNativePath(aPersistentDescriptor);
+}
+
+NS_IMETHODIMP
+nsLocalFile::SetPersistentDescriptor(const nsACString &aPersistentDescriptor)
+{
+ return InitWithNativePath(aPersistentDescriptor);
+}
+
+#ifndef OPEN_DEFAULT
+#define OPEN_DEFAULT 0
+#define OPEN_CONTENTS 1
+#endif
+
+
+NS_IMETHODIMP
+nsLocalFile::Reveal()
+{
+ PRBool isDirectory = PR_FALSE;
+ nsCAutoString path;
+
+ IsDirectory(&isDirectory);
+ if (isDirectory)
+ {
+ GetNativePath(path);
+ }
+ else
+ {
+ nsCOMPtr<nsIFile> parent;
+ GetParent(getter_AddRefs(parent));
+ if (parent)
+ parent->GetNativePath(path);
+ }
+
+ HOBJECT hobject = WinQueryObject(path.get());
+ WinSetFocus(HWND_DESKTOP, HWND_DESKTOP);
+ WinOpenObject( hobject, OPEN_CONTENTS, TRUE);
+
+ // we don't care if it succeeded or failed.
+ return NS_OK;
+}
+
+
+NS_IMETHODIMP
+nsLocalFile::Launch()
+{
+ HOBJECT hobject = WinQueryObject(mWorkingPath.get());
+ WinSetFocus(HWND_DESKTOP, HWND_DESKTOP);
+ WinOpenObject( hobject, OPEN_DEFAULT, TRUE);
+
+ // we don't care if it succeeded or failed.
+ return NS_OK;
+}
+
+nsresult
+NS_NewNativeLocalFile(const nsACString &path, PRBool followLinks, nsILocalFile* *result)
+{
+ nsLocalFile* file = new nsLocalFile();
+ if (file == nsnull)
+ return NS_ERROR_OUT_OF_MEMORY;
+ NS_ADDREF(file);
+
+ if (!path.IsEmpty()) {
+ nsresult rv = file->InitWithNativePath(path);
+ if (NS_FAILED(rv)) {
+ NS_RELEASE(file);
+ return rv;
+ }
+ }
+
+ *result = file;
+ return NS_OK;
+}
+
+// Locates the first occurrence of charToSearchFor in the stringToSearch
+static unsigned char* PR_CALLBACK
+_mbschr( const unsigned char* stringToSearch, int charToSearchFor)
+{
+ const unsigned char* p = stringToSearch;
+ do {
+ if (*p == charToSearchFor)
+ break;
+ p = (const unsigned char*)WinNextChar(0,0,0,(char*)p);
+ } while (*p); /* enddo */
+ // Result is p or NULL
+ return *p ? (unsigned char*)p : NULL;
+}
+
+// Locates last occurence of charToSearchFor in the stringToSearch
+extern unsigned char*
+_mbsrchr( const unsigned char* stringToSearch, int charToSearchFor)
+{
+ int length = strlen((const char*)stringToSearch);
+ const unsigned char* p = stringToSearch+length;
+ do {
+ if (*p == charToSearchFor)
+ break;
+ p = (const unsigned char*)WinPrevChar(0,0,0,(char*)stringToSearch,(char*)p);
+ } while (p > stringToSearch); /* enddo */
+ // Result is p or NULL
+ return (*p == charToSearchFor) ? (unsigned char*)p : NULL;
+}
+
+// Implement equivalent of Win32 CreateDirectoryA
+static nsresult PR_CALLBACK
+CreateDirectoryA( PSZ path, PEAOP2 ppEABuf)
+{
+ APIRET rc;
+ nsresult rv;
+ FILESTATUS3 pathInfo;
+
+ rc = DosCreateDir( path, ppEABuf );
+ if (rc != NO_ERROR) {
+ rv = ConvertOS2Error(rc);
+
+ // Check if directory already exists and if so, reflect that in the return value
+ rc = DosQueryPathInfo(path,
+ FIL_STANDARD, // Level 1 info
+ &pathInfo,
+ sizeof(pathInfo));
+ if (rc == NO_ERROR)
+ rv = ERROR_FILE_EXISTS;
+ }
+ else
+ rv = rc;
+
+ return rv;
+}
+
+static int isleadbyte(int c)
+{
+ static BOOL bDBCSFilled=FALSE;
+ static BYTE DBCSInfo[12] = { 0 }; /* According to the Control Program Guide&Ref,
+ 12 bytes is sufficient */
+ BYTE *curr;
+ BOOL retval = FALSE;
+
+ if( !bDBCSFilled ) {
+ COUNTRYCODE ctrycodeInfo = { 0 };
+ APIRET rc = NO_ERROR;
+ ctrycodeInfo.country = 0; /* Current Country */
+ ctrycodeInfo.codepage = 0; /* Current Codepage */
+
+ rc = DosQueryDBCSEnv( sizeof( DBCSInfo ),
+ &ctrycodeInfo,
+ DBCSInfo );
+ if( rc != NO_ERROR ) {
+ /* we had an error, do something? */
+ return FALSE;
+ }
+ bDBCSFilled=TRUE;
+ }
+
+ curr = DBCSInfo;
+ /* DBCSInfo returned by DosQueryDBCSEnv is terminated with two '0' bytes in a row */
+ while(( *curr != 0 ) && ( *(curr+1) != 0)) {
+ if(( c >= *curr ) && ( c <= *(curr+1) )) {
+ retval=TRUE;
+ break;
+ }
+ curr+=2;
+ }
+
+ return retval;
+}
+
+NS_IMETHODIMP
+nsLocalFile::InitWithPath(const nsAString &filePath)
+{
+ if (filePath.IsEmpty())
+ return InitWithNativePath(nsCString());
+
+ nsCAutoString tmp;
+ nsresult rv = NS_CopyUnicodeToNative(filePath, tmp);
+
+ if (NS_SUCCEEDED(rv))
+ return InitWithNativePath(tmp);
+
+ return rv;
+}
+
+NS_IMETHODIMP
+nsLocalFile::Append(const nsAString &node)
+{
+ if (node.IsEmpty())
+ return NS_OK;
+
+ nsCAutoString tmp;
+ nsresult rv = NS_CopyUnicodeToNative(node, tmp);
+
+ if (NS_SUCCEEDED(rv))
+ return AppendNative(tmp);
+
+ return rv;
+}
+
+NS_IMETHODIMP
+nsLocalFile::AppendRelativePath(const nsAString &node)
+{
+ if (node.IsEmpty())
+ return NS_OK;
+
+ nsCAutoString tmp;
+ nsresult rv = NS_CopyUnicodeToNative(node, tmp);
+
+ if (NS_SUCCEEDED(rv))
+ return AppendRelativeNativePath(tmp);
+
+ return rv;
+}
+
+NS_IMETHODIMP
+nsLocalFile::GetLeafName(nsAString &aLeafName)
+{
+ nsCAutoString tmp;
+ nsresult rv = GetNativeLeafName(tmp);
+
+ if (NS_SUCCEEDED(rv))
+ rv = NS_CopyNativeToUnicode(tmp, aLeafName);
+
+ return rv;
+}
+
+NS_IMETHODIMP
+nsLocalFile::SetLeafName(const nsAString &aLeafName)
+{
+ if (aLeafName.IsEmpty())
+ return SetNativeLeafName(nsCString());
+
+ nsCAutoString tmp;
+ nsresult rv = NS_CopyUnicodeToNative(aLeafName, tmp);
+
+ if (NS_SUCCEEDED(rv))
+ return SetNativeLeafName(tmp);
+
+ return rv;
+}
+
+NS_IMETHODIMP
+nsLocalFile::GetPath(nsAString &_retval)
+{
+ return NS_CopyNativeToUnicode(mWorkingPath, _retval);
+}
+
+NS_IMETHODIMP
+nsLocalFile::CopyTo(nsIFile *newParentDir, const nsAString &newName)
+{
+ if (newName.IsEmpty())
+ return CopyToNative(newParentDir, nsCString());
+
+ nsCAutoString tmp;
+ nsresult rv = NS_CopyUnicodeToNative(newName, tmp);
+
+ if (NS_SUCCEEDED(rv))
+ return CopyToNative(newParentDir, tmp);
+
+ return rv;
+}
+
+NS_IMETHODIMP
+nsLocalFile::CopyToFollowingLinks(nsIFile *newParentDir, const nsAString &newName)
+{
+ if (newName.IsEmpty())
+ return CopyToFollowingLinksNative(newParentDir, nsCString());
+
+ nsCAutoString tmp;
+ nsresult rv = NS_CopyUnicodeToNative(newName, tmp);
+
+ if (NS_SUCCEEDED(rv))
+ return CopyToFollowingLinksNative(newParentDir, tmp);
+
+ return rv;
+}
+
+NS_IMETHODIMP
+nsLocalFile::MoveTo(nsIFile *newParentDir, const nsAString &newName)
+{
+ if (newName.IsEmpty())
+ return MoveToNative(newParentDir, nsCString());
+
+ nsCAutoString tmp;
+ nsresult rv = NS_CopyUnicodeToNative(newName, tmp);
+
+ if (NS_SUCCEEDED(rv))
+ return MoveToNative(newParentDir, tmp);
+
+ return rv;
+}
+
+NS_IMETHODIMP
+nsLocalFile::GetTarget(nsAString &_retval)
+{
+ nsCAutoString tmp;
+ nsresult rv = GetNativeTarget(tmp);
+
+ if (NS_SUCCEEDED(rv))
+ rv = NS_CopyNativeToUnicode(tmp, _retval);
+
+ return rv;
+}
+
+nsresult
+NS_NewLocalFile(const nsAString &path, PRBool followLinks, nsILocalFile* *result)
+{
+ nsCAutoString buf;
+ nsresult rv = NS_CopyUnicodeToNative(path, buf);
+ if (NS_FAILED(rv)) {
+ *result = nsnull;
+ return rv;
+ }
+ return NS_NewNativeLocalFile(buf, followLinks, result);
+}
+
+//----------------------------------------------------------------------------
+// global init/shutdown
+//----------------------------------------------------------------------------
+
+void
+nsLocalFile::GlobalInit()
+{
+}
+
+void
+nsLocalFile::GlobalShutdown()
+{
+}
diff --git a/src/libs/xpcom18a4/xpcom/io/nsLocalFileOS2.h b/src/libs/xpcom18a4/xpcom/io/nsLocalFileOS2.h
new file mode 100644
index 00000000..72f3127a
--- /dev/null
+++ b/src/libs/xpcom18a4/xpcom/io/nsLocalFileOS2.h
@@ -0,0 +1,113 @@
+/* -*- Mode: C++; tab-width: 2; 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 client code, released
+ * March 31, 1998.
+ *
+ * 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 *****
+ *
+ * This Original Code has been modified by IBM Corporation. Modifications made by IBM
+ * described herein are Copyright (c) International Business Machines Corporation, 2000.
+ * Modifications to Mozilla code or documentation identified per MPL Section 3.3
+ *
+ * Date Modified by Description of modification
+ * 05/26/2000 IBM Corp. Make more like Windows.
+ */
+
+#ifndef _nsLocalFileOS2_H_
+#define _nsLocalFileOS2_H_
+
+#include "nscore.h"
+#include "nsError.h"
+#include "nsString.h"
+#include "nsCRT.h"
+#include "nsIFile.h"
+#include "nsIFactory.h"
+
+#define INCL_DOSFILEMGR
+#define INCL_DOSERRORS
+#define INCL_DOSPROCESS
+#define INCL_DOSSESMGR
+#define INCL_DOSMODULEMGR
+#define INCL_DOSNLS
+#define INCL_DOSMISC
+#define INCL_WINCOUNTRY
+#define INCL_WINWORKPLACE
+
+#include <os2.h>
+
+class NS_COM nsLocalFile : public nsILocalFile
+{
+public:
+ NS_DEFINE_STATIC_CID_ACCESSOR(NS_LOCAL_FILE_CID)
+
+ nsLocalFile();
+
+ static NS_METHOD nsLocalFileConstructor(nsISupports* outer, const nsIID& aIID, void* *aInstancePtr);
+
+ // nsISupports interface
+ NS_DECL_ISUPPORTS
+
+ // nsIFile interface
+ NS_DECL_NSIFILE
+
+ // nsILocalFile interface
+ NS_DECL_NSILOCALFILE
+
+public:
+ static void GlobalInit();
+ static void GlobalShutdown();
+
+private:
+ ~nsLocalFile() {}
+
+ nsLocalFile(const nsLocalFile& other);
+
+ // this is the flag which indicates if I can used cached information about the file
+ PRPackedBool mDirty;
+
+ // this string will alway be in native format!
+ nsCString mWorkingPath;
+
+ PRFileInfo64 mFileInfo64;
+
+ void MakeDirty();
+ nsresult Stat();
+
+ nsresult CopyMove(nsIFile *newParentDir, const nsACString &newName, PRBool move);
+ nsresult CopySingleFile(nsIFile *source, nsIFile* dest, const nsACString &newName, PRBool move);
+
+ nsresult SetModDate(PRInt64 aLastModifiedTime);
+};
+
+#endif
diff --git a/src/libs/xpcom18a4/xpcom/io/nsLocalFileOSX.cpp b/src/libs/xpcom18a4/xpcom/io/nsLocalFileOSX.cpp
new file mode 100644
index 00000000..dfc431d8
--- /dev/null
+++ b/src/libs/xpcom18a4/xpcom/io/nsLocalFileOSX.cpp
@@ -0,0 +1,2542 @@
+/* -*- Mode: C; tab-width: 2; 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.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2001, 2002
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Conrad Carlen <ccarlen@netscape.com>
+ * Jungshik Shin <jshin@mailaps.org>
+ * Asaf Romano <mozilla.mano@sent.com>
+ * Mark Mentovai <mark@moxienet.com>
+ *
+ * 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 "nsLocalFile.h"
+#include "nsDirectoryServiceDefs.h"
+
+#include "nsString.h"
+#include "nsReadableUtils.h"
+#include "nsIDirectoryEnumerator.h"
+#include "nsISimpleEnumerator.h"
+#include "nsITimelineService.h"
+#include "nsVoidArray.h"
+
+#include "plbase64.h"
+#include "prmem.h"
+#include "nsCRT.h"
+#include "nsHashKeys.h"
+
+#include "MoreFilesX.h"
+#include "FSCopyObject.h"
+#include "nsAutoBuffer.h"
+#include "nsTraceRefcntImpl.h"
+
+// Mac Includes
+#include <Carbon/Carbon.h>
+
+// Unix Includes
+#include <unistd.h>
+#include <sys/stat.h>
+#include <stdlib.h>
+
+#if !defined(MAC_OS_X_VERSION_10_4) || MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_4
+#define GetAliasSizeFromRecord(aliasRecord) aliasRecord.aliasSize
+#else
+#define GetAliasSizeFromRecord(aliasRecord) GetAliasSizeFromPtr(&aliasRecord)
+#endif
+
+#define CHECK_mBaseRef() \
+ PR_BEGIN_MACRO \
+ if (!mBaseRef) \
+ return NS_ERROR_NOT_INITIALIZED; \
+ PR_END_MACRO
+
+//*****************************************************************************
+// Static Function Prototypes
+//*****************************************************************************
+
+static nsresult MacErrorMapper(OSErr inErr);
+static OSErr FindRunningAppBySignature(OSType aAppSig, ProcessSerialNumber& outPsn);
+static void CopyUTF8toUTF16NFC(const nsACString& aSrc, nsAString& aResult);
+
+//*****************************************************************************
+// Local Helper Classes
+//*****************************************************************************
+
+#pragma mark -
+#pragma mark [FSRef operator==]
+
+bool operator==(const FSRef& lhs, const FSRef& rhs)
+{
+ return (::FSCompareFSRefs(&lhs, &rhs) == noErr);
+}
+
+#pragma mark -
+#pragma mark [StFollowLinksState]
+
+class StFollowLinksState
+{
+ public:
+ StFollowLinksState(nsLocalFile& aFile) :
+ mFile(aFile)
+ {
+ mFile.GetFollowLinks(&mSavedState);
+ }
+
+ StFollowLinksState(nsLocalFile& aFile, PRBool followLinksState) :
+ mFile(aFile)
+ {
+ mFile.GetFollowLinks(&mSavedState);
+ mFile.SetFollowLinks(followLinksState);
+ }
+
+ ~StFollowLinksState()
+ {
+ mFile.SetFollowLinks(mSavedState);
+ }
+
+ private:
+ nsLocalFile& mFile;
+ PRBool mSavedState;
+};
+
+#pragma mark -
+#pragma mark [nsDirEnumerator]
+
+class nsDirEnumerator : public nsISimpleEnumerator,
+ public nsIDirectoryEnumerator
+{
+ public:
+
+ NS_DECL_ISUPPORTS
+
+ nsDirEnumerator() :
+ mIterator(nsnull),
+ mFSRefsArray(nsnull),
+ mArrayCnt(0), mArrayIndex(0)
+ {
+ }
+
+ nsresult Init(nsILocalFileMac* parent)
+ {
+ NS_ENSURE_ARG(parent);
+
+ OSErr err;
+ nsresult rv;
+ FSRef parentRef;
+
+ rv = parent->GetFSRef(&parentRef);
+ if (NS_FAILED(rv))
+ return rv;
+
+ mFSRefsArray = (FSRef *)nsMemory::Alloc(sizeof(FSRef)
+ * kRequestCountPerIteration);
+ if (!mFSRefsArray)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ err = ::FSOpenIterator(&parentRef, kFSIterateFlat, &mIterator);
+ if (err != noErr)
+ return MacErrorMapper(err);
+
+ return NS_OK;
+ }
+
+ NS_IMETHOD HasMoreElements(PRBool *result)
+ {
+ if (mNext == nsnull) {
+ if (mArrayIndex >= mArrayCnt) {
+ ItemCount actualCnt;
+ OSErr err = ::FSGetCatalogInfoBulk(mIterator,
+ kRequestCountPerIteration,
+ &actualCnt,
+ nsnull,
+ kFSCatInfoNone,
+ nsnull,
+ mFSRefsArray,
+ nsnull,
+ nsnull);
+
+ if (err == noErr || err == errFSNoMoreItems) {
+ mArrayCnt = actualCnt;
+ mArrayIndex = 0;
+ }
+ }
+ if (mArrayIndex < mArrayCnt) {
+ nsLocalFile *newFile = new nsLocalFile;
+ if (!newFile)
+ return NS_ERROR_OUT_OF_MEMORY;
+ FSRef fsRef = mFSRefsArray[mArrayIndex];
+ if (NS_FAILED(newFile->InitWithFSRef(&fsRef)))
+ return NS_ERROR_FAILURE;
+ mArrayIndex++;
+ mNext = newFile;
+ }
+ }
+ *result = mNext != nsnull;
+ if (!*result)
+ Close();
+ return NS_OK;
+ }
+
+ NS_IMETHOD GetNext(nsISupports **result)
+ {
+ NS_ENSURE_ARG_POINTER(result);
+ *result = nsnull;
+
+ nsresult rv;
+ PRBool hasMore;
+ rv = HasMoreElements(&hasMore);
+ if (NS_FAILED(rv)) return rv;
+
+ *result = mNext; // might return nsnull
+ NS_IF_ADDREF(*result);
+
+ mNext = nsnull;
+ return NS_OK;
+ }
+
+ NS_IMETHOD GetNextFile(nsIFile **result)
+ {
+ *result = nsnull;
+ PRBool hasMore = PR_FALSE;
+ nsresult rv = HasMoreElements(&hasMore);
+ if (NS_FAILED(rv) || !hasMore)
+ return rv;
+ *result = mNext;
+ NS_IF_ADDREF(*result);
+ mNext = nsnull;
+ return NS_OK;
+ }
+
+ NS_IMETHOD Close()
+ {
+ if (mIterator) {
+ ::FSCloseIterator(mIterator);
+ mIterator = nsnull;
+ }
+ if (mFSRefsArray) {
+ nsMemory::Free(mFSRefsArray);
+ mFSRefsArray = nsnull;
+ }
+ return NS_OK;
+ }
+
+ private:
+ ~nsDirEnumerator()
+ {
+ Close();
+ }
+
+ protected:
+ // According to Apple doc, request the number of objects
+ // per call that will fit in 4 VM pages.
+ enum {
+ kRequestCountPerIteration = ((4096 * 4) / sizeof(FSRef))
+ };
+
+ nsCOMPtr<nsILocalFileMac> mNext;
+
+ FSIterator mIterator;
+ FSRef *mFSRefsArray;
+ PRInt32 mArrayCnt, mArrayIndex;
+};
+
+NS_IMPL_ISUPPORTS2(nsDirEnumerator, nsISimpleEnumerator, nsIDirectoryEnumerator)
+
+#pragma mark -
+#pragma mark [StAEDesc]
+
+class StAEDesc: public AEDesc
+{
+public:
+ StAEDesc()
+ {
+ descriptorType = typeNull;
+ dataHandle = nil;
+ }
+
+ ~StAEDesc()
+ {
+ ::AEDisposeDesc(this);
+ }
+};
+
+#define FILENAME_BUFFER_SIZE 512
+
+//*****************************************************************************
+// nsLocalFile
+//*****************************************************************************
+
+const char nsLocalFile::kPathSepChar = '/';
+const PRUnichar nsLocalFile::kPathSepUnichar = '/';
+
+// The HFS+ epoch is Jan. 1, 1904 GMT - differs from HFS in which times were local
+// The NSPR epoch is Jan. 1, 1970 GMT
+// 2082844800 is the difference in seconds between those dates
+const PRInt64 nsLocalFile::kJanuaryFirst1970Seconds = 2082844800LL;
+
+#pragma mark -
+#pragma mark [CTORs/DTOR]
+
+nsLocalFile::nsLocalFile() :
+ mBaseRef(nsnull),
+ mTargetRef(nsnull),
+ mCachedFSRefValid(PR_FALSE),
+ mFollowLinks(PR_TRUE),
+ mFollowLinksDirty(PR_TRUE)
+{
+}
+
+nsLocalFile::nsLocalFile(const nsLocalFile& src) :
+ mBaseRef(src.mBaseRef),
+ mTargetRef(src.mTargetRef),
+ mCachedFSRef(src.mCachedFSRef),
+ mCachedFSRefValid(src.mCachedFSRefValid),
+ mFollowLinks(src.mFollowLinks),
+ mFollowLinksDirty(src.mFollowLinksDirty)
+{
+ // A CFURLRef is immutable so no need to copy, just retain.
+ if (mBaseRef)
+ ::CFRetain(mBaseRef);
+ if (mTargetRef)
+ ::CFRetain(mTargetRef);
+}
+
+nsLocalFile::~nsLocalFile()
+{
+ if (mBaseRef)
+ ::CFRelease(mBaseRef);
+ if (mTargetRef)
+ ::CFRelease(mTargetRef);
+}
+
+
+//*****************************************************************************
+// nsLocalFile::nsISupports
+//*****************************************************************************
+#pragma mark -
+#pragma mark [nsISupports]
+
+NS_IMPL_THREADSAFE_ISUPPORTS4(nsLocalFile,
+ nsILocalFileMac,
+ nsILocalFile,
+ nsIFile,
+ nsIHashable)
+
+NS_METHOD nsLocalFile::nsLocalFileConstructor(nsISupports* outer, const nsIID& aIID, void* *aInstancePtr)
+{
+ NS_ENSURE_ARG_POINTER(aInstancePtr);
+ NS_ENSURE_NO_AGGREGATION(outer);
+
+ nsLocalFile* inst = new nsLocalFile();
+ if (inst == NULL)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ nsresult rv = inst->QueryInterface(aIID, aInstancePtr);
+ if (NS_FAILED(rv))
+ {
+ delete inst;
+ return rv;
+ }
+ return NS_OK;
+}
+
+
+//*****************************************************************************
+// nsLocalFile::nsIFile
+//*****************************************************************************
+#pragma mark -
+#pragma mark [nsIFile]
+
+/* void append (in AString node); */
+NS_IMETHODIMP nsLocalFile::Append(const nsAString& aNode)
+{
+ return AppendNative(NS_ConvertUTF16toUTF8(aNode));
+}
+
+/* [noscript] void appendNative (in ACString node); */
+NS_IMETHODIMP nsLocalFile::AppendNative(const nsACString& aNode)
+{
+ // Check we are correctly initialized.
+ CHECK_mBaseRef();
+
+ nsACString::const_iterator start, end;
+ aNode.BeginReading(start);
+ aNode.EndReading(end);
+ if (FindCharInReadable(kPathSepChar, start, end))
+ return NS_ERROR_FILE_UNRECOGNIZED_PATH;
+
+ CFStringRef nodeStrRef = ::CFStringCreateWithCString(kCFAllocatorDefault,
+ PromiseFlatCString(aNode).get(),
+ kCFStringEncodingUTF8);
+ if (nodeStrRef) {
+ CFURLRef newRef = ::CFURLCreateCopyAppendingPathComponent(kCFAllocatorDefault,
+ mBaseRef, nodeStrRef, PR_FALSE);
+ ::CFRelease(nodeStrRef);
+ if (newRef) {
+ SetBaseRef(newRef);
+ ::CFRelease(newRef);
+ return NS_OK;
+ }
+ }
+ return NS_ERROR_FAILURE;
+}
+
+/* void normalize (); */
+NS_IMETHODIMP nsLocalFile::Normalize()
+{
+ // Check we are correctly initialized.
+ CHECK_mBaseRef();
+
+ // CFURL doesn't doesn't seem to resolve paths containing relative
+ // components, so we'll nick the stdlib code from nsLocalFileUnix
+ UInt8 path[PATH_MAX] = "";
+ Boolean success;
+ success = ::CFURLGetFileSystemRepresentation(mBaseRef, true, path, PATH_MAX);
+ if (!success)
+ return NS_ERROR_FAILURE;
+
+ char resolved_path[PATH_MAX] = "";
+ char *resolved_path_ptr = nsnull;
+ resolved_path_ptr = realpath((char*)path, resolved_path);
+
+ // if there is an error, the return is null.
+ if (!resolved_path_ptr)
+ return NSRESULT_FOR_ERRNO();
+
+ // Need to know whether we're a directory to create a new CFURLRef
+ PRBool isDirectory;
+ nsresult rv = IsDirectory(&isDirectory);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = NS_ERROR_FAILURE;
+ CFStringRef pathStrRef =
+ ::CFStringCreateWithCString(kCFAllocatorDefault,
+ resolved_path,
+ kCFStringEncodingUTF8);
+ if (pathStrRef) {
+ CFURLRef newURLRef =
+ ::CFURLCreateWithFileSystemPath(kCFAllocatorDefault, pathStrRef,
+ kCFURLPOSIXPathStyle, isDirectory);
+ if (newURLRef) {
+ SetBaseRef(newURLRef);
+ ::CFRelease(newURLRef);
+ rv = NS_OK;
+ }
+ ::CFRelease(pathStrRef);
+ }
+
+ return rv;
+}
+
+/* void create (in unsigned long type, in unsigned long permissions); */
+NS_IMETHODIMP nsLocalFile::Create(PRUint32 type, PRUint32 permissions)
+{
+ if (type != NORMAL_FILE_TYPE && type != DIRECTORY_TYPE)
+ return NS_ERROR_FILE_UNKNOWN_TYPE;
+
+ // Check we are correctly initialized.
+ CHECK_mBaseRef();
+
+ nsStringArray nonExtantNodes;
+ CFURLRef pathURLRef = mBaseRef;
+ FSRef pathFSRef;
+ CFStringRef leafStrRef = nsnull;
+ nsAutoBuffer<UniChar, FILENAME_BUFFER_SIZE> buffer;
+ Boolean success;
+
+ // Work backwards through the path to find the last node which
+ // exists. Place the nodes which don't exist in an array and we'll
+ // create those below.
+ while ((success = ::CFURLGetFSRef(pathURLRef, &pathFSRef)) == false) {
+ leafStrRef = ::CFURLCopyLastPathComponent(pathURLRef);
+ if (!leafStrRef)
+ break;
+ CFIndex leafLen = ::CFStringGetLength(leafStrRef);
+ if (!buffer.EnsureElemCapacity(leafLen + 1))
+ break;
+ ::CFStringGetCharacters(leafStrRef, CFRangeMake(0, leafLen), buffer.get());
+ buffer.get()[leafLen] = '\0';
+ nonExtantNodes.AppendString(nsString(nsDependentString(buffer.get())));
+ ::CFRelease(leafStrRef);
+ leafStrRef = nsnull;
+
+ // Get the parent of the leaf for the next go round
+ CFURLRef parent = ::CFURLCreateCopyDeletingLastPathComponent(NULL, pathURLRef);
+ if (!parent)
+ break;
+ if (pathURLRef != mBaseRef)
+ ::CFRelease(pathURLRef);
+ pathURLRef = parent;
+ }
+ if (pathURLRef != mBaseRef)
+ ::CFRelease(pathURLRef);
+ if (leafStrRef != nsnull)
+ ::CFRelease(leafStrRef);
+ if (!success)
+ return NS_ERROR_FAILURE;
+ PRInt32 nodesToCreate = nonExtantNodes.Count();
+ if (nodesToCreate == 0)
+ return NS_ERROR_FILE_ALREADY_EXISTS;
+
+ OSErr err;
+ nsAutoString nextNodeName;
+ for (PRInt32 i = nodesToCreate - 1; i > 0; i--) {
+ nonExtantNodes.StringAt(i, nextNodeName);
+ err = ::FSCreateDirectoryUnicode(&pathFSRef,
+ nextNodeName.Length(),
+ (const UniChar *)nextNodeName.get(),
+ kFSCatInfoNone,
+ nsnull, &pathFSRef, nsnull, nsnull);
+ if (err != noErr)
+ return MacErrorMapper(err);
+ }
+ nonExtantNodes.StringAt(0, nextNodeName);
+ if (type == NORMAL_FILE_TYPE) {
+ err = ::FSCreateFileUnicode(&pathFSRef,
+ nextNodeName.Length(),
+ (const UniChar *)nextNodeName.get(),
+ kFSCatInfoNone,
+ nsnull, nsnull, nsnull);
+ }
+ else {
+ err = ::FSCreateDirectoryUnicode(&pathFSRef,
+ nextNodeName.Length(),
+ (const UniChar *)nextNodeName.get(),
+ kFSCatInfoNone,
+ nsnull, nsnull, nsnull, nsnull);
+ }
+
+ return MacErrorMapper(err);
+}
+
+/* attribute AString leafName; */
+NS_IMETHODIMP nsLocalFile::GetLeafName(nsAString& aLeafName)
+{
+ nsCAutoString nativeString;
+ nsresult rv = GetNativeLeafName(nativeString);
+ if (NS_FAILED(rv))
+ return rv;
+ CopyUTF8toUTF16NFC(nativeString, aLeafName);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsLocalFile::SetLeafName(const nsAString& aLeafName)
+{
+ return SetNativeLeafName(NS_ConvertUTF16toUTF8(aLeafName));
+}
+
+/* [noscript] attribute ACString nativeLeafName; */
+NS_IMETHODIMP nsLocalFile::GetNativeLeafName(nsACString& aNativeLeafName)
+{
+ // Check we are correctly initialized.
+ CHECK_mBaseRef();
+
+ nsresult rv = NS_ERROR_FAILURE;
+ CFStringRef leafStrRef = ::CFURLCopyLastPathComponent(mBaseRef);
+ if (leafStrRef) {
+ rv = CFStringReftoUTF8(leafStrRef, aNativeLeafName);
+ ::CFRelease(leafStrRef);
+ }
+ return rv;
+}
+
+NS_IMETHODIMP nsLocalFile::SetNativeLeafName(const nsACString& aNativeLeafName)
+{
+ // Check we are correctly initialized.
+ CHECK_mBaseRef();
+
+ nsresult rv = NS_ERROR_FAILURE;
+ CFURLRef parentURLRef = ::CFURLCreateCopyDeletingLastPathComponent(kCFAllocatorDefault, mBaseRef);
+ if (parentURLRef) {
+ CFStringRef nodeStrRef = ::CFStringCreateWithCString(kCFAllocatorDefault,
+ PromiseFlatCString(aNativeLeafName).get(),
+ kCFStringEncodingUTF8);
+
+ if (nodeStrRef) {
+ CFURLRef newURLRef = ::CFURLCreateCopyAppendingPathComponent(kCFAllocatorDefault,
+ parentURLRef, nodeStrRef, PR_FALSE);
+ if (newURLRef) {
+ SetBaseRef(newURLRef);
+ ::CFRelease(newURLRef);
+ rv = NS_OK;
+ }
+ ::CFRelease(nodeStrRef);
+ }
+ ::CFRelease(parentURLRef);
+ }
+ return rv;
+}
+
+/* void copyTo (in nsIFile newParentDir, in AString newName); */
+NS_IMETHODIMP nsLocalFile::CopyTo(nsIFile *newParentDir, const nsAString& newName)
+{
+ return CopyInternal(newParentDir, newName, PR_FALSE);
+}
+
+/* [noscrpit] void CopyToNative (in nsIFile newParentDir, in ACString newName); */
+NS_IMETHODIMP nsLocalFile::CopyToNative(nsIFile *newParentDir, const nsACString& newName)
+{
+ return CopyInternal(newParentDir, NS_ConvertUTF8toUTF16(newName), PR_FALSE);
+}
+
+/* void copyToFollowingLinks (in nsIFile newParentDir, in AString newName); */
+NS_IMETHODIMP nsLocalFile::CopyToFollowingLinks(nsIFile *newParentDir, const nsAString& newName)
+{
+ return CopyInternal(newParentDir, newName, PR_TRUE);
+}
+
+/* [noscript] void copyToFollowingLinksNative (in nsIFile newParentDir, in ACString newName); */
+NS_IMETHODIMP nsLocalFile::CopyToFollowingLinksNative(nsIFile *newParentDir, const nsACString& newName)
+{
+ return CopyInternal(newParentDir, NS_ConvertUTF8toUTF16(newName), PR_TRUE);
+}
+
+/* void moveTo (in nsIFile newParentDir, in AString newName); */
+NS_IMETHODIMP nsLocalFile::MoveTo(nsIFile *newParentDir, const nsAString& newName)
+{
+ return MoveToNative(newParentDir, NS_ConvertUTF16toUTF8(newName));
+}
+
+/* [noscript] void moveToNative (in nsIFile newParentDir, in ACString newName); */
+NS_IMETHODIMP nsLocalFile::MoveToNative(nsIFile *newParentDir, const nsACString& newName)
+{
+ // Check we are correctly initialized.
+ CHECK_mBaseRef();
+
+ StFollowLinksState followLinks(*this, PR_FALSE);
+
+ PRBool isDirectory;
+ nsresult rv = IsDirectory(&isDirectory);
+ if (NS_FAILED(rv))
+ return rv;
+
+ // Get the source path.
+ nsCAutoString srcPath;
+ rv = GetNativePath(srcPath);
+ if (NS_FAILED(rv))
+ return rv;
+
+ // Build the destination path.
+ nsCOMPtr<nsIFile> parentDir = newParentDir;
+ if (!parentDir) {
+ if (newName.IsEmpty())
+ return NS_ERROR_INVALID_ARG;
+ rv = GetParent(getter_AddRefs(parentDir));
+ if (NS_FAILED(rv))
+ return rv;
+ }
+ else {
+ PRBool exists;
+ rv = parentDir->Exists(&exists);
+ if (NS_FAILED(rv))
+ return rv;
+ if (!exists) {
+ rv = parentDir->Create(nsIFile::DIRECTORY_TYPE, 0777);
+ if (NS_FAILED(rv))
+ return rv;
+ }
+ }
+
+ nsCAutoString destPath;
+ rv = parentDir->GetNativePath(destPath);
+ if (NS_FAILED(rv))
+ return rv;
+
+ if (!newName.IsEmpty())
+ destPath.Append(NS_LITERAL_CSTRING("/") + newName);
+ else {
+ nsCAutoString leafName;
+ rv = GetNativeLeafName(leafName);
+ if (NS_FAILED(rv))
+ return rv;
+ destPath.Append(NS_LITERAL_CSTRING("/") + leafName);
+ }
+
+ // Perform the move.
+ if (rename(srcPath.get(), destPath.get()) != 0) {
+ if (errno == EXDEV) {
+ // Can't move across volume (device) boundaries. Copy and remove.
+ rv = CopyToNative(parentDir, newName);
+ if (NS_SUCCEEDED(rv)) {
+ // Permit removal failure.
+ Remove(PR_TRUE);
+ }
+ }
+ else
+ rv = NSRESULT_FOR_ERRNO();
+
+ if (NS_FAILED(rv))
+ return rv;
+ }
+
+ // Update |this| to refer to the moved file.
+ CFURLRef newBaseRef =
+ ::CFURLCreateFromFileSystemRepresentation(NULL, (UInt8*)destPath.get(),
+ destPath.Length(), isDirectory);
+ if (!newBaseRef)
+ return NS_ERROR_FAILURE;
+ SetBaseRef(newBaseRef);
+ ::CFRelease(newBaseRef);
+
+ return rv;
+}
+
+/* void remove (in boolean recursive); */
+NS_IMETHODIMP nsLocalFile::Remove(PRBool recursive)
+{
+ // Check we are correctly initialized.
+ CHECK_mBaseRef();
+
+ // XXX If we're an alias, never remove target
+ StFollowLinksState followLinks(*this, PR_FALSE);
+
+ PRBool isDirectory;
+ nsresult rv = IsDirectory(&isDirectory);
+ if (NS_FAILED(rv))
+ return rv;
+
+ if (recursive && isDirectory) {
+ FSRef fsRef;
+ rv = GetFSRefInternal(fsRef);
+ if (NS_FAILED(rv))
+ return rv;
+
+ // Call MoreFilesX to do a recursive removal.
+ OSStatus err = ::FSDeleteContainer(&fsRef);
+ rv = MacErrorMapper(err);
+ }
+ else {
+ nsCAutoString path;
+ rv = GetNativePath(path);
+ if (NS_FAILED(rv))
+ return rv;
+
+ const char* pathPtr = path.get();
+ int status;
+ if (isDirectory)
+ status = rmdir(pathPtr);
+ else
+ status = unlink(pathPtr);
+
+ if (status != 0)
+ rv = NSRESULT_FOR_ERRNO();
+ }
+
+ mCachedFSRefValid = PR_FALSE;
+ return rv;
+}
+
+/* attribute unsigned long permissions; */
+NS_IMETHODIMP nsLocalFile::GetPermissions(PRUint32 *aPermissions)
+{
+ NS_ENSURE_ARG_POINTER(aPermissions);
+
+ FSRef fsRef;
+ nsresult rv = GetFSRefInternal(fsRef);
+ if (NS_FAILED(rv))
+ return rv;
+
+ FSCatalogInfo catalogInfo;
+ OSErr err = ::FSGetCatalogInfo(&fsRef, kFSCatInfoPermissions, &catalogInfo,
+ nsnull, nsnull, nsnull);
+ if (err != noErr)
+ return MacErrorMapper(err);
+ FSPermissionInfo *permPtr = (FSPermissionInfo*)catalogInfo.permissions;
+ *aPermissions = permPtr->mode;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsLocalFile::SetPermissions(PRUint32 aPermissions)
+{
+ FSRef fsRef;
+ nsresult rv = GetFSRefInternal(fsRef);
+ if (NS_FAILED(rv))
+ return rv;
+
+ FSCatalogInfo catalogInfo;
+ OSErr err = ::FSGetCatalogInfo(&fsRef, kFSCatInfoPermissions, &catalogInfo,
+ nsnull, nsnull, nsnull);
+ if (err != noErr)
+ return MacErrorMapper(err);
+ FSPermissionInfo *permPtr = (FSPermissionInfo*)catalogInfo.permissions;
+ permPtr->mode = (UInt16)aPermissions;
+ err = ::FSSetCatalogInfo(&fsRef, kFSCatInfoPermissions, &catalogInfo);
+ return MacErrorMapper(err);
+}
+
+/* attribute unsigned long permissionsOfLink; */
+NS_IMETHODIMP nsLocalFile::GetPermissionsOfLink(PRUint32 *aPermissionsOfLink)
+{
+ NS_ERROR("NS_ERROR_NOT_IMPLEMENTED");
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP nsLocalFile::SetPermissionsOfLink(PRUint32 aPermissionsOfLink)
+{
+ NS_ERROR("NS_ERROR_NOT_IMPLEMENTED");
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+/* attribute PRInt64 lastModifiedTime; */
+NS_IMETHODIMP nsLocalFile::GetLastModifiedTime(PRInt64 *aLastModifiedTime)
+{
+ // Check we are correctly initialized.
+ CHECK_mBaseRef();
+
+ NS_ENSURE_ARG_POINTER(aLastModifiedTime);
+
+ FSRef fsRef;
+ nsresult rv = GetFSRefInternal(fsRef);
+ if (NS_FAILED(rv))
+ return rv;
+
+ FSCatalogInfo catalogInfo;
+ OSErr err = ::FSGetCatalogInfo(&fsRef, kFSCatInfoContentMod, &catalogInfo,
+ nsnull, nsnull, nsnull);
+ if (err != noErr)
+ return MacErrorMapper(err);
+ *aLastModifiedTime = HFSPlustoNSPRTime(catalogInfo.contentModDate);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsLocalFile::SetLastModifiedTime(PRInt64 aLastModifiedTime)
+{
+ // Check we are correctly initialized.
+ CHECK_mBaseRef();
+
+ OSErr err;
+ nsresult rv;
+ FSRef fsRef;
+ FSCatalogInfo catalogInfo;
+
+ rv = GetFSRefInternal(fsRef);
+ if (NS_FAILED(rv))
+ return rv;
+
+ FSRef parentRef;
+ PRBool notifyParent;
+
+ /* Get the node flags, the content modification date and time, and the parent ref */
+ err = ::FSGetCatalogInfo(&fsRef, kFSCatInfoNodeFlags + kFSCatInfoContentMod,
+ &catalogInfo, NULL, NULL, &parentRef);
+ if (err != noErr)
+ return MacErrorMapper(err);
+
+ /* Notify the parent if this is a file */
+ notifyParent = (0 == (catalogInfo.nodeFlags & kFSNodeIsDirectoryMask));
+
+ NSPRtoHFSPlusTime(aLastModifiedTime, catalogInfo.contentModDate);
+ err = ::FSSetCatalogInfo(&fsRef, kFSCatInfoContentMod, &catalogInfo);
+ if (err != noErr)
+ return MacErrorMapper(err);
+
+ /* Send a notification for the parent of the file, or for the directory */
+ err = FNNotify(notifyParent ? &parentRef : &fsRef, kFNDirectoryModifiedMessage, kNilOptions);
+ if (err != noErr)
+ return MacErrorMapper(err);
+
+ return NS_OK;
+}
+
+/* attribute PRInt64 lastModifiedTimeOfLink; */
+NS_IMETHODIMP nsLocalFile::GetLastModifiedTimeOfLink(PRInt64 *aLastModifiedTimeOfLink)
+{
+ NS_ERROR("NS_ERROR_NOT_IMPLEMENTED");
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+NS_IMETHODIMP nsLocalFile::SetLastModifiedTimeOfLink(PRInt64 aLastModifiedTimeOfLink)
+{
+ NS_ERROR("NS_ERROR_NOT_IMPLEMENTED");
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+/* attribute PRInt64 fileSize; */
+NS_IMETHODIMP nsLocalFile::GetFileSize(PRInt64 *aFileSize)
+{
+ NS_ENSURE_ARG_POINTER(aFileSize);
+ *aFileSize = 0;
+
+ FSRef fsRef;
+ nsresult rv = GetFSRefInternal(fsRef);
+ if (NS_FAILED(rv))
+ return rv;
+
+ FSCatalogInfo catalogInfo;
+ OSErr err = ::FSGetCatalogInfo(&fsRef, kFSCatInfoNodeFlags + kFSCatInfoDataSizes, &catalogInfo,
+ nsnull, nsnull, nsnull);
+ if (err != noErr)
+ return MacErrorMapper(err);
+
+ // FSGetCatalogInfo can return a bogus size for directories sometimes, so only
+ // rely on the answer for files
+ if ((catalogInfo.nodeFlags & kFSNodeIsDirectoryMask) == 0)
+ *aFileSize = catalogInfo.dataLogicalSize;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsLocalFile::SetFileSize(PRInt64 aFileSize)
+{
+ // Check we are correctly initialized.
+ CHECK_mBaseRef();
+
+ FSRef fsRef;
+ nsresult rv = GetFSRefInternal(fsRef);
+ if (NS_FAILED(rv))
+ return rv;
+
+ SInt16 refNum;
+ OSErr err = ::FSOpenFork(&fsRef, 0, nsnull, fsWrPerm, &refNum);
+ if (err != noErr)
+ return MacErrorMapper(err);
+ err = ::FSSetForkSize(refNum, fsFromStart, aFileSize);
+ ::FSCloseFork(refNum);
+
+ return MacErrorMapper(err);
+}
+
+/* readonly attribute PRInt64 fileSizeOfLink; */
+NS_IMETHODIMP nsLocalFile::GetFileSizeOfLink(PRInt64 *aFileSizeOfLink)
+{
+ // Check we are correctly initialized.
+ CHECK_mBaseRef();
+
+ NS_ENSURE_ARG_POINTER(aFileSizeOfLink);
+
+ StFollowLinksState followLinks(*this, PR_FALSE);
+ return GetFileSize(aFileSizeOfLink);
+}
+
+/* readonly attribute AString target; */
+NS_IMETHODIMP nsLocalFile::GetTarget(nsAString& aTarget)
+{
+ NS_ERROR("NS_ERROR_NOT_IMPLEMENTED");
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+/* [noscript] readonly attribute ACString nativeTarget; */
+NS_IMETHODIMP nsLocalFile::GetNativeTarget(nsACString& aNativeTarget)
+{
+ NS_ERROR("NS_ERROR_NOT_IMPLEMENTED");
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+/* readonly attribute AString path; */
+NS_IMETHODIMP nsLocalFile::GetPath(nsAString& aPath)
+{
+ nsCAutoString nativeString;
+ nsresult rv = GetNativePath(nativeString);
+ if (NS_FAILED(rv))
+ return rv;
+ CopyUTF8toUTF16NFC(nativeString, aPath);
+ return NS_OK;
+}
+
+/* [noscript] readonly attribute ACString nativePath; */
+NS_IMETHODIMP nsLocalFile::GetNativePath(nsACString& aNativePath)
+{
+ // Check we are correctly initialized.
+ CHECK_mBaseRef();
+
+ nsresult rv = NS_ERROR_FAILURE;
+ CFStringRef pathStrRef = ::CFURLCopyFileSystemPath(mBaseRef, kCFURLPOSIXPathStyle);
+ if (pathStrRef) {
+ rv = CFStringReftoUTF8(pathStrRef, aNativePath);
+ ::CFRelease(pathStrRef);
+ }
+ return rv;
+}
+
+/* boolean exists (); */
+NS_IMETHODIMP nsLocalFile::Exists(PRBool *_retval)
+{
+ // Check we are correctly initialized.
+ CHECK_mBaseRef();
+
+ NS_ENSURE_ARG_POINTER(_retval);
+ *_retval = PR_FALSE;
+
+ FSRef fsRef;
+ if (NS_SUCCEEDED(GetFSRefInternal(fsRef, PR_TRUE))) {
+ *_retval = PR_TRUE;
+ }
+
+ return NS_OK;
+}
+
+/* boolean isWritable (); */
+NS_IMETHODIMP nsLocalFile::IsWritable(PRBool *_retval)
+{
+ // Check we are correctly initialized.
+ CHECK_mBaseRef();
+
+ NS_ENSURE_ARG_POINTER(_retval);
+ *_retval = PR_FALSE;
+
+ FSRef fsRef;
+ nsresult rv = GetFSRefInternal(fsRef);
+ if (NS_FAILED(rv))
+ return rv;
+ if (::FSCheckLock(&fsRef) == noErr) {
+ PRUint32 permissions;
+ rv = GetPermissions(&permissions);
+ if (NS_FAILED(rv))
+ return rv;
+ *_retval = ((permissions & S_IWUSR) != 0);
+ }
+ return NS_OK;
+}
+
+/* boolean isReadable (); */
+NS_IMETHODIMP nsLocalFile::IsReadable(PRBool *_retval)
+{
+ // Check we are correctly initialized.
+ CHECK_mBaseRef();
+
+ NS_ENSURE_ARG_POINTER(_retval);
+ *_retval = PR_FALSE;
+
+ PRUint32 permissions;
+ nsresult rv = GetPermissions(&permissions);
+ if (NS_FAILED(rv))
+ return rv;
+ *_retval = ((permissions & S_IRUSR) != 0);
+ return NS_OK;
+}
+
+/* boolean isExecutable (); */
+NS_IMETHODIMP nsLocalFile::IsExecutable(PRBool *_retval)
+{
+ // Check we are correctly initialized.
+ CHECK_mBaseRef();
+
+ NS_ENSURE_ARG_POINTER(_retval);
+ *_retval = PR_FALSE;
+
+ FSRef fsRef;
+ nsresult rv = GetFSRefInternal(fsRef);
+ if (NS_FAILED(rv))
+ return rv;
+
+ LSRequestedInfo theInfoRequest = kLSRequestAllInfo;
+ LSItemInfoRecord theInfo;
+ if (::LSCopyItemInfoForRef(&fsRef, theInfoRequest, &theInfo) == noErr) {
+ if ((theInfo.flags & kLSItemInfoIsApplication) != 0)
+ *_retval = PR_TRUE;
+ }
+ return NS_OK;
+}
+
+/* boolean isHidden (); */
+NS_IMETHODIMP nsLocalFile::IsHidden(PRBool *_retval)
+{
+ NS_ENSURE_ARG_POINTER(_retval);
+ *_retval = PR_FALSE;
+
+ FSRef fsRef;
+ nsresult rv = GetFSRefInternal(fsRef);
+ if (NS_FAILED(rv))
+ return rv;
+
+ FSCatalogInfo catalogInfo;
+ HFSUniStr255 leafName;
+ OSErr err = ::FSGetCatalogInfo(&fsRef, kFSCatInfoFinderInfo, &catalogInfo,
+ &leafName, nsnull, nsnull);
+ if (err != noErr)
+ return MacErrorMapper(err);
+
+ FileInfo *fInfoPtr = (FileInfo *)(catalogInfo.finderInfo); // Finder flags are in the same place whether we use FileInfo or FolderInfo
+ if ((fInfoPtr->finderFlags & kIsInvisible) != 0) {
+ *_retval = PR_TRUE;
+ }
+ else {
+ // If the leaf name begins with a '.', consider it invisible
+ if (leafName.length >= 1 && leafName.unicode[0] == UniChar('.'))
+ *_retval = PR_TRUE;
+ }
+ return NS_OK;
+}
+
+/* boolean isDirectory (); */
+NS_IMETHODIMP nsLocalFile::IsDirectory(PRBool *_retval)
+{
+ NS_ENSURE_ARG_POINTER(_retval);
+ *_retval = PR_FALSE;
+
+ FSRef fsRef;
+ nsresult rv = GetFSRefInternal(fsRef, PR_FALSE);
+ if (NS_FAILED(rv))
+ return rv;
+
+ FSCatalogInfo catalogInfo;
+ OSErr err = ::FSGetCatalogInfo(&fsRef, kFSCatInfoNodeFlags, &catalogInfo,
+ nsnull, nsnull, nsnull);
+ if (err != noErr)
+ return MacErrorMapper(err);
+ *_retval = ((catalogInfo.nodeFlags & kFSNodeIsDirectoryMask) != 0);
+ return NS_OK;
+}
+
+/* boolean isFile (); */
+NS_IMETHODIMP nsLocalFile::IsFile(PRBool *_retval)
+{
+ NS_ENSURE_ARG_POINTER(_retval);
+ *_retval = PR_FALSE;
+
+ FSRef fsRef;
+ nsresult rv = GetFSRefInternal(fsRef, PR_FALSE);
+ if (NS_FAILED(rv))
+ return rv;
+
+ FSCatalogInfo catalogInfo;
+ OSErr err = ::FSGetCatalogInfo(&fsRef, kFSCatInfoNodeFlags, &catalogInfo,
+ nsnull, nsnull, nsnull);
+ if (err != noErr)
+ return MacErrorMapper(err);
+ *_retval = ((catalogInfo.nodeFlags & kFSNodeIsDirectoryMask) == 0);
+ return NS_OK;
+}
+
+/* boolean isSymlink (); */
+NS_IMETHODIMP nsLocalFile::IsSymlink(PRBool *_retval)
+{
+ // Check we are correctly initialized.
+ CHECK_mBaseRef();
+
+ NS_ENSURE_ARG(_retval);
+ *_retval = PR_FALSE;
+
+ // Check we are correctly initialized.
+ CHECK_mBaseRef();
+
+ FSRef fsRef;
+ if (::CFURLGetFSRef(mBaseRef, &fsRef)) {
+ Boolean isAlias, isFolder;
+ if (::FSIsAliasFile(&fsRef, &isAlias, &isFolder) == noErr)
+ *_retval = isAlias;
+ }
+ return NS_OK;
+}
+
+/* boolean isSpecial (); */
+NS_IMETHODIMP nsLocalFile::IsSpecial(PRBool *_retval)
+{
+ NS_ERROR("NS_ERROR_NOT_IMPLEMENTED");
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+/* nsIFile clone (); */
+NS_IMETHODIMP nsLocalFile::Clone(nsIFile **_retval)
+{
+ // Just copy-construct ourselves
+ *_retval = new nsLocalFile(*this);
+ if (!*_retval)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ NS_ADDREF(*_retval);
+
+ return NS_OK;
+}
+
+/* boolean equals (in nsIFile inFile); */
+NS_IMETHODIMP nsLocalFile::Equals(nsIFile *inFile, PRBool *_retval)
+{
+ return EqualsInternal(inFile, PR_TRUE, _retval);
+}
+
+nsresult
+nsLocalFile::EqualsInternal(nsISupports* inFile, PRBool aUpdateCache,
+ PRBool *_retval)
+{
+ NS_ENSURE_ARG_POINTER(_retval);
+ *_retval = PR_FALSE;
+
+ nsCOMPtr<nsILocalFileMac> inMacFile(do_QueryInterface(inFile));
+ if (!inFile)
+ return NS_OK;
+
+ nsLocalFile* inLF =
+ static_cast<nsLocalFile*>((nsILocalFileMac*) inMacFile);
+
+ // If both exist, compare FSRefs
+ FSRef thisFSRef, inFSRef;
+ nsresult rv1 = GetFSRefInternal(thisFSRef, aUpdateCache);
+ nsresult rv2 = inLF->GetFSRefInternal(inFSRef, aUpdateCache);
+ if (NS_SUCCEEDED(rv1) && NS_SUCCEEDED(rv2)) {
+ *_retval = (thisFSRef == inFSRef);
+ return NS_OK;
+ }
+ // If one exists and the other doesn't, not equal
+ if (rv1 != rv2)
+ return NS_OK;
+
+ // Arg, we have to get their paths and compare
+ nsCAutoString thisPath, inPath;
+ if (NS_FAILED(GetNativePath(thisPath)))
+ return NS_ERROR_FAILURE;
+ if (NS_FAILED(inMacFile->GetNativePath(inPath)))
+ return NS_ERROR_FAILURE;
+ *_retval = thisPath.Equals(inPath);
+
+ return NS_OK;
+}
+
+/* boolean contains (in nsIFile inFile, in boolean recur); */
+NS_IMETHODIMP nsLocalFile::Contains(nsIFile *inFile, PRBool recur, PRBool *_retval)
+{
+ // Check we are correctly initialized.
+ CHECK_mBaseRef();
+
+ NS_ENSURE_ARG_POINTER(_retval);
+ *_retval = PR_FALSE;
+
+ PRBool isDir;
+ nsresult rv = IsDirectory(&isDir);
+ if (NS_FAILED(rv))
+ return rv;
+ if (!isDir)
+ return NS_OK; // must be a dir to contain someone
+
+ nsCAutoString thisPath, inPath;
+ if (NS_FAILED(GetNativePath(thisPath)) || NS_FAILED(inFile->GetNativePath(inPath)))
+ return NS_ERROR_FAILURE;
+ size_t thisPathLen = thisPath.Length();
+ if ((inPath.Length() > thisPathLen + 1) && (strncasecmp(thisPath.get(), inPath.get(), thisPathLen) == 0)) {
+ // Now make sure that the |inFile|'s path has a separator at thisPathLen,
+ // and there's at least one more character after that.
+ if (inPath[thisPathLen] == kPathSepChar)
+ *_retval = PR_TRUE;
+ }
+ return NS_OK;
+}
+
+/* readonly attribute nsIFile parent; */
+NS_IMETHODIMP nsLocalFile::GetParent(nsIFile * *aParent)
+{
+ NS_ENSURE_ARG_POINTER(aParent);
+ *aParent = nsnull;
+
+ // Check we are correctly initialized.
+ CHECK_mBaseRef();
+
+ nsLocalFile *newFile = nsnull;
+
+ // If it can be determined without error that a file does not
+ // have a parent, return nsnull for the parent and NS_OK as the result.
+ // See bug 133617.
+ nsresult rv = NS_OK;
+ CFURLRef parentURLRef = ::CFURLCreateCopyDeletingLastPathComponent(kCFAllocatorDefault, mBaseRef);
+ if (parentURLRef) {
+ // If the parent path is longer than file's path then
+ // CFURLCreateCopyDeletingLastPathComponent must have simply added
+ // two dots at the end - in this case indicate that there is no parent.
+ // See bug 332389.
+ CFStringRef path = ::CFURLGetString(mBaseRef);
+ CFStringRef newPath = ::CFURLGetString(parentURLRef);
+ if (::CFStringGetLength(newPath) < ::CFStringGetLength(path)) {
+ rv = NS_ERROR_FAILURE;
+ newFile = new nsLocalFile;
+ if (newFile) {
+ rv = newFile->InitWithCFURL(parentURLRef);
+ if (NS_SUCCEEDED(rv)) {
+ NS_ADDREF(*aParent = newFile);
+ rv = NS_OK;
+ }
+ }
+ }
+ ::CFRelease(parentURLRef);
+ }
+ return rv;
+}
+
+/* readonly attribute nsISimpleEnumerator directoryEntries; */
+NS_IMETHODIMP nsLocalFile::GetDirectoryEntries(nsISimpleEnumerator **aDirectoryEntries)
+{
+ NS_ENSURE_ARG_POINTER(aDirectoryEntries);
+ *aDirectoryEntries = nsnull;
+
+ nsresult rv;
+ PRBool isDir;
+ rv = IsDirectory(&isDir);
+ if (NS_FAILED(rv))
+ return rv;
+ if (!isDir)
+ return NS_ERROR_FILE_NOT_DIRECTORY;
+
+ nsDirEnumerator* dirEnum = new nsDirEnumerator;
+ if (dirEnum == nsnull)
+ return NS_ERROR_OUT_OF_MEMORY;
+ NS_ADDREF(dirEnum);
+ rv = dirEnum->Init(this);
+ if (NS_FAILED(rv)) {
+ NS_RELEASE(dirEnum);
+ return rv;
+ }
+ *aDirectoryEntries = dirEnum;
+
+ return NS_OK;
+}
+
+
+//*****************************************************************************
+// nsLocalFile::nsILocalFile
+//*****************************************************************************
+#pragma mark -
+#pragma mark [nsILocalFile]
+
+/* void initWithPath (in AString filePath); */
+NS_IMETHODIMP nsLocalFile::InitWithPath(const nsAString& filePath)
+{
+ return InitWithNativePath(NS_ConvertUTF16toUTF8(filePath));
+}
+
+/* [noscript] void initWithNativePath (in ACString filePath); */
+NS_IMETHODIMP nsLocalFile::InitWithNativePath(const nsACString& filePath)
+{
+ nsCAutoString fixedPath;
+ if (Substring(filePath, 0, 2).EqualsLiteral("~/")) {
+ nsCOMPtr<nsIFile> homeDir;
+ nsCAutoString homePath;
+ nsresult rv = NS_GetSpecialDirectory(NS_OS_HOME_DIR,
+ getter_AddRefs(homeDir));
+ NS_ENSURE_SUCCESS(rv, rv);
+ rv = homeDir->GetNativePath(homePath);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ fixedPath = homePath + Substring(filePath, 1, filePath.Length() - 1);
+ }
+ else if (filePath.IsEmpty() || filePath.First() != '/')
+ return NS_ERROR_FILE_UNRECOGNIZED_PATH;
+ else
+ fixedPath.Assign(filePath);
+
+ // A path with consecutive '/'s which are not between
+ // nodes crashes CFURLGetFSRef(). Consecutive '/'s which
+ // are between actual nodes are OK. So, convert consecutive
+ // '/'s to a single one.
+ fixedPath.ReplaceSubstring("//", "/");
+
+#if 1 // bird: hack to fix RegistryLocationForSpec issues with /path/to/./components
+ fixedPath.ReplaceSubstring("/./", "/");
+ size_t len = fixedPath.Length();
+ while (len > 2)
+ {
+ size_t choplen = 0;
+ if (!strcmp(fixedPath.get() + len - 2, "/."))
+ choplen = 2;
+ else if (!strcmp(fixedPath.get() + len - 1, "/"))
+ choplen = 1;
+ else
+ break;
+ fixedPath = StringHead(fixedPath, len - choplen);
+ }
+ // bird: another hack for the issue with VirtualBoxVM and symlinks...
+ char tmpBuf[PATH_MAX];
+ if (realpath(fixedPath.get(), tmpBuf))
+ fixedPath = tmpBuf;
+#endif
+
+ // On 10.2, huge paths also crash CFURLGetFSRef()
+ if (fixedPath.Length() > PATH_MAX)
+ return NS_ERROR_FILE_NAME_TOO_LONG;
+
+ CFStringRef pathAsCFString;
+ CFURLRef pathAsCFURL;
+
+ pathAsCFString = ::CFStringCreateWithCString(nsnull, fixedPath.get(), kCFStringEncodingUTF8);
+ if (!pathAsCFString)
+ return NS_ERROR_FAILURE;
+ pathAsCFURL = ::CFURLCreateWithFileSystemPath(nsnull, pathAsCFString, kCFURLPOSIXPathStyle, PR_FALSE);
+ if (!pathAsCFURL) {
+ ::CFRelease(pathAsCFString);
+ return NS_ERROR_FAILURE;
+ }
+ SetBaseRef(pathAsCFURL);
+ ::CFRelease(pathAsCFURL);
+ ::CFRelease(pathAsCFString);
+ return NS_OK;
+}
+
+/* void initWithFile (in nsILocalFile aFile); */
+NS_IMETHODIMP nsLocalFile::InitWithFile(nsILocalFile *aFile)
+{
+ NS_ENSURE_ARG(aFile);
+
+ nsCOMPtr<nsILocalFileMac> aFileMac(do_QueryInterface(aFile));
+ if (!aFileMac)
+ return NS_ERROR_UNEXPECTED;
+ CFURLRef urlRef;
+ nsresult rv = aFileMac->GetCFURL(&urlRef);
+ if (NS_FAILED(rv))
+ return rv;
+ rv = InitWithCFURL(urlRef);
+ ::CFRelease(urlRef);
+ return rv;
+}
+
+/* attribute PRBool followLinks; */
+NS_IMETHODIMP nsLocalFile::GetFollowLinks(PRBool *aFollowLinks)
+{
+ NS_ENSURE_ARG_POINTER(aFollowLinks);
+
+ *aFollowLinks = mFollowLinks;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsLocalFile::SetFollowLinks(PRBool aFollowLinks)
+{
+ if (aFollowLinks != mFollowLinks) {
+ mFollowLinks = aFollowLinks;
+ UpdateTargetRef();
+ }
+ return NS_OK;
+}
+
+/* [noscript] PRFileDescStar openNSPRFileDesc (in long flags, in long mode); */
+NS_IMETHODIMP nsLocalFile::OpenNSPRFileDesc(PRInt32 flags, PRInt32 mode, PRFileDesc **_retval)
+{
+ NS_ENSURE_ARG_POINTER(_retval);
+
+ nsCAutoString path;
+ nsresult rv = GetPathInternal(path);
+ if (NS_FAILED(rv))
+ return rv;
+
+ *_retval = PR_Open(path.get(), flags, mode);
+ if (! *_retval)
+ return NS_ErrorAccordingToNSPR();
+
+ return NS_OK;
+}
+
+/* [noscript] FILE openANSIFileDesc (in string mode); */
+NS_IMETHODIMP nsLocalFile::OpenANSIFileDesc(const char *mode, FILE **_retval)
+{
+ NS_ENSURE_ARG_POINTER(_retval);
+
+ nsCAutoString path;
+ nsresult rv = GetPathInternal(path);
+ if (NS_FAILED(rv))
+ return rv;
+
+ *_retval = fopen(path.get(), mode);
+ if (! *_retval)
+ return NS_ERROR_FAILURE;
+
+ return NS_OK;
+}
+
+/* [noscript] PRLibraryStar load (); */
+NS_IMETHODIMP nsLocalFile::Load(PRLibrary **_retval)
+{
+ // Check we are correctly initialized.
+ CHECK_mBaseRef();
+
+ NS_ENSURE_ARG_POINTER(_retval);
+
+ NS_TIMELINE_START_TIMER("PR_LoadLibrary");
+
+ nsCAutoString path;
+ nsresult rv = GetPathInternal(path);
+ if (NS_FAILED(rv))
+ return rv;
+
+#ifdef NS_BUILD_REFCNT_LOGGING
+ nsTraceRefcntImpl::SetActivityIsLegal(PR_FALSE);
+#endif
+
+ *_retval = PR_LoadLibrary(path.get());
+
+#ifdef NS_BUILD_REFCNT_LOGGING
+ nsTraceRefcntImpl::SetActivityIsLegal(PR_TRUE);
+#endif
+
+ NS_TIMELINE_STOP_TIMER("PR_LoadLibrary");
+ NS_TIMELINE_MARK_TIMER1("PR_LoadLibrary", path.get());
+
+ if (!*_retval)
+ return NS_ERROR_FAILURE;
+
+ return NS_OK;
+}
+
+/* readonly attribute PRInt64 diskSpaceAvailable; */
+NS_IMETHODIMP nsLocalFile::GetDiskSpaceAvailable(PRInt64 *aDiskSpaceAvailable)
+{
+ // Check we are correctly initialized.
+ CHECK_mBaseRef();
+
+ NS_ENSURE_ARG_POINTER(aDiskSpaceAvailable);
+
+ FSRef fsRef;
+ nsresult rv = GetFSRefInternal(fsRef);
+ if (NS_FAILED(rv))
+ return rv;
+
+ OSErr err;
+ FSCatalogInfo catalogInfo;
+ err = ::FSGetCatalogInfo(&fsRef, kFSCatInfoVolume, &catalogInfo,
+ nsnull, nsnull, nsnull);
+ if (err != noErr)
+ return MacErrorMapper(err);
+
+ FSVolumeInfo volumeInfo;
+ err = ::FSGetVolumeInfo(catalogInfo.volume, 0, nsnull, kFSVolInfoSizes,
+ &volumeInfo, nsnull, nsnull);
+ if (err != noErr)
+ return MacErrorMapper(err);
+
+ *aDiskSpaceAvailable = volumeInfo.freeBytes;
+ return NS_OK;
+}
+
+/* void appendRelativePath (in AString relativeFilePath); */
+NS_IMETHODIMP nsLocalFile::AppendRelativePath(const nsAString& relativeFilePath)
+{
+ return AppendRelativeNativePath(NS_ConvertUTF16toUTF8(relativeFilePath));
+}
+
+/* [noscript] void appendRelativeNativePath (in ACString relativeFilePath); */
+NS_IMETHODIMP nsLocalFile::AppendRelativeNativePath(const nsACString& relativeFilePath)
+{
+ if (relativeFilePath.IsEmpty())
+ return NS_OK;
+ // No leading '/'
+ if (relativeFilePath.First() == '/')
+ return NS_ERROR_FILE_UNRECOGNIZED_PATH;
+
+ // Parse the nodes and call Append() for each
+ nsACString::const_iterator nodeBegin, pathEnd;
+ relativeFilePath.BeginReading(nodeBegin);
+ relativeFilePath.EndReading(pathEnd);
+ nsACString::const_iterator nodeEnd(nodeBegin);
+
+ while (nodeEnd != pathEnd) {
+ FindCharInReadable(kPathSepChar, nodeEnd, pathEnd);
+ nsresult rv = AppendNative(Substring(nodeBegin, nodeEnd));
+ if (NS_FAILED(rv))
+ return rv;
+ if (nodeEnd != pathEnd) // If there's more left in the string, inc over the '/' nodeEnd is on.
+ ++nodeEnd;
+ nodeBegin = nodeEnd;
+ }
+ return NS_OK;
+}
+
+/* attribute ACString persistentDescriptor; */
+NS_IMETHODIMP nsLocalFile::GetPersistentDescriptor(nsACString& aPersistentDescriptor)
+{
+ FSRef fsRef;
+ nsresult rv = GetFSRefInternal(fsRef);
+ if (NS_FAILED(rv))
+ return rv;
+
+ AliasHandle aliasH;
+ OSErr err = ::FSNewAlias(nsnull, &fsRef, &aliasH);
+ if (err != noErr)
+ return MacErrorMapper(err);
+
+ PRUint32 bytes = ::GetHandleSize((Handle) aliasH);
+ ::HLock((Handle) aliasH);
+ // Passing nsnull for dest makes NULL-term string
+ char* buf = PL_Base64Encode((const char*)*aliasH, bytes, nsnull);
+ ::DisposeHandle((Handle) aliasH);
+ NS_ENSURE_TRUE(buf, NS_ERROR_OUT_OF_MEMORY);
+
+ aPersistentDescriptor = buf;
+ PR_Free(buf);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsLocalFile::SetPersistentDescriptor(const nsACString& aPersistentDescriptor)
+{
+ if (aPersistentDescriptor.IsEmpty())
+ return NS_ERROR_INVALID_ARG;
+
+ // Support pathnames as user-supplied descriptors if they begin with '/'
+ // or '~'. These characters do not collide with the base64 set used for
+ // encoding alias records.
+ char first = aPersistentDescriptor.First();
+ if (first == '/' || first == '~')
+ return InitWithNativePath(aPersistentDescriptor);
+
+ nsresult rv = NS_OK;
+
+ PRUint32 dataSize = aPersistentDescriptor.Length();
+ char* decodedData = PL_Base64Decode(PromiseFlatCString(aPersistentDescriptor).get(), dataSize, nsnull);
+ if (!decodedData) {
+ NS_ERROR("SetPersistentDescriptor was given bad data");
+ return NS_ERROR_FAILURE;
+ }
+
+ // Cast to an alias record and resolve.
+ AliasRecord aliasHeader = *(AliasPtr)decodedData;
+ PRInt32 aliasSize = GetAliasSizeFromRecord(aliasHeader);
+ if (aliasSize > ((PRInt32)dataSize * 3) / 4) { // be paranoid about having too few data
+ PR_Free(decodedData);
+ return NS_ERROR_FAILURE;
+ }
+
+ // Move the now-decoded data into the Handle.
+ // The size of the decoded data is 3/4 the size of the encoded data. See plbase64.h
+ Handle newHandle = nsnull;
+ if (::PtrToHand(decodedData, &newHandle, aliasSize) != noErr)
+ rv = NS_ERROR_OUT_OF_MEMORY;
+ PR_Free(decodedData);
+ if (NS_FAILED(rv))
+ return rv;
+
+ Boolean changed;
+ FSRef resolvedFSRef;
+ OSErr err = ::FSResolveAlias(nsnull, (AliasHandle)newHandle, &resolvedFSRef, &changed);
+
+ rv = MacErrorMapper(err);
+ DisposeHandle(newHandle);
+ if (NS_FAILED(rv))
+ return rv;
+
+ return InitWithFSRef(&resolvedFSRef);
+}
+
+/* void reveal (); */
+NS_IMETHODIMP nsLocalFile::Reveal()
+{
+ FSRef fsRefToReveal;
+ AppleEvent aeEvent = {0, nil};
+ AppleEvent aeReply = {0, nil};
+ StAEDesc aeDirDesc, listElem, myAddressDesc, fileList;
+ OSErr err;
+ ProcessSerialNumber process;
+
+ nsresult rv = GetFSRefInternal(fsRefToReveal);
+ if (NS_FAILED(rv))
+ return rv;
+
+ err = ::FindRunningAppBySignature ('MACS', process);
+ if (err == noErr) {
+ err = ::AECreateDesc(typeProcessSerialNumber, (Ptr)&process, sizeof(process), &myAddressDesc);
+ if (err == noErr) {
+ // Create the FinderEvent
+ err = ::AECreateAppleEvent(kAEMiscStandards, kAEMakeObjectsVisible, &myAddressDesc,
+ kAutoGenerateReturnID, kAnyTransactionID, &aeEvent);
+ if (err == noErr) {
+ // Create the file list
+ err = ::AECreateList(nil, 0, false, &fileList);
+ if (err == noErr) {
+ FSSpec fsSpecToReveal;
+ err = ::FSRefMakeFSSpec(&fsRefToReveal, &fsSpecToReveal);
+ if (err == noErr) {
+ err = ::AEPutPtr(&fileList, 0, typeFSS, &fsSpecToReveal, sizeof(FSSpec));
+ if (err == noErr) {
+ err = ::AEPutParamDesc(&aeEvent, keyDirectObject, &fileList);
+ if (err == noErr) {
+ err = ::AESend(&aeEvent, &aeReply, kAENoReply, kAENormalPriority, kAEDefaultTimeout, nil, nil);
+ if (err == noErr)
+ ::SetFrontProcess(&process);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return NS_OK;
+}
+
+/* void launch (); */
+NS_IMETHODIMP nsLocalFile::Launch()
+{
+ FSRef fsRef;
+ nsresult rv = GetFSRefInternal(fsRef);
+ if (NS_FAILED(rv))
+ return rv;
+
+ OSErr err = ::LSOpenFSRef(&fsRef, NULL);
+ return MacErrorMapper(err);
+}
+
+
+//*****************************************************************************
+// nsLocalFile::nsILocalFileMac
+//*****************************************************************************
+#pragma mark -
+#pragma mark [nsILocalFileMac]
+
+/* void initWithCFURL (in CFURLRef aCFURL); */
+NS_IMETHODIMP nsLocalFile::InitWithCFURL(CFURLRef aCFURL)
+{
+ NS_ENSURE_ARG(aCFURL);
+
+ SetBaseRef(aCFURL);
+ return NS_OK;
+}
+
+/* void initWithFSRef ([const] in FSRefPtr aFSRef); */
+NS_IMETHODIMP nsLocalFile::InitWithFSRef(const FSRef *aFSRef)
+{
+ NS_ENSURE_ARG(aFSRef);
+ nsresult rv = NS_ERROR_FAILURE;
+
+ CFURLRef newURLRef = ::CFURLCreateFromFSRef(kCFAllocatorDefault, aFSRef);
+ if (newURLRef) {
+ SetBaseRef(newURLRef);
+ ::CFRelease(newURLRef);
+ rv = NS_OK;
+ }
+ return rv;
+}
+
+/* void initWithFSSpec ([const] in FSSpecPtr aFileSpec); */
+NS_IMETHODIMP nsLocalFile::InitWithFSSpec(const FSSpec *aFileSpec)
+{
+ NS_ENSURE_ARG(aFileSpec);
+
+ FSRef fsRef;
+ OSErr err = ::FSpMakeFSRef(aFileSpec, &fsRef);
+ if (err == noErr)
+ return InitWithFSRef(&fsRef);
+ else if (err == fnfErr) {
+ CInfoPBRec pBlock;
+ FSSpec parentDirSpec;
+
+ memset(&pBlock, 0, sizeof(CInfoPBRec));
+ parentDirSpec.name[0] = 0;
+ pBlock.dirInfo.ioVRefNum = aFileSpec->vRefNum;
+ pBlock.dirInfo.ioDrDirID = aFileSpec->parID;
+ pBlock.dirInfo.ioNamePtr = (StringPtr)parentDirSpec.name;
+ pBlock.dirInfo.ioFDirIndex = -1; //get info on parID
+ err = ::PBGetCatInfoSync(&pBlock);
+ if (err != noErr)
+ return MacErrorMapper(err);
+
+ parentDirSpec.vRefNum = aFileSpec->vRefNum;
+ parentDirSpec.parID = pBlock.dirInfo.ioDrParID;
+ err = ::FSpMakeFSRef(&parentDirSpec, &fsRef);
+ if (err != noErr)
+ return MacErrorMapper(err);
+ HFSUniStr255 unicodeName;
+ err = ::HFSNameGetUnicodeName(aFileSpec->name, kTextEncodingUnknown, &unicodeName);
+ if (err != noErr)
+ return MacErrorMapper(err);
+ nsresult rv = InitWithFSRef(&fsRef);
+ if (NS_FAILED(rv))
+ return rv;
+ return Append(nsDependentString(unicodeName.unicode, unicodeName.length));
+ }
+ return MacErrorMapper(err);
+}
+
+/* void initToAppWithCreatorCode (in OSType aAppCreator); */
+NS_IMETHODIMP nsLocalFile::InitToAppWithCreatorCode(OSType aAppCreator)
+{
+ FSRef fsRef;
+ OSErr err = ::LSFindApplicationForInfo(aAppCreator, nsnull, nsnull, &fsRef, nsnull);
+ if (err != noErr)
+ return MacErrorMapper(err);
+ return InitWithFSRef(&fsRef);
+}
+
+/* CFURLRef getCFURL (); */
+NS_IMETHODIMP nsLocalFile::GetCFURL(CFURLRef *_retval)
+{
+ NS_ENSURE_ARG_POINTER(_retval);
+ CFURLRef whichURLRef = mFollowLinks ? mTargetRef : mBaseRef;
+ if (whichURLRef)
+ ::CFRetain(whichURLRef);
+ *_retval = whichURLRef;
+ return whichURLRef ? NS_OK : NS_ERROR_FAILURE;
+}
+
+/* FSRef getFSRef (); */
+NS_IMETHODIMP nsLocalFile::GetFSRef(FSRef *_retval)
+{
+ NS_ENSURE_ARG_POINTER(_retval);
+ return GetFSRefInternal(*_retval);
+}
+
+/* FSSpec getFSSpec (); */
+NS_IMETHODIMP nsLocalFile::GetFSSpec(FSSpec *_retval)
+{
+ NS_ENSURE_ARG_POINTER(_retval);
+
+ // Check we are correctly initialized.
+ CHECK_mBaseRef();
+
+ OSErr err;
+ FSRef fsRef;
+ nsresult rv = GetFSRefInternal(fsRef);
+ if (NS_SUCCEEDED(rv)) {
+ // If the leaf node exists, things are simple.
+ err = ::FSGetCatalogInfo(&fsRef, kFSCatInfoNone,
+ nsnull, nsnull, _retval, nsnull);
+ return MacErrorMapper(err);
+ }
+ else if (rv == NS_ERROR_FILE_NOT_FOUND) {
+ // If the parent of the leaf exists, make an FSSpec from that.
+ CFURLRef parentURLRef = ::CFURLCreateCopyDeletingLastPathComponent(kCFAllocatorDefault, mBaseRef);
+ if (!parentURLRef)
+ return NS_ERROR_FAILURE;
+
+ err = fnfErr;
+ if (::CFURLGetFSRef(parentURLRef, &fsRef)) {
+ FSCatalogInfo catalogInfo;
+ if ((err = ::FSGetCatalogInfo(&fsRef,
+ kFSCatInfoVolume + kFSCatInfoNodeID + kFSCatInfoTextEncoding,
+ &catalogInfo, nsnull, nsnull, nsnull)) == noErr) {
+ nsAutoString leafName;
+ if (NS_SUCCEEDED(GetLeafName(leafName))) {
+ Str31 hfsName;
+ if ((err = ::UnicodeNameGetHFSName(leafName.Length(),
+ leafName.get(),
+ catalogInfo.textEncodingHint,
+ catalogInfo.nodeID == fsRtDirID,
+ hfsName)) == noErr)
+ err = ::FSMakeFSSpec(catalogInfo.volume, catalogInfo.nodeID, hfsName, _retval);
+ }
+ }
+ }
+ ::CFRelease(parentURLRef);
+ rv = MacErrorMapper(err);
+ }
+ return rv;
+}
+
+/* readonly attribute PRInt64 fileSizeWithResFork; */
+NS_IMETHODIMP nsLocalFile::GetFileSizeWithResFork(PRInt64 *aFileSizeWithResFork)
+{
+ NS_ENSURE_ARG_POINTER(aFileSizeWithResFork);
+
+ FSRef fsRef;
+ nsresult rv = GetFSRefInternal(fsRef);
+ if (NS_FAILED(rv))
+ return rv;
+
+ FSCatalogInfo catalogInfo;
+ OSErr err = ::FSGetCatalogInfo(&fsRef, kFSCatInfoDataSizes + kFSCatInfoRsrcSizes,
+ &catalogInfo, nsnull, nsnull, nsnull);
+ if (err != noErr)
+ return MacErrorMapper(err);
+
+ *aFileSizeWithResFork = catalogInfo.dataLogicalSize + catalogInfo.rsrcLogicalSize;
+ return NS_OK;
+}
+
+/* attribute OSType fileType; */
+NS_IMETHODIMP nsLocalFile::GetFileType(OSType *aFileType)
+{
+ NS_ENSURE_ARG_POINTER(aFileType);
+
+ FSRef fsRef;
+ nsresult rv = GetFSRefInternal(fsRef);
+ if (NS_FAILED(rv))
+ return rv;
+
+ FinderInfo fInfo;
+ OSErr err = ::FSGetFinderInfo(&fsRef, &fInfo, nsnull, nsnull);
+ if (err != noErr)
+ return MacErrorMapper(err);
+ *aFileType = fInfo.file.fileType;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsLocalFile::SetFileType(OSType aFileType)
+{
+ FSRef fsRef;
+ nsresult rv = GetFSRefInternal(fsRef);
+ if (NS_FAILED(rv))
+ return rv;
+
+ OSErr err = ::FSChangeCreatorType(&fsRef, 0, aFileType);
+ return MacErrorMapper(err);
+}
+
+/* attribute OSType fileCreator; */
+NS_IMETHODIMP nsLocalFile::GetFileCreator(OSType *aFileCreator)
+{
+ NS_ENSURE_ARG_POINTER(aFileCreator);
+
+ FSRef fsRef;
+ nsresult rv = GetFSRefInternal(fsRef);
+ if (NS_FAILED(rv))
+ return rv;
+
+ FinderInfo fInfo;
+ OSErr err = ::FSGetFinderInfo(&fsRef, &fInfo, nsnull, nsnull);
+ if (err != noErr)
+ return MacErrorMapper(err);
+ *aFileCreator = fInfo.file.fileCreator;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsLocalFile::SetFileCreator(OSType aFileCreator)
+{
+ FSRef fsRef;
+ nsresult rv = GetFSRefInternal(fsRef);
+ if (NS_FAILED(rv))
+ return rv;
+
+ OSErr err = ::FSChangeCreatorType(&fsRef, aFileCreator, 0);
+ return MacErrorMapper(err);
+}
+
+/* void setFileTypeAndCreatorFromMIMEType (in string aMIMEType); */
+NS_IMETHODIMP nsLocalFile::SetFileTypeAndCreatorFromMIMEType(const char *aMIMEType)
+{
+ // XXX - This should be cut from the API. Would create an evil dependency.
+ NS_ERROR("NS_ERROR_NOT_IMPLEMENTED");
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+/* void setFileTypeAndCreatorFromExtension (in string aExtension); */
+NS_IMETHODIMP nsLocalFile::SetFileTypeAndCreatorFromExtension(const char *aExtension)
+{
+ // XXX - This should be cut from the API. Would create an evil dependency.
+ NS_ERROR("NS_ERROR_NOT_IMPLEMENTED");
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+/* void launchWithDoc (in nsILocalFile aDocToLoad, in boolean aLaunchInBackground); */
+NS_IMETHODIMP nsLocalFile::LaunchWithDoc(nsILocalFile *aDocToLoad, PRBool aLaunchInBackground)
+{
+ PRBool isExecutable;
+ nsresult rv = IsExecutable(&isExecutable);
+ if (NS_FAILED(rv))
+ return rv;
+ if (!isExecutable)
+ return NS_ERROR_FILE_EXECUTION_FAILED;
+
+ FSRef appFSRef, docFSRef;
+ rv = GetFSRefInternal(appFSRef);
+ if (NS_FAILED(rv))
+ return rv;
+
+ if (aDocToLoad) {
+ nsCOMPtr<nsILocalFileMac> macDoc = do_QueryInterface(aDocToLoad);
+ rv = macDoc->GetFSRef(&docFSRef);
+ if (NS_FAILED(rv))
+ return rv;
+ }
+
+ LSLaunchFlags theLaunchFlags = kLSLaunchDefaults;
+ LSLaunchFSRefSpec thelaunchSpec;
+
+ if (aLaunchInBackground)
+ theLaunchFlags |= kLSLaunchDontSwitch;
+ memset(&thelaunchSpec, 0, sizeof(LSLaunchFSRefSpec));
+
+ thelaunchSpec.appRef = &appFSRef;
+ if (aDocToLoad) {
+ thelaunchSpec.numDocs = 1;
+ thelaunchSpec.itemRefs = &docFSRef;
+ }
+ thelaunchSpec.launchFlags = theLaunchFlags;
+
+ OSErr err = ::LSOpenFromRefSpec(&thelaunchSpec, NULL);
+ if (err != noErr)
+ return MacErrorMapper(err);
+
+ return NS_OK;
+}
+
+/* void openDocWithApp (in nsILocalFile aAppToOpenWith, in boolean aLaunchInBackground); */
+NS_IMETHODIMP nsLocalFile::OpenDocWithApp(nsILocalFile *aAppToOpenWith, PRBool aLaunchInBackground)
+{
+ nsresult rv;
+ OSErr err;
+
+ FSRef docFSRef, appFSRef;
+ rv = GetFSRefInternal(docFSRef);
+ if (NS_FAILED(rv))
+ return rv;
+
+ if (aAppToOpenWith) {
+ nsCOMPtr<nsILocalFileMac> appFileMac = do_QueryInterface(aAppToOpenWith, &rv);
+ if (!appFileMac)
+ return rv;
+
+ PRBool isExecutable;
+ rv = appFileMac->IsExecutable(&isExecutable);
+ if (NS_FAILED(rv))
+ return rv;
+ if (!isExecutable)
+ return NS_ERROR_FILE_EXECUTION_FAILED;
+
+ rv = appFileMac->GetFSRef(&appFSRef);
+ if (NS_FAILED(rv))
+ return rv;
+ }
+ else {
+ OSType fileCreator;
+ rv = GetFileCreator(&fileCreator);
+ if (NS_FAILED(rv))
+ return rv;
+
+ err = ::LSFindApplicationForInfo(fileCreator, nsnull, nsnull, &appFSRef, nsnull);
+ if (err != noErr)
+ return MacErrorMapper(err);
+ }
+
+ LSLaunchFlags theLaunchFlags = kLSLaunchDefaults;
+ LSLaunchFSRefSpec thelaunchSpec;
+
+ if (aLaunchInBackground)
+ theLaunchFlags |= kLSLaunchDontSwitch;
+ memset(&thelaunchSpec, 0, sizeof(LSLaunchFSRefSpec));
+
+ thelaunchSpec.appRef = &appFSRef;
+ thelaunchSpec.numDocs = 1;
+ thelaunchSpec.itemRefs = &docFSRef;
+ thelaunchSpec.launchFlags = theLaunchFlags;
+
+ err = ::LSOpenFromRefSpec(&thelaunchSpec, NULL);
+ if (err != noErr)
+ return MacErrorMapper(err);
+
+ return NS_OK;
+}
+
+/* boolean isPackage (); */
+NS_IMETHODIMP nsLocalFile::IsPackage(PRBool *_retval)
+{
+ NS_ENSURE_ARG(_retval);
+ *_retval = PR_FALSE;
+
+ FSRef fsRef;
+ nsresult rv = GetFSRefInternal(fsRef);
+ if (NS_FAILED(rv))
+ return rv;
+
+ FSCatalogInfo catalogInfo;
+ OSErr err = ::FSGetCatalogInfo(&fsRef, kFSCatInfoNodeFlags + kFSCatInfoFinderInfo,
+ &catalogInfo, nsnull, nsnull, nsnull);
+ if (err != noErr)
+ return MacErrorMapper(err);
+ if ((catalogInfo.nodeFlags & kFSNodeIsDirectoryMask) != 0) {
+ FileInfo *fInfoPtr = (FileInfo *)(catalogInfo.finderInfo);
+ if ((fInfoPtr->finderFlags & kHasBundle) != 0) {
+ *_retval = PR_TRUE;
+ }
+ else {
+ // Folders ending with ".app" are also considered to
+ // be packages, even if the top-level folder doesn't have bundle set
+ nsCAutoString name;
+ if (NS_SUCCEEDED(rv = GetNativeLeafName(name))) {
+ const char *extPtr = strrchr(name.get(), '.');
+ if (extPtr) {
+ if ((nsCRT::strcasecmp(extPtr, ".app") == 0))
+ *_retval = PR_TRUE;
+ }
+ }
+ }
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::GetBundleDisplayName(nsAString& outBundleName)
+{
+ PRBool isPackage = PR_FALSE;
+ nsresult rv = IsPackage(&isPackage);
+ if (NS_FAILED(rv) || !isPackage)
+ return NS_ERROR_FAILURE;
+
+ nsAutoString name;
+ rv = GetLeafName(name);
+ if (NS_FAILED(rv))
+ return rv;
+
+ PRInt32 length = name.Length();
+ if (Substring(name, length - 4, length).EqualsLiteral(".app")) {
+ // 4 characters in ".app"
+ outBundleName = Substring(name, 0, length - 4);
+ }
+ else
+ outBundleName = name;
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::GetBundleIdentifier(nsACString& outBundleIdentifier)
+{
+ nsresult rv = NS_ERROR_FAILURE;
+
+ CFURLRef urlRef;
+ if (NS_SUCCEEDED(GetCFURL(&urlRef))) {
+ CFBundleRef bundle = ::CFBundleCreate(NULL, urlRef);
+ if (bundle) {
+ CFStringRef bundleIdentifier = ::CFBundleGetIdentifier(bundle);
+ if (bundleIdentifier)
+ rv = CFStringReftoUTF8(bundleIdentifier, outBundleIdentifier);
+
+ ::CFRelease(bundle);
+ }
+ ::CFRelease(urlRef);
+ }
+
+ return rv;
+}
+
+
+//*****************************************************************************
+// nsLocalFile Methods
+//*****************************************************************************
+#pragma mark -
+#pragma mark [Protected Methods]
+
+nsresult nsLocalFile::SetBaseRef(CFURLRef aCFURLRef)
+{
+ NS_ENSURE_ARG(aCFURLRef);
+
+ ::CFRetain(aCFURLRef);
+ if (mBaseRef)
+ ::CFRelease(mBaseRef);
+ mBaseRef = aCFURLRef;
+
+ mFollowLinksDirty = PR_TRUE;
+ UpdateTargetRef();
+ mCachedFSRefValid = PR_FALSE;
+ return NS_OK;
+}
+
+nsresult nsLocalFile::UpdateTargetRef()
+{
+ // Check we are correctly initialized.
+ CHECK_mBaseRef();
+
+ if (mFollowLinksDirty) {
+ if (mTargetRef) {
+ ::CFRelease(mTargetRef);
+ mTargetRef = nsnull;
+ }
+ if (mFollowLinks) {
+ mTargetRef = mBaseRef;
+ ::CFRetain(mTargetRef);
+
+ FSRef fsRef;
+ if (::CFURLGetFSRef(mBaseRef, &fsRef)) {
+ Boolean targetIsFolder, wasAliased;
+ if (FSResolveAliasFile(&fsRef, true /*resolveAliasChains*/,
+ &targetIsFolder, &wasAliased) == noErr && wasAliased) {
+ ::CFRelease(mTargetRef);
+ mTargetRef = CFURLCreateFromFSRef(NULL, &fsRef);
+ if (!mTargetRef)
+ return NS_ERROR_FAILURE;
+ }
+ }
+ mFollowLinksDirty = PR_FALSE;
+ }
+ }
+ return NS_OK;
+}
+
+nsresult nsLocalFile::GetFSRefInternal(FSRef& aFSRef, PRBool bForceUpdateCache)
+{
+ if (bForceUpdateCache || !mCachedFSRefValid) {
+ mCachedFSRefValid = PR_FALSE;
+ CFURLRef whichURLRef = mFollowLinks ? mTargetRef : mBaseRef;
+ NS_ENSURE_TRUE(whichURLRef, NS_ERROR_NULL_POINTER);
+ if (::CFURLGetFSRef(whichURLRef, &mCachedFSRef))
+ mCachedFSRefValid = PR_TRUE;
+ }
+ if (mCachedFSRefValid) {
+ aFSRef = mCachedFSRef;
+ return NS_OK;
+ }
+ // CFURLGetFSRef only returns a Boolean for success,
+ // so we have to assume what the error was. This is
+ // the only probable cause.
+ return NS_ERROR_FILE_NOT_FOUND;
+}
+
+nsresult nsLocalFile::GetPathInternal(nsACString& path)
+{
+ nsresult rv = NS_ERROR_FAILURE;
+
+ CFURLRef whichURLRef = mFollowLinks ? mTargetRef : mBaseRef;
+ NS_ENSURE_TRUE(whichURLRef, NS_ERROR_NULL_POINTER);
+
+ CFStringRef pathStrRef = ::CFURLCopyFileSystemPath(whichURLRef, kCFURLPOSIXPathStyle);
+ if (pathStrRef) {
+ rv = CFStringReftoUTF8(pathStrRef, path);
+ ::CFRelease(pathStrRef);
+ }
+ return rv;
+}
+
+nsresult nsLocalFile::CopyInternal(nsIFile* aParentDir,
+ const nsAString& newName,
+ PRBool followLinks)
+{
+ // Check we are correctly initialized.
+ CHECK_mBaseRef();
+
+ StFollowLinksState srcFollowState(*this, followLinks);
+
+ nsresult rv;
+ OSErr err;
+ FSRef srcFSRef, newFSRef;
+
+ rv = GetFSRefInternal(srcFSRef);
+ if (NS_FAILED(rv))
+ return rv;
+
+ nsCOMPtr<nsIFile> newParentDir = aParentDir;
+
+ if (!newParentDir) {
+ if (newName.IsEmpty())
+ return NS_ERROR_INVALID_ARG;
+ rv = GetParent(getter_AddRefs(newParentDir));
+ if (NS_FAILED(rv))
+ return rv;
+ }
+
+ // If newParentDir does not exist, create it
+ PRBool exists;
+ rv = newParentDir->Exists(&exists);
+ if (NS_FAILED(rv))
+ return rv;
+ if (!exists) {
+ rv = newParentDir->Create(nsIFile::DIRECTORY_TYPE, 0777);
+ if (NS_FAILED(rv))
+ return rv;
+ }
+
+ FSRef destFSRef;
+ nsCOMPtr<nsILocalFileMac> newParentDirMac(do_QueryInterface(newParentDir));
+ if (!newParentDirMac)
+ return NS_ERROR_NO_INTERFACE;
+ rv = newParentDirMac->GetFSRef(&destFSRef);
+ if (NS_FAILED(rv))
+ return rv;
+
+ err =
+ ::FSCopyObject(&srcFSRef, &destFSRef, newName.Length(),
+ newName.Length() ? PromiseFlatString(newName).get() : NULL,
+ 0, kFSCatInfoNone, false, false, NULL, NULL, &newFSRef);
+
+ return MacErrorMapper(err);
+}
+
+const PRInt64 kMillisecsPerSec = 1000LL;
+const PRInt64 kUTCDateTimeFractionDivisor = 65535LL;
+
+PRInt64 nsLocalFile::HFSPlustoNSPRTime(const UTCDateTime& utcTime)
+{
+ // Start with seconds since Jan. 1, 1904 GMT
+ PRInt64 result = ((PRInt64)utcTime.highSeconds << 32) + (PRInt64)utcTime.lowSeconds;
+ // Subtract to convert to NSPR epoch of 1970
+ result -= kJanuaryFirst1970Seconds;
+ // Convert to millisecs
+ result *= kMillisecsPerSec;
+ // Convert the fraction to millisecs and add it
+ result += ((PRInt64)utcTime.fraction * kMillisecsPerSec) / kUTCDateTimeFractionDivisor;
+
+ return result;
+}
+
+void nsLocalFile::NSPRtoHFSPlusTime(PRInt64 nsprTime, UTCDateTime& utcTime)
+{
+ PRInt64 fraction = nsprTime % kMillisecsPerSec;
+ PRInt64 seconds = (nsprTime / kMillisecsPerSec) + kJanuaryFirst1970Seconds;
+ utcTime.highSeconds = (UInt16)((PRUint64)seconds >> 32);
+ utcTime.lowSeconds = (UInt32)seconds;
+ utcTime.fraction = (UInt16)((fraction * kUTCDateTimeFractionDivisor) / kMillisecsPerSec);
+}
+
+nsresult nsLocalFile::CFStringReftoUTF8(CFStringRef aInStrRef, nsACString& aOutStr)
+{
+ nsresult rv = NS_ERROR_FAILURE;
+ CFIndex usedBufLen, inStrLen = ::CFStringGetLength(aInStrRef);
+ CFIndex charsConverted = ::CFStringGetBytes(aInStrRef, CFRangeMake(0, inStrLen),
+ kCFStringEncodingUTF8, 0, PR_FALSE, nsnull, 0, &usedBufLen);
+ if (charsConverted == inStrLen) {
+#if 0 /* bird: too new? */
+ aOutStr.SetLength(usedBufLen);
+ if (aOutStr.Length() != (unsigned int)usedBufLen)
+ return NS_ERROR_OUT_OF_MEMORY;
+ UInt8 *buffer = (UInt8*) aOutStr.BeginWriting();
+
+ ::CFStringGetBytes(aInStrRef, CFRangeMake(0, inStrLen),
+ kCFStringEncodingUTF8, 0, false, buffer, usedBufLen, &usedBufLen);
+ rv = NS_OK;
+#else
+ nsAutoBuffer<UInt8, FILENAME_BUFFER_SIZE> buffer;
+ if (buffer.EnsureElemCapacity(usedBufLen + 1)) {
+ ::CFStringGetBytes(aInStrRef, CFRangeMake(0, inStrLen),
+ kCFStringEncodingUTF8, 0, false, buffer.get(), usedBufLen, &usedBufLen);
+ buffer.get()[usedBufLen] = '\0';
+ aOutStr.Assign(nsDependentCString((char*)buffer.get()));
+ rv = NS_OK;
+ }
+#endif
+ }
+ return rv;
+}
+
+// nsIHashable
+
+NS_IMETHODIMP
+nsLocalFile::Equals(nsIHashable* aOther, PRBool *aResult)
+{
+ return EqualsInternal(aOther, PR_FALSE, aResult);
+}
+
+NS_IMETHODIMP
+nsLocalFile::GetHashCode(PRUint32 *aResult)
+{
+ CFStringRef pathStrRef = ::CFURLCopyFileSystemPath(mBaseRef, kCFURLPOSIXPathStyle);
+ nsCAutoString path;
+ CFStringReftoUTF8(pathStrRef, path);
+ ::CFRelease(pathStrRef);
+ *aResult = HashString(path);
+ return NS_OK;
+}
+
+//*****************************************************************************
+// Global Functions
+//*****************************************************************************
+#pragma mark -
+#pragma mark [Global Functions]
+
+void nsLocalFile::GlobalInit()
+{
+}
+
+void nsLocalFile::GlobalShutdown()
+{
+}
+
+nsresult NS_NewLocalFile(const nsAString& path, PRBool followLinks, nsILocalFile* *result)
+{
+ nsLocalFile* file = new nsLocalFile;
+ if (file == nsnull)
+ return NS_ERROR_OUT_OF_MEMORY;
+ NS_ADDREF(file);
+
+ file->SetFollowLinks(followLinks);
+
+ if (!path.IsEmpty()) {
+ nsresult rv = file->InitWithPath(path);
+ if (NS_FAILED(rv)) {
+ NS_RELEASE(file);
+ return rv;
+ }
+ }
+ *result = file;
+ return NS_OK;
+}
+
+nsresult NS_NewNativeLocalFile(const nsACString& path, PRBool followLinks, nsILocalFile **result)
+{
+ return NS_NewLocalFile(NS_ConvertUTF8toUTF16(path), followLinks, result);
+}
+
+nsresult NS_NewLocalFileWithFSSpec(const FSSpec* inSpec, PRBool followLinks, nsILocalFileMac **result)
+{
+ nsLocalFile* file = new nsLocalFile();
+ if (file == nsnull)
+ return NS_ERROR_OUT_OF_MEMORY;
+ NS_ADDREF(file);
+
+ file->SetFollowLinks(followLinks);
+
+ nsresult rv = file->InitWithFSSpec(inSpec);
+ if (NS_FAILED(rv)) {
+ NS_RELEASE(file);
+ return rv;
+ }
+ *result = file;
+ return NS_OK;
+}
+
+nsresult NS_NewLocalFileWithFSRef(const FSRef* aFSRef, PRBool aFollowLinks, nsILocalFileMac** result)
+{
+ nsLocalFile* file = new nsLocalFile();
+ if (file == nsnull)
+ return NS_ERROR_OUT_OF_MEMORY;
+ NS_ADDREF(file);
+
+ file->SetFollowLinks(aFollowLinks);
+
+ nsresult rv = file->InitWithFSRef(aFSRef);
+ if (NS_FAILED(rv)) {
+ NS_RELEASE(file);
+ return rv;
+ }
+ *result = file;
+ return NS_OK;
+}
+
+//*****************************************************************************
+// Static Functions
+//*****************************************************************************
+
+static nsresult MacErrorMapper(OSErr inErr)
+{
+ nsresult outErr;
+
+ switch (inErr)
+ {
+ case noErr:
+ outErr = NS_OK;
+ break;
+
+ case fnfErr:
+ case afpObjectNotFound:
+ case afpDirNotFound:
+ outErr = NS_ERROR_FILE_NOT_FOUND;
+ break;
+
+ case dupFNErr:
+ case afpObjectExists:
+ outErr = NS_ERROR_FILE_ALREADY_EXISTS;
+ break;
+
+ case dskFulErr:
+ case afpDiskFull:
+ outErr = NS_ERROR_FILE_DISK_FULL;
+ break;
+
+ case fLckdErr:
+ case afpVolLocked:
+ outErr = NS_ERROR_FILE_IS_LOCKED;
+ break;
+
+ case afpAccessDenied:
+ outErr = NS_ERROR_FILE_ACCESS_DENIED;
+ break;
+
+ case afpDirNotEmpty:
+ outErr = NS_ERROR_FILE_DIR_NOT_EMPTY;
+ break;
+
+ // Can't find good map for some
+ case bdNamErr:
+ outErr = NS_ERROR_FAILURE;
+ break;
+
+ default:
+ outErr = NS_ERROR_FAILURE;
+ break;
+ }
+ return outErr;
+}
+
+static OSErr FindRunningAppBySignature(OSType aAppSig, ProcessSerialNumber& outPsn)
+{
+ ProcessInfoRec info;
+ OSErr err = noErr;
+
+ outPsn.highLongOfPSN = 0;
+ outPsn.lowLongOfPSN = kNoProcess;
+
+ while (PR_TRUE)
+ {
+ err = ::GetNextProcess(&outPsn);
+ if (err == procNotFound)
+ break;
+ if (err != noErr)
+ return err;
+ info.processInfoLength = sizeof(ProcessInfoRec);
+ info.processName = nil;
+ info.processAppSpec = nil;
+ err = ::GetProcessInformation(&outPsn, &info);
+ if (err != noErr)
+ return err;
+
+ if (info.processSignature == aAppSig)
+ return noErr;
+ }
+ return procNotFound;
+}
+
+// Convert a UTF-8 string to a UTF-16 string while normalizing to
+// Normalization Form C (composed Unicode). We need this because
+// Mac OS X file system uses NFD (Normalization Form D : decomposed Unicode)
+// while most other OS', server-side programs usually expect NFC.
+
+typedef void (*UnicodeNormalizer) (CFMutableStringRef, CFStringNormalizationForm);
+static void CopyUTF8toUTF16NFC(const nsACString& aSrc, nsAString& aResult)
+{
+ static PRBool sChecked = PR_FALSE;
+ static UnicodeNormalizer sUnicodeNormalizer = NULL;
+
+ // CFStringNormalize was not introduced until Mac OS 10.2
+ if (!sChecked) {
+ CFBundleRef carbonBundle =
+ CFBundleGetBundleWithIdentifier(CFSTR("com.apple.Carbon"));
+ if (carbonBundle)
+ sUnicodeNormalizer = (UnicodeNormalizer)
+ ::CFBundleGetFunctionPointerForName(carbonBundle,
+ CFSTR("CFStringNormalize"));
+ sChecked = PR_TRUE;
+ }
+
+ if (!sUnicodeNormalizer) { // OS X 10.2 or earlier
+ CopyUTF8toUTF16(aSrc, aResult);
+ return;
+ }
+
+ const nsAFlatCString &inFlatSrc = PromiseFlatCString(aSrc);
+
+ // The number of 16bit code units in a UTF-16 string will never be
+ // larger than the number of bytes in the corresponding UTF-8 string.
+ CFMutableStringRef inStr =
+ ::CFStringCreateMutable(NULL, inFlatSrc.Length());
+
+ if (!inStr) {
+ CopyUTF8toUTF16(aSrc, aResult);
+ return;
+ }
+
+ ::CFStringAppendCString(inStr, inFlatSrc.get(), kCFStringEncodingUTF8);
+
+ sUnicodeNormalizer(inStr, kCFStringNormalizationFormC);
+
+ CFIndex length = CFStringGetLength(inStr);
+ const UniChar* chars = CFStringGetCharactersPtr(inStr);
+
+ if (chars)
+ aResult.Assign(chars, length);
+ else {
+ nsAutoBuffer<UniChar, FILENAME_BUFFER_SIZE> buffer;
+ if (!buffer.EnsureElemCapacity(length))
+ CopyUTF8toUTF16(aSrc, aResult);
+ else {
+ CFStringGetCharacters(inStr, CFRangeMake(0, length), buffer.get());
+ aResult.Assign(buffer.get(), length);
+ }
+ }
+ CFRelease(inStr);
+}
diff --git a/src/libs/xpcom18a4/xpcom/io/nsLocalFileOSX.h b/src/libs/xpcom18a4/xpcom/io/nsLocalFileOSX.h
new file mode 100644
index 00000000..b6d74521
--- /dev/null
+++ b/src/libs/xpcom18a4/xpcom/io/nsLocalFileOSX.h
@@ -0,0 +1,119 @@
+/* -*- Mode: C; tab-width: 2; 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.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2001, 2002
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Conrad Carlen <ccarlen@netscape.com>
+ *
+ * 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 nsLocalFileMac_h__
+#define nsLocalFileMac_h__
+
+#include "nsILocalFileMac.h"
+#include "nsString.h"
+#include "nsIHashable.h"
+
+class nsDirEnumerator;
+
+//*****************************************************************************
+// nsLocalFile
+//
+// The native charset of this implementation is UTF-8. The Unicode used by the
+// Mac OS file system is decomposed, so "Native" versions of these routines will
+// always use decomposed Unicode (NFD). Their "non-Native" counterparts are
+// intended to be simple wrappers which call the "Native" version and convert
+// between UTF-8 and UTF-16. All the work is done on the "Native" side except
+// for the conversion to NFC (composed Unicode) done in "non-Native" getters.
+//*****************************************************************************
+
+class NS_COM nsLocalFile : public nsILocalFileMac,
+ public nsIHashable
+{
+ friend class nsDirEnumerator;
+
+public:
+ NS_DEFINE_STATIC_CID_ACCESSOR(NS_LOCAL_FILE_CID)
+
+ nsLocalFile();
+
+ static NS_METHOD nsLocalFileConstructor(nsISupports* outer, const nsIID& aIID, void* *aInstancePtr);
+
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIFILE
+ NS_DECL_NSILOCALFILE
+ NS_DECL_NSILOCALFILEMAC
+ NS_DECL_NSIHASHABLE
+
+public:
+
+ static void GlobalInit();
+ static void GlobalShutdown();
+
+private:
+ ~nsLocalFile();
+
+protected:
+ nsLocalFile(const nsLocalFile& src);
+
+ nsresult SetBaseRef(CFURLRef aCFURLRef); // Does CFRetain on aCFURLRef
+ nsresult UpdateTargetRef();
+
+ nsresult GetFSRefInternal(FSRef& aFSSpec, PRBool bForceUpdateCache = PR_TRUE);
+ nsresult GetPathInternal(nsACString& path); // Returns path WRT mFollowLinks
+ nsresult EqualsInternal(nsISupports* inFile,
+ PRBool aUpdateCache, PRBool *_retval);
+
+ nsresult CopyInternal(nsIFile* newParentDir,
+ const nsAString& newName,
+ PRBool followLinks);
+
+ static PRInt64 HFSPlustoNSPRTime(const UTCDateTime& utcTime);
+ static void NSPRtoHFSPlusTime(PRInt64 nsprTime, UTCDateTime& utcTime);
+ static nsresult CFStringReftoUTF8(CFStringRef aInStrRef, nsACString& aOutStr);
+
+protected:
+ CFURLRef mBaseRef; // The FS object we represent
+ CFURLRef mTargetRef; // If mBaseRef is an alias, its target
+
+ FSRef mCachedFSRef;
+ PRPackedBool mCachedFSRefValid;
+
+ PRPackedBool mFollowLinks;
+ PRPackedBool mFollowLinksDirty;
+
+ static const char kPathSepChar;
+ static const PRUnichar kPathSepUnichar;
+ static const PRInt64 kJanuaryFirst1970Seconds;
+};
+
+#endif // nsLocalFileMac_h__
diff --git a/src/libs/xpcom18a4/xpcom/io/nsLocalFileUnicode.h b/src/libs/xpcom18a4/xpcom/io/nsLocalFileUnicode.h
new file mode 100644
index 00000000..90bc9c07
--- /dev/null
+++ b/src/libs/xpcom18a4/xpcom/io/nsLocalFileUnicode.h
@@ -0,0 +1,47 @@
+/* -*- 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) 1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Henry Sobotka <sobotka@axess.com>
+ *
+ * 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 ***** */
+
+#ifndef _NS_LOCALFILEUNICODE_H_
+
+extern void NS_ShutdownLocalFileUnicode();
+
+extern "C" NS_EXPORT nsresult
+NS_NewUnicodeLocalFile(const PRUnichar *, PRBool, nsILocalFile **);
+
+#endif // _NS_LOCALFILEUNICODE_H_
+
diff --git a/src/libs/xpcom18a4/xpcom/io/nsLocalFileUnix.cpp b/src/libs/xpcom18a4/xpcom/io/nsLocalFileUnix.cpp
new file mode 100644
index 00000000..eef17bde
--- /dev/null
+++ b/src/libs/xpcom18a4/xpcom/io/nsLocalFileUnix.cpp
@@ -0,0 +1,1724 @@
+/* -*- 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, released
+ * March 31, 1998.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1998-1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Mike Shaver <shaver@mozilla.org>
+ * Christopher Blizzard <blizzard@mozilla.org>
+ * Jason Eager <jce2@po.cwru.edu>
+ * Stuart Parmenter <pavlov@netscape.com>
+ * Brendan Eich <brendan@mozilla.org>
+ * Pete Collins <petejc@mozdev.org>
+ * Paul Ashford <arougthopher@lizardland.net>
+ *
+ * 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 ***** */
+
+/**
+ * Implementation of nsIFile for ``Unixy'' systems.
+ */
+
+// We're going to need some autoconf loving, I can just tell.
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <utime.h>
+#include <dirent.h>
+#include <ctype.h>
+#include <locale.h>
+#ifdef XP_BEOS
+ #include <Path.h>
+ #include <Entry.h>
+ #include <Roster.h>
+#endif
+#if defined(VMS)
+ #include <fabdef.h>
+#endif
+
+#include "nsDirectoryServiceDefs.h"
+#include "nsCRT.h"
+#include "nsCOMPtr.h"
+#include "nsMemory.h"
+#include "nsIFile.h"
+#include "nsString.h"
+#include "nsReadableUtils.h"
+#include "nsLocalFile.h"
+#include "nsIComponentManager.h"
+#include "nsXPIDLString.h"
+#include "prproces.h"
+#include "nsISimpleEnumerator.h"
+#include "nsITimelineService.h"
+
+#include "nsNativeCharsetUtils.h"
+
+// On some platforms file/directory name comparisons need to
+// be case-blind.
+#if defined(VMS)
+ #define FILE_STRCMP strcasecmp
+ #define FILE_STRNCMP strncasecmp
+#else
+ #define FILE_STRCMP strcmp
+ #define FILE_STRNCMP strncmp
+#endif
+
+#define VALIDATE_STAT_CACHE() \
+ PR_BEGIN_MACRO \
+ if (!mHaveCachedStat) { \
+ FillStatCache(); \
+ if (!mHaveCachedStat) \
+ return NSRESULT_FOR_ERRNO(); \
+ } \
+ PR_END_MACRO
+
+#define CHECK_mPath() \
+ PR_BEGIN_MACRO \
+ if (mPath.IsEmpty()) \
+ return NS_ERROR_NOT_INITIALIZED; \
+ PR_END_MACRO
+
+/* directory enumerator */
+class NS_COM
+nsDirEnumeratorUnix : public nsISimpleEnumerator
+{
+ public:
+ nsDirEnumeratorUnix();
+
+ // nsISupports interface
+ NS_DECL_ISUPPORTS
+
+ // nsISimpleEnumerator interface
+ NS_DECL_NSISIMPLEENUMERATOR
+
+ NS_IMETHOD Init(nsLocalFile *parent, PRBool ignored);
+
+ private:
+ ~nsDirEnumeratorUnix();
+
+ protected:
+ NS_IMETHOD GetNextEntry();
+
+ DIR *mDir;
+ struct dirent *mEntry;
+ nsCString mParentPath;
+};
+
+nsDirEnumeratorUnix::nsDirEnumeratorUnix() :
+ mDir(nsnull),
+ mEntry(nsnull)
+{
+}
+
+nsDirEnumeratorUnix::~nsDirEnumeratorUnix()
+{
+ if (mDir)
+ closedir(mDir);
+}
+
+NS_IMPL_ISUPPORTS1(nsDirEnumeratorUnix, nsISimpleEnumerator)
+
+NS_IMETHODIMP
+nsDirEnumeratorUnix::Init(nsLocalFile *parent, PRBool resolveSymlinks /*ignored*/)
+{
+ nsCAutoString dirPath;
+ if (NS_FAILED(parent->GetNativePath(dirPath)) ||
+ dirPath.IsEmpty()) {
+ return NS_ERROR_FILE_INVALID_PATH;
+ }
+
+ if (NS_FAILED(parent->GetNativePath(mParentPath)))
+ return NS_ERROR_FAILURE;
+
+ mDir = opendir(dirPath.get());
+ if (!mDir)
+ return NSRESULT_FOR_ERRNO();
+ return GetNextEntry();
+}
+
+NS_IMETHODIMP
+nsDirEnumeratorUnix::HasMoreElements(PRBool *result)
+{
+ *result = mDir && mEntry;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDirEnumeratorUnix::GetNext(nsISupports **_retval)
+{
+ nsresult rv;
+ if (!mDir || !mEntry) {
+ *_retval = nsnull;
+ return NS_OK;
+ }
+
+ nsLocalFile* file = new nsLocalFile();
+ if (!file)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ if (NS_FAILED(rv = file->InitWithNativePath(mParentPath)) ||
+ NS_FAILED(rv = file->AppendNative(nsDependentCString(mEntry->d_name)))) {
+ return rv;
+ }
+ *_retval = NS_STATIC_CAST(nsISupports *, file);
+ NS_ADDREF(*_retval);
+ return GetNextEntry();
+}
+
+NS_IMETHODIMP
+nsDirEnumeratorUnix::GetNextEntry()
+{
+ do {
+ errno = 0;
+ mEntry = readdir(mDir);
+
+ // end of dir or error
+ if (!mEntry)
+ return NSRESULT_FOR_ERRNO();
+
+ // keep going past "." and ".."
+ } while (mEntry->d_name[0] == '.' &&
+ (mEntry->d_name[1] == '\0' || // .\0
+ (mEntry->d_name[1] == '.' &&
+ mEntry->d_name[2] == '\0'))); // ..\0
+ return NS_OK;
+}
+
+nsLocalFile::nsLocalFile() :
+ mHaveCachedStat(PR_FALSE)
+{
+}
+
+nsLocalFile::nsLocalFile(const nsLocalFile& other)
+ : mCachedStat(other.mCachedStat)
+ , mPath(other.mPath)
+ , mHaveCachedStat(other.mHaveCachedStat)
+{
+}
+
+NS_IMPL_THREADSAFE_ISUPPORTS2(nsLocalFile,
+ nsIFile,
+ nsILocalFile)
+
+nsresult
+nsLocalFile::nsLocalFileConstructor(nsISupports *outer,
+ const nsIID &aIID,
+ void **aInstancePtr)
+{
+ NS_ENSURE_ARG_POINTER(aInstancePtr);
+ NS_ENSURE_NO_AGGREGATION(outer);
+
+ *aInstancePtr = nsnull;
+
+ nsCOMPtr<nsIFile> inst = new nsLocalFile();
+ if (!inst)
+ return NS_ERROR_OUT_OF_MEMORY;
+ return inst->QueryInterface(aIID, aInstancePtr);
+}
+
+nsresult
+nsLocalFile::FillStatCache() {
+ if (stat(mPath.get(), &mCachedStat) == -1) {
+ // try lstat it may be a symlink
+ if (lstat(mPath.get(), &mCachedStat) == -1) {
+ return NSRESULT_FOR_ERRNO();
+ }
+ }
+ mHaveCachedStat = PR_TRUE;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::Clone(nsIFile **file)
+{
+ // Just copy-construct ourselves
+ *file = new nsLocalFile(*this);
+ if (!*file)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ NS_ADDREF(*file);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::InitWithNativePath(const nsACString &filePath)
+{
+ if (Substring(filePath, 0, 2).EqualsLiteral("~/")) {
+ nsCOMPtr<nsIFile> homeDir;
+ nsCAutoString homePath;
+ if (NS_FAILED(NS_GetSpecialDirectory(NS_OS_HOME_DIR,
+ getter_AddRefs(homeDir))) ||
+ NS_FAILED(homeDir->GetNativePath(homePath))) {
+ return NS_ERROR_FAILURE;
+ }
+
+ mPath = homePath + Substring(filePath, 1, filePath.Length() - 1);
+ } else if (filePath.IsEmpty() || filePath.First() != '/') {
+ NS_ERROR("Relative paths not allowed");
+ return NS_ERROR_FILE_UNRECOGNIZED_PATH;
+ } else {
+ mPath = filePath;
+ }
+
+ // trim off trailing slashes
+ ssize_t len = mPath.Length();
+ while ((len > 1) && (mPath[len - 1] == '/'))
+ --len;
+ mPath.SetLength(len);
+
+ InvalidateCache();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::CreateAllAncestors(PRUint32 permissions)
+{
+ // <jband> I promise to play nice
+ char *buffer = mPath.BeginWriting(),
+ *slashp = buffer;
+
+#ifdef DEBUG_NSIFILE
+ fprintf(stderr, "nsIFile: before: %s\n", buffer);
+#endif
+
+ while ((slashp = strchr(slashp + 1, '/'))) {
+ /*
+ * Sequences of '/' are equivalent to a single '/'.
+ */
+ if (slashp[1] == '/')
+ continue;
+
+ /*
+ * If the path has a trailing slash, don't make the last component,
+ * because we'll get EEXIST in Create when we try to build the final
+ * component again, and it's easier to condition the logic here than
+ * there.
+ */
+ if (slashp[1] == '\0')
+ break;
+
+ /* Temporarily NUL-terminate here */
+ *slashp = '\0';
+#ifdef DEBUG_NSIFILE
+ fprintf(stderr, "nsIFile: mkdir(\"%s\")\n", buffer);
+#endif
+ int mkdir_result = mkdir(buffer, permissions);
+ int mkdir_errno = errno;
+ if (mkdir_result == -1) {
+ /*
+ * Always set |errno| to EEXIST if the dir already exists
+ * (we have to do this here since the errno value is not consistent
+ * in all cases - various reasons like different platform,
+ * automounter-controlled dir, etc. can affect it (see bug 125489
+ * for details)).
+ */
+ if (access(buffer, F_OK) == 0) {
+ mkdir_errno = EEXIST;
+ }
+ }
+
+ /* Put the / back before we (maybe) return */
+ *slashp = '/';
+
+ /*
+ * We could get EEXIST for an existing file -- not directory --
+ * with the name of one of our ancestors, but that's OK: we'll get
+ * ENOTDIR when we try to make the next component in the path,
+ * either here on back in Create, and error out appropriately.
+ */
+ if (mkdir_result == -1 && mkdir_errno != EEXIST)
+ return nsresultForErrno(mkdir_errno);
+ }
+
+#ifdef DEBUG_NSIFILE
+ fprintf(stderr, "nsIFile: after: %s\n", buffer);
+#endif
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::OpenNSPRFileDesc(PRInt32 flags, PRInt32 mode, PRFileDesc **_retval)
+{
+ *_retval = PR_Open(mPath.get(), flags, mode);
+ if (! *_retval)
+ return NS_ErrorAccordingToNSPR();
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::OpenANSIFileDesc(const char *mode, FILE **_retval)
+{
+ *_retval = fopen(mPath.get(), mode);
+ if (! *_retval)
+ return NS_ERROR_FAILURE;
+
+ return NS_OK;
+}
+
+static int
+do_create(const char *path, PRIntn flags, mode_t mode, PRFileDesc **_retval)
+{
+ *_retval = PR_Open(path, flags, mode);
+ return *_retval ? 0 : -1;
+}
+
+static int
+do_mkdir(const char *path, PRIntn flags, mode_t mode, PRFileDesc **_retval)
+{
+ *_retval = nsnull;
+ return mkdir(path, mode);
+}
+
+nsresult
+nsLocalFile::CreateAndKeepOpen(PRUint32 type, PRIntn flags,
+ PRUint32 permissions, PRFileDesc **_retval)
+{
+ if (type != NORMAL_FILE_TYPE && type != DIRECTORY_TYPE)
+ return NS_ERROR_FILE_UNKNOWN_TYPE;
+
+ int result;
+ int (*createFunc)(const char *, PRIntn, mode_t, PRFileDesc **) =
+ (type == NORMAL_FILE_TYPE) ? do_create : do_mkdir;
+
+ result = createFunc(mPath.get(), flags, permissions, _retval);
+ if (result == -1 && errno == ENOENT) {
+ /*
+ * If we failed because of missing ancestor components, try to create
+ * them and then retry the original creation.
+ *
+ * Ancestor directories get the same permissions as the file we're
+ * creating, with the X bit set for each of (user,group,other) with
+ * an R bit in the original permissions. If you want to do anything
+ * fancy like setgid or sticky bits, do it by hand.
+ */
+ int dirperm = permissions;
+ if (permissions & S_IRUSR)
+ dirperm |= S_IXUSR;
+ if (permissions & S_IRGRP)
+ dirperm |= S_IXGRP;
+ if (permissions & S_IROTH)
+ dirperm |= S_IXOTH;
+
+#ifdef DEBUG_NSIFILE
+ fprintf(stderr, "nsIFile: perm = %o, dirperm = %o\n", permissions,
+ dirperm);
+#endif
+
+ if (NS_FAILED(CreateAllAncestors(dirperm)))
+ return NS_ERROR_FAILURE;
+
+#ifdef DEBUG_NSIFILE
+ fprintf(stderr, "nsIFile: Create(\"%s\") again\n", mPath.get());
+#endif
+ result = createFunc(mPath.get(), flags, permissions, _retval);
+ }
+
+ return NSRESULT_FOR_RETURN(result);
+}
+
+NS_IMETHODIMP
+nsLocalFile::Create(PRUint32 type, PRUint32 permissions)
+{
+ PRFileDesc *junk = nsnull;
+ nsresult rv = CreateAndKeepOpen(type,
+ PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE |
+ PR_EXCL,
+ permissions,
+ &junk);
+ if (junk)
+ PR_Close(junk);
+ return rv;
+}
+
+NS_IMETHODIMP
+nsLocalFile::AppendNative(const nsACString &fragment)
+{
+ if (fragment.IsEmpty())
+ return NS_OK;
+
+ // only one component of path can be appended
+ nsACString::const_iterator begin, end;
+ if (FindCharInReadable('/', fragment.BeginReading(begin),
+ fragment.EndReading(end)))
+ return NS_ERROR_FILE_UNRECOGNIZED_PATH;
+
+ return AppendRelativeNativePath(fragment);
+}
+
+NS_IMETHODIMP
+nsLocalFile::AppendRelativeNativePath(const nsACString &fragment)
+{
+ if (fragment.IsEmpty())
+ return NS_OK;
+
+ // No leading '/'
+ if (fragment.First() == '/')
+ return NS_ERROR_FILE_UNRECOGNIZED_PATH;
+
+ if (mPath.EqualsLiteral("/"))
+ mPath.Append(fragment);
+ else
+ mPath.Append(NS_LITERAL_CSTRING("/") + fragment);
+
+ InvalidateCache();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::Normalize()
+{
+ char resolved_path[PATH_MAX] = "";
+ char *resolved_path_ptr = nsnull;
+
+#ifdef XP_BEOS
+ BEntry be_e(mPath.get(), true);
+ BPath be_p;
+ status_t err;
+ if ((err = be_e.GetPath(&be_p)) == B_OK) {
+ resolved_path_ptr = (char *)be_p.Path();
+ PL_strncpyz(resolved_path, resolved_path_ptr, PATH_MAX - 1);
+ }
+#else
+ resolved_path_ptr = realpath(mPath.get(), resolved_path);
+#endif
+ // if there is an error, the return is null.
+ if (!resolved_path_ptr)
+ return NSRESULT_FOR_ERRNO();
+
+ mPath = resolved_path;
+ return NS_OK;
+}
+
+void
+nsLocalFile::LocateNativeLeafName(nsACString::const_iterator &begin,
+ nsACString::const_iterator &end)
+{
+ // XXX perhaps we should cache this??
+
+ mPath.BeginReading(begin);
+ mPath.EndReading(end);
+
+ nsACString::const_iterator it = end;
+ nsACString::const_iterator stop = begin;
+ --stop;
+ while (--it != stop) {
+ if (*it == '/') {
+ begin = ++it;
+ return;
+ }
+ }
+ // else, the entire path is the leaf name (which means this
+ // isn't an absolute path... unexpected??)
+}
+
+NS_IMETHODIMP
+nsLocalFile::GetNativeLeafName(nsACString &aLeafName)
+{
+ nsACString::const_iterator begin, end;
+ LocateNativeLeafName(begin, end);
+ aLeafName = Substring(begin, end);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::SetNativeLeafName(const nsACString &aLeafName)
+{
+ nsACString::const_iterator begin, end;
+ LocateNativeLeafName(begin, end);
+ mPath.Replace(begin.get() - mPath.get(), Distance(begin, end), aLeafName);
+ InvalidateCache();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::GetNativePath(nsACString &_retval)
+{
+ _retval = mPath;
+ return NS_OK;
+}
+
+nsresult
+nsLocalFile::GetNativeTargetPathName(nsIFile *newParent,
+ const nsACString &newName,
+ nsACString &_retval)
+{
+ nsresult rv;
+ nsCOMPtr<nsIFile> oldParent;
+
+ if (!newParent) {
+ if (NS_FAILED(rv = GetParent(getter_AddRefs(oldParent))))
+ return rv;
+ newParent = oldParent.get();
+ } else {
+ // check to see if our target directory exists
+ PRBool targetExists;
+ if (NS_FAILED(rv = newParent->Exists(&targetExists)))
+ return rv;
+
+ if (!targetExists) {
+ // XXX create the new directory with some permissions
+ rv = newParent->Create(DIRECTORY_TYPE, 0700);
+ if (NS_FAILED(rv))
+ return rv;
+ } else {
+ // make sure that the target is actually a directory
+ PRBool targetIsDirectory;
+ if (NS_FAILED(rv = newParent->IsDirectory(&targetIsDirectory)))
+ return rv;
+ if (!targetIsDirectory)
+ return NS_ERROR_FILE_DESTINATION_NOT_DIR;
+ }
+ }
+
+ nsACString::const_iterator nameBegin, nameEnd;
+ if (!newName.IsEmpty()) {
+ newName.BeginReading(nameBegin);
+ newName.EndReading(nameEnd);
+ }
+ else
+ LocateNativeLeafName(nameBegin, nameEnd);
+
+ nsCAutoString dirName;
+ if (NS_FAILED(rv = newParent->GetNativePath(dirName)))
+ return rv;
+
+ _retval = dirName
+ + NS_LITERAL_CSTRING("/")
+ + Substring(nameBegin, nameEnd);
+ return NS_OK;
+}
+
+nsresult
+nsLocalFile::CopyDirectoryTo(nsIFile *newParent)
+{
+ nsresult rv;
+ /*
+ * dirCheck is used for various boolean test results such as from Equals,
+ * Exists, isDir, etc.
+ */
+ PRBool dirCheck, isSymlink;
+ PRUint32 oldPerms;
+
+ if (NS_FAILED((rv = IsDirectory(&dirCheck))))
+ return rv;
+ if (!dirCheck)
+ return CopyToNative(newParent, EmptyCString());
+
+ if (NS_FAILED(rv = Equals(newParent, &dirCheck)))
+ return rv;
+ if (dirCheck) {
+ // can't copy dir to itself
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ if (NS_FAILED(rv = newParent->Exists(&dirCheck)))
+ return rv;
+ // get the dirs old permissions
+ if (NS_FAILED(rv = GetPermissions(&oldPerms)))
+ return rv;
+ if (!dirCheck) {
+ if (NS_FAILED(rv = newParent->Create(DIRECTORY_TYPE, oldPerms)))
+ return rv;
+ } else { // dir exists lets try to use leaf
+ nsCAutoString leafName;
+ if (NS_FAILED(rv = GetNativeLeafName(leafName)))
+ return rv;
+ if (NS_FAILED(rv = newParent->AppendNative(leafName)))
+ return rv;
+ if (NS_FAILED(rv = newParent->Exists(&dirCheck)))
+ return rv;
+ if (dirCheck)
+ return NS_ERROR_FILE_ALREADY_EXISTS; // dest exists
+ if (NS_FAILED(rv = newParent->Create(DIRECTORY_TYPE, oldPerms)))
+ return rv;
+ }
+
+ nsCOMPtr<nsISimpleEnumerator> dirIterator;
+ if (NS_FAILED(rv = GetDirectoryEntries(getter_AddRefs(dirIterator))))
+ return rv;
+
+ PRBool hasMore = PR_FALSE;
+ while (dirIterator->HasMoreElements(&hasMore), hasMore) {
+ nsCOMPtr<nsIFile> entry;
+ rv = dirIterator->GetNext((nsISupports**)getter_AddRefs(entry));
+ if (NS_FAILED(rv))
+ continue;
+ if (NS_FAILED(rv = entry->IsSymlink(&isSymlink)))
+ return rv;
+ if (NS_FAILED(rv = entry->IsDirectory(&dirCheck)))
+ return rv;
+ if (dirCheck && !isSymlink) {
+ nsCOMPtr<nsIFile> destClone;
+ rv = newParent->Clone(getter_AddRefs(destClone));
+ if (NS_SUCCEEDED(rv)) {
+ nsCOMPtr<nsILocalFile> newDir(do_QueryInterface(destClone));
+ if (NS_FAILED(rv = entry->CopyToNative(newDir, EmptyCString()))) {
+#ifdef DEBUG
+ nsresult rv2;
+ nsCAutoString pathName;
+ if (NS_FAILED(rv2 = entry->GetNativePath(pathName)))
+ return rv2;
+ printf("Operation not supported: %s\n", pathName.get());
+#endif
+ if (rv == NS_ERROR_OUT_OF_MEMORY)
+ return rv;
+ continue;
+ }
+ }
+ } else {
+ if (NS_FAILED(rv = entry->CopyToNative(newParent, EmptyCString()))) {
+#ifdef DEBUG
+ nsresult rv2;
+ nsCAutoString pathName;
+ if (NS_FAILED(rv2 = entry->GetNativePath(pathName)))
+ return rv2;
+ printf("Operation not supported: %s\n", pathName.get());
+#endif
+ if (rv == NS_ERROR_OUT_OF_MEMORY)
+ return rv;
+ continue;
+ }
+ }
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::CopyToNative(nsIFile *newParent, const nsACString &newName)
+{
+ nsresult rv;
+ // check to make sure that this has been initialized properly
+ CHECK_mPath();
+
+ // we copy the parent here so 'newParent' remains immutable
+ nsCOMPtr <nsIFile> workParent;
+ if (newParent) {
+ if (NS_FAILED(rv = newParent->Clone(getter_AddRefs(workParent))))
+ return rv;
+ } else {
+ if (NS_FAILED(rv = GetParent(getter_AddRefs(workParent))))
+ return rv;
+ }
+
+ // check to see if we are a directory or if we are a file
+ PRBool isDirectory;
+ if (NS_FAILED(rv = IsDirectory(&isDirectory)))
+ return rv;
+
+ nsCAutoString newPathName;
+ if (isDirectory) {
+ if (!newName.IsEmpty()) {
+ if (NS_FAILED(rv = workParent->AppendNative(newName)))
+ return rv;
+ } else {
+ if (NS_FAILED(rv = GetNativeLeafName(newPathName)))
+ return rv;
+ if (NS_FAILED(rv = workParent->AppendNative(newPathName)))
+ return rv;
+ }
+ if (NS_FAILED(rv = CopyDirectoryTo(workParent)))
+ return rv;
+ } else {
+ rv = GetNativeTargetPathName(workParent, newName, newPathName);
+ if (NS_FAILED(rv))
+ return rv;
+
+#ifdef DEBUG_blizzard
+ printf("nsLocalFile::CopyTo() %s -> %s\n", mPath.get(), newPathName.get());
+#endif
+
+ // actually create the file.
+ nsLocalFile *newFile = new nsLocalFile();
+ if (!newFile)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ nsCOMPtr<nsILocalFile> fileRef(newFile); // release on exit
+
+ rv = newFile->InitWithNativePath(newPathName);
+ if (NS_FAILED(rv))
+ return rv;
+
+ // get the old permissions
+ PRUint32 myPerms;
+ GetPermissions(&myPerms);
+
+ // Create the new file with the old file's permissions, even if write
+ // permission is missing. We can't create with write permission and
+ // then change back to myPerm on all filesystems (FAT on Linux, e.g.).
+ // But we can write to a read-only file on all Unix filesystems if we
+ // open it successfully for writing.
+
+ PRFileDesc *newFD;
+ rv = newFile->CreateAndKeepOpen(NORMAL_FILE_TYPE,
+ PR_WRONLY|PR_CREATE_FILE|PR_TRUNCATE,
+ myPerms,
+ &newFD);
+ if (NS_FAILED(rv))
+ return rv;
+
+ // open the old file, too
+ PRBool specialFile;
+ if (NS_FAILED(rv = IsSpecial(&specialFile))) {
+ PR_Close(newFD);
+ return rv;
+ }
+ if (specialFile) {
+#ifdef DEBUG
+ printf("Operation not supported: %s\n", mPath.get());
+#endif
+ // make sure to clean up properly
+ PR_Close(newFD);
+ return NS_OK;
+ }
+
+ PRFileDesc *oldFD;
+ rv = OpenNSPRFileDesc(PR_RDONLY, myPerms, &oldFD);
+ if (NS_FAILED(rv)) {
+ // make sure to clean up properly
+ PR_Close(newFD);
+ return rv;
+ }
+
+#ifdef DEBUG_blizzard
+ PRInt32 totalRead = 0;
+ PRInt32 totalWritten = 0;
+#endif
+ char buf[BUFSIZ];
+ PRInt32 bytesRead;
+
+ while ((bytesRead = PR_Read(oldFD, buf, BUFSIZ)) > 0) {
+#ifdef DEBUG_blizzard
+ totalRead += bytesRead;
+#endif
+
+ // PR_Write promises never to do a short write
+ PRInt32 bytesWritten = PR_Write(newFD, buf, bytesRead);
+ if (bytesWritten < 0) {
+ bytesRead = -1;
+ break;
+ }
+ NS_ASSERTION(bytesWritten == bytesRead, "short PR_Write?");
+
+#ifdef DEBUG_blizzard
+ totalWritten += bytesWritten;
+#endif
+ }
+
+#ifdef DEBUG_blizzard
+ printf("read %d bytes, wrote %d bytes\n",
+ totalRead, totalWritten);
+#endif
+
+ // close the files
+ PR_Close(newFD);
+ PR_Close(oldFD);
+
+ // check for read (or write) error after cleaning up
+ if (bytesRead < 0)
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ return rv;
+}
+
+NS_IMETHODIMP
+nsLocalFile::CopyToFollowingLinksNative(nsIFile *newParent, const nsACString &newName)
+{
+ return CopyToNative(newParent, newName);
+}
+
+NS_IMETHODIMP
+nsLocalFile::MoveToNative(nsIFile *newParent, const nsACString &newName)
+{
+ nsresult rv;
+
+ // check to make sure that this has been initialized properly
+ CHECK_mPath();
+
+ // check to make sure that we have a new parent
+ nsCAutoString newPathName;
+ rv = GetNativeTargetPathName(newParent, newName, newPathName);
+ if (NS_FAILED(rv))
+ return rv;
+
+ // try for atomic rename, falling back to copy/delete
+ if (rename(mPath.get(), newPathName.get()) < 0) {
+#ifdef VMS
+ if (errno == EXDEV || errno == ENXIO) {
+#else
+ if (errno == EXDEV) {
+#endif
+ rv = CopyToNative(newParent, newName);
+ if (NS_SUCCEEDED(rv))
+ rv = Remove(PR_TRUE);
+ } else {
+ rv = NSRESULT_FOR_ERRNO();
+ }
+ }
+ return rv;
+}
+
+NS_IMETHODIMP
+nsLocalFile::Remove(PRBool recursive)
+{
+ CHECK_mPath();
+
+ VALIDATE_STAT_CACHE();
+ PRBool isSymLink, isDir;
+
+ nsresult rv = IsSymlink(&isSymLink);
+ if (NS_FAILED(rv))
+ return rv;
+
+ if (!recursive && isSymLink)
+ return NSRESULT_FOR_RETURN(unlink(mPath.get()));
+
+ isDir = S_ISDIR(mCachedStat.st_mode);
+ InvalidateCache();
+ if (isDir) {
+ if (recursive) {
+ nsDirEnumeratorUnix *dir = new nsDirEnumeratorUnix();
+ if (!dir)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ nsCOMPtr<nsISimpleEnumerator> dirRef(dir); // release on exit
+
+ rv = dir->Init(this, PR_FALSE);
+ if (NS_FAILED(rv))
+ return rv;
+
+ PRBool more;
+ while (dir->HasMoreElements(&more), more) {
+ nsCOMPtr<nsISupports> item;
+ rv = dir->GetNext(getter_AddRefs(item));
+ if (NS_FAILED(rv))
+ return NS_ERROR_FAILURE;
+
+ nsCOMPtr<nsIFile> file = do_QueryInterface(item, &rv);
+ if (NS_FAILED(rv))
+ return NS_ERROR_FAILURE;
+ if (NS_FAILED(rv = file->Remove(recursive)))
+ return rv;
+ }
+ }
+
+ if (rmdir(mPath.get()) == -1)
+ return NSRESULT_FOR_ERRNO();
+ } else {
+ if (unlink(mPath.get()) == -1)
+ return NSRESULT_FOR_ERRNO();
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::GetLastModifiedTime(PRInt64 *aLastModTime)
+{
+ CHECK_mPath();
+ NS_ENSURE_ARG(aLastModTime);
+
+ PRFileInfo64 info;
+ if (PR_GetFileInfo64(mPath.get(), &info) != PR_SUCCESS)
+ return NSRESULT_FOR_ERRNO();
+
+ // PRTime is a 64 bit value
+ // microseconds -> milliseconds
+ PRInt64 usecPerMsec;
+ LL_I2L(usecPerMsec, PR_USEC_PER_MSEC);
+ LL_DIV(*aLastModTime, info.modifyTime, usecPerMsec);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::SetLastModifiedTime(PRInt64 aLastModTime)
+{
+ CHECK_mPath();
+
+ int result;
+ if (! LL_IS_ZERO(aLastModTime)) {
+ VALIDATE_STAT_CACHE();
+ struct utimbuf ut;
+ ut.actime = mCachedStat.st_atime;
+
+ // convert milliseconds to seconds since the unix epoch
+ double dTime;
+ LL_L2D(dTime, aLastModTime);
+ ut.modtime = (time_t) (dTime / PR_MSEC_PER_SEC);
+ result = utime(mPath.get(), &ut);
+ } else {
+ result = utime(mPath.get(), nsnull);
+ }
+ InvalidateCache();
+ return NSRESULT_FOR_RETURN(result);
+}
+
+NS_IMETHODIMP
+nsLocalFile::GetLastModifiedTimeOfLink(PRInt64 *aLastModTimeOfLink)
+{
+ CHECK_mPath();
+ NS_ENSURE_ARG(aLastModTimeOfLink);
+
+ struct stat sbuf;
+ if (lstat(mPath.get(), &sbuf) == -1)
+ return NSRESULT_FOR_ERRNO();
+ LL_I2L(*aLastModTimeOfLink, (PRInt32)sbuf.st_mtime);
+
+ // lstat returns st_mtime in seconds
+ PRInt64 msecPerSec;
+ LL_I2L(msecPerSec, PR_MSEC_PER_SEC);
+ LL_MUL(*aLastModTimeOfLink, *aLastModTimeOfLink, msecPerSec);
+
+ return NS_OK;
+}
+
+/*
+ * utime(2) may or may not dereference symlinks, joy.
+ */
+NS_IMETHODIMP
+nsLocalFile::SetLastModifiedTimeOfLink(PRInt64 aLastModTimeOfLink)
+{
+ return SetLastModifiedTime(aLastModTimeOfLink);
+}
+
+/*
+ * Only send back permissions bits: maybe we want to send back the whole
+ * mode_t to permit checks against other file types?
+ */
+
+#define NORMALIZE_PERMS(mode) ((mode)& (S_IRWXU | S_IRWXG | S_IRWXO))
+
+NS_IMETHODIMP
+nsLocalFile::GetPermissions(PRUint32 *aPermissions)
+{
+ NS_ENSURE_ARG(aPermissions);
+ VALIDATE_STAT_CACHE();
+ *aPermissions = NORMALIZE_PERMS(mCachedStat.st_mode);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::GetPermissionsOfLink(PRUint32 *aPermissionsOfLink)
+{
+ CHECK_mPath();
+ NS_ENSURE_ARG(aPermissionsOfLink);
+
+ struct stat sbuf;
+ if (lstat(mPath.get(), &sbuf) == -1)
+ return NSRESULT_FOR_ERRNO();
+ *aPermissionsOfLink = NORMALIZE_PERMS(sbuf.st_mode);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::SetPermissions(PRUint32 aPermissions)
+{
+ CHECK_mPath();
+
+ InvalidateCache();
+
+ /*
+ * Race condition here: we should use fchmod instead, there's no way to
+ * guarantee the name still refers to the same file.
+ */
+ if (chmod(mPath.get(), aPermissions) < 0)
+ return NSRESULT_FOR_ERRNO();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::SetPermissionsOfLink(PRUint32 aPermissions)
+{
+ return SetPermissions(aPermissions);
+}
+
+NS_IMETHODIMP
+nsLocalFile::GetFileSize(PRInt64 *aFileSize)
+{
+ NS_ENSURE_ARG_POINTER(aFileSize);
+ *aFileSize = LL_ZERO;
+ VALIDATE_STAT_CACHE();
+
+#if defined(VMS)
+ /* Only two record formats can report correct file content size */
+ if ((mCachedStat.st_fab_rfm != FAB$C_STMLF) &&
+ (mCachedStat.st_fab_rfm != FAB$C_STMCR)) {
+ return NS_ERROR_FAILURE;
+ }
+#endif
+
+ /* XXX autoconf for and use stat64 if available */
+ if (!S_ISDIR(mCachedStat.st_mode)) {
+ LL_UI2L(*aFileSize, (PRUint32)mCachedStat.st_size);
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::SetFileSize(PRInt64 aFileSize)
+{
+ CHECK_mPath();
+
+ PRInt32 size;
+ LL_L2I(size, aFileSize);
+ /* XXX truncate64? */
+ InvalidateCache();
+ if (truncate(mPath.get(), (off_t)size) == -1)
+ return NSRESULT_FOR_ERRNO();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::GetFileSizeOfLink(PRInt64 *aFileSize)
+{
+ CHECK_mPath();
+ NS_ENSURE_ARG(aFileSize);
+
+ struct stat sbuf;
+ if (lstat(mPath.get(), &sbuf) == -1)
+ return NSRESULT_FOR_ERRNO();
+ /* XXX autoconf for and use lstat64 if available */
+ LL_UI2L(*aFileSize, (PRUint32)sbuf.st_size);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::GetDiskSpaceAvailable(PRInt64 *aDiskSpaceAvailable)
+{
+ NS_ENSURE_ARG_POINTER(aDiskSpaceAvailable);
+
+ // These systems have the operations necessary to check disk space.
+
+#if defined(HAVE_SYS_STATFS_H) || defined(HAVE_SYS_STATVFS_H)
+
+ // check to make sure that mPath is properly initialized
+ CHECK_mPath();
+
+ struct STATFS fs_buf;
+
+ /*
+ * Members of the STATFS struct that you should know about:
+ * f_bsize = block size on disk.
+ * f_bavail = number of free blocks available to a non-superuser.
+ * f_bfree = number of total free blocks in file system.
+ */
+
+ if (STATFS(mPath.get(), &fs_buf) < 0) {
+ // The call to STATFS failed.
+#ifdef DEBUG
+ printf("ERROR: GetDiskSpaceAvailable: STATFS call FAILED. \n");
+#endif
+ return NS_ERROR_FAILURE;
+ }
+#ifdef DEBUG_DISK_SPACE
+ printf("DiskSpaceAvailable: %d bytes\n",
+ fs_buf.f_bsize * (fs_buf.f_bavail - 1));
+#endif
+
+ /*
+ * The number of bytes free == The number of free blocks available to
+ * a non-superuser, minus one as a fudge factor, multiplied by the size
+ * of the aforementioned blocks.
+ */
+ PRInt64 bsize, bavail;
+
+ LL_I2L(bsize, fs_buf.f_bsize);
+ LL_I2L(bavail, fs_buf.f_bavail - 1);
+ LL_MUL(*aDiskSpaceAvailable, bsize, bavail);
+ return NS_OK;
+
+#else
+ /*
+ * This platform doesn't have statfs or statvfs. I'm sure that there's
+ * a way to check for free disk space on platforms that don't have statfs
+ * (I'm SURE they have df, for example).
+ *
+ * Until we figure out how to do that, lets be honest and say that this
+ * command isn't implemented properly for these platforms yet.
+ */
+#ifdef DEBUG
+ printf("ERROR: GetDiskSpaceAvailable: Not implemented for plaforms without statfs.\n");
+#endif
+ return NS_ERROR_NOT_IMPLEMENTED;
+
+#endif /* HAVE_SYS_STATFS_H or HAVE_SYS_STATVFS_H */
+
+}
+
+NS_IMETHODIMP
+nsLocalFile::GetParent(nsIFile **aParent)
+{
+ CHECK_mPath();
+ NS_ENSURE_ARG_POINTER(aParent);
+ *aParent = nsnull;
+
+ // if '/' we are at the top of the volume, return null
+ if (mPath.Equals("/"))
+ return NS_OK;
+
+ // <brendan, after jband> I promise to play nice
+ char *buffer = mPath.BeginWriting(),
+ *slashp = buffer;
+
+ // find the last significant slash in buffer
+ slashp = strrchr(buffer, '/');
+ NS_ASSERTION(slashp, "non-canonical mPath?");
+ if (!slashp)
+ return NS_ERROR_FILE_INVALID_PATH;
+
+ // for the case where we are at '/'
+ if (slashp == buffer)
+ slashp++;
+
+ // temporarily terminate buffer at the last significant slash
+ char c = *slashp;
+ *slashp = '\0';
+
+ nsCOMPtr<nsILocalFile> localFile;
+ nsresult rv = NS_NewNativeLocalFile(nsDependentCString(buffer), PR_TRUE,
+ getter_AddRefs(localFile));
+
+ // make buffer whole again
+ *slashp = c;
+
+ if (NS_SUCCEEDED(rv) && localFile)
+ rv = CallQueryInterface(localFile, aParent);
+ return rv;
+}
+
+/*
+ * The results of Exists, isWritable and isReadable are not cached.
+ */
+
+NS_IMETHODIMP
+nsLocalFile::Exists(PRBool *_retval)
+{
+ CHECK_mPath();
+ NS_ENSURE_ARG_POINTER(_retval);
+
+ *_retval = (access(mPath.get(), F_OK) == 0);
+ return NS_OK;
+}
+
+#ifdef XP_BEOS
+// access() is buggy in BeOS POSIX implementation, at least for BFS, using stat() instead
+NS_IMETHODIMP
+nsLocalFile::IsWritable(PRBool *_retval)
+{
+ CHECK_mPath();
+ NS_ENSURE_ARG_POINTER(_retval);
+ struct stat buf;
+ *_retval = (stat(mPath.get(), &buf) == 0);
+ *_retval = *_retval && (buf.st_mode & (S_IWUSR | S_IWGRP | S_IWOTH ));
+ if (*_retval || errno == EACCES)
+ return NS_OK;
+ return NSRESULT_FOR_ERRNO();
+}
+
+NS_IMETHODIMP
+nsLocalFile::IsReadable(PRBool *_retval)
+{
+ CHECK_mPath();
+ NS_ENSURE_ARG_POINTER(_retval);
+ struct stat buf;
+ *_retval = (stat(mPath.get(), &buf) == 0);
+ *_retval = *_retval && (buf.st_mode & (S_IRUSR | S_IRGRP | S_IROTH ));
+ if (*_retval || errno == EACCES)
+ return NS_OK;
+ return NSRESULT_FOR_ERRNO();
+}
+
+NS_IMETHODIMP
+nsLocalFile::IsExecutable(PRBool *_retval)
+{
+ CHECK_mPath();
+ NS_ENSURE_ARG_POINTER(_retval);
+ struct stat buf;
+ *_retval = (stat(mPath.get(), &buf) == 0);
+ *_retval = *_retval && (buf.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH ));
+ if (*_retval || errno == EACCES)
+ return NS_OK;
+ return NSRESULT_FOR_ERRNO();
+}
+#else
+NS_IMETHODIMP
+nsLocalFile::IsWritable(PRBool *_retval)
+{
+ CHECK_mPath();
+ NS_ENSURE_ARG_POINTER(_retval);
+
+ *_retval = (access(mPath.get(), W_OK) == 0);
+ if (*_retval || errno == EACCES)
+ return NS_OK;
+ return NSRESULT_FOR_ERRNO();
+}
+
+NS_IMETHODIMP
+nsLocalFile::IsReadable(PRBool *_retval)
+{
+ CHECK_mPath();
+ NS_ENSURE_ARG_POINTER(_retval);
+
+ *_retval = (access(mPath.get(), R_OK) == 0);
+ if (*_retval || errno == EACCES)
+ return NS_OK;
+ return NSRESULT_FOR_ERRNO();
+}
+
+NS_IMETHODIMP
+nsLocalFile::IsExecutable(PRBool *_retval)
+{
+ CHECK_mPath();
+ NS_ENSURE_ARG_POINTER(_retval);
+
+ *_retval = (access(mPath.get(), X_OK) == 0);
+ if (*_retval || errno == EACCES)
+ return NS_OK;
+ return NSRESULT_FOR_ERRNO();
+}
+#endif
+NS_IMETHODIMP
+nsLocalFile::IsDirectory(PRBool *_retval)
+{
+ NS_ENSURE_ARG_POINTER(_retval);
+ *_retval = PR_FALSE;
+ VALIDATE_STAT_CACHE();
+ *_retval = S_ISDIR(mCachedStat.st_mode);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::IsFile(PRBool *_retval)
+{
+ NS_ENSURE_ARG_POINTER(_retval);
+ *_retval = PR_FALSE;
+ VALIDATE_STAT_CACHE();
+ *_retval = S_ISREG(mCachedStat.st_mode);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::IsHidden(PRBool *_retval)
+{
+ NS_ENSURE_ARG_POINTER(_retval);
+ nsACString::const_iterator begin, end;
+ LocateNativeLeafName(begin, end);
+ *_retval = (*begin == '.');
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::IsSymlink(PRBool *_retval)
+{
+ NS_ENSURE_ARG_POINTER(_retval);
+ CHECK_mPath();
+
+ struct stat symStat;
+ lstat(mPath.get(), &symStat);
+ *_retval=S_ISLNK(symStat.st_mode);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::IsSpecial(PRBool *_retval)
+{
+ NS_ENSURE_ARG_POINTER(_retval);
+ VALIDATE_STAT_CACHE();
+ *_retval = S_ISCHR(mCachedStat.st_mode) ||
+ S_ISBLK(mCachedStat.st_mode) ||
+#ifdef S_ISSOCK
+ S_ISSOCK(mCachedStat.st_mode) ||
+#endif
+ S_ISFIFO(mCachedStat.st_mode);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::Equals(nsIFile *inFile, PRBool *_retval)
+{
+ NS_ENSURE_ARG(inFile);
+ NS_ENSURE_ARG_POINTER(_retval);
+ *_retval = PR_FALSE;
+
+ nsresult rv;
+ nsCAutoString inPath;
+
+ if (NS_FAILED(rv = inFile->GetNativePath(inPath)))
+ return rv;
+
+ *_retval = !FILE_STRCMP(inPath.get(), mPath.get());
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::Contains(nsIFile *inFile, PRBool recur, PRBool *_retval)
+{
+ CHECK_mPath();
+ NS_ENSURE_ARG(inFile);
+ NS_ENSURE_ARG_POINTER(_retval);
+
+ nsCAutoString inPath;
+ nsresult rv;
+
+ if (NS_FAILED(rv = inFile->GetNativePath(inPath)))
+ return rv;
+
+ *_retval = PR_FALSE;
+
+ ssize_t len = mPath.Length();
+ if (FILE_STRNCMP(mPath.get(), inPath.get(), len) == 0) {
+ // Now make sure that the |inFile|'s path has a separator at len,
+ // which implies that it has more components after len.
+ if (inPath[len] == '/')
+ *_retval = PR_TRUE;
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::GetNativeTarget(nsACString &_retval)
+{
+ CHECK_mPath();
+ _retval.Truncate();
+
+ struct stat symStat;
+ lstat(mPath.get(), &symStat);
+ if (!S_ISLNK(symStat.st_mode))
+ return NS_ERROR_FILE_INVALID_PATH;
+
+ PRInt64 targetSize64;
+ if (NS_FAILED(GetFileSizeOfLink(&targetSize64)))
+ return NS_ERROR_FAILURE;
+
+ PRInt32 size;
+ LL_L2I(size, targetSize64);
+ char *target = (char *)nsMemory::Alloc(size + 1);
+ if (!target)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ if (readlink(mPath.get(), target, (size_t)size) < 0) {
+ nsMemory::Free(target);
+ return NSRESULT_FOR_ERRNO();
+ }
+ target[size] = '\0';
+
+ nsresult rv;
+ PRBool isSymlink;
+ nsCOMPtr<nsIFile> self(this);
+ nsCOMPtr<nsIFile> parent;
+ while (NS_SUCCEEDED(rv = self->GetParent(getter_AddRefs(parent)))) {
+ NS_ASSERTION(parent != nsnull, "no parent?!");
+
+ if (target[0] != '/') {
+ nsCOMPtr<nsILocalFile> localFile(do_QueryInterface(parent, &rv));
+ if (NS_FAILED(rv))
+ break;
+ if (NS_FAILED(rv = localFile->AppendRelativeNativePath(nsDependentCString(target))))
+ break;
+ if (NS_FAILED(rv = localFile->GetNativePath(_retval)))
+ break;
+ if (NS_FAILED(rv = parent->IsSymlink(&isSymlink)))
+ break;
+ self = parent;
+ } else {
+ nsCOMPtr<nsILocalFile> localFile;
+ rv = NS_NewNativeLocalFile(nsDependentCString(target), PR_TRUE,
+ getter_AddRefs(localFile));
+ if (NS_FAILED(rv))
+ break;
+ if (NS_FAILED(rv = localFile->IsSymlink(&isSymlink)))
+ break;
+ _retval = target; // XXX can we avoid this buffer copy?
+ self = do_QueryInterface(localFile);
+ }
+ if (NS_FAILED(rv) || !isSymlink)
+ break;
+
+ const nsPromiseFlatCString &flatRetval = PromiseFlatCString(_retval);
+
+ // strip off any and all trailing '/'
+ PRInt32 len = strlen(target);
+ while (target[len-1] == '/' && len > 1)
+ target[--len] = '\0';
+ if (lstat(flatRetval.get(), &symStat) < 0) {
+ rv = NSRESULT_FOR_ERRNO();
+ break;
+ }
+ if (!S_ISLNK(symStat.st_mode)) {
+ rv = NS_ERROR_FILE_INVALID_PATH;
+ break;
+ }
+ size = symStat.st_size;
+ if (readlink(flatRetval.get(), target, size) < 0) {
+ rv = NSRESULT_FOR_ERRNO();
+ break;
+ }
+ target[size] = '\0';
+
+ _retval.Truncate();
+ }
+
+ nsMemory::Free(target);
+
+ if (NS_FAILED(rv))
+ _retval.Truncate();
+ return rv;
+}
+
+/* attribute PRBool followLinks; */
+NS_IMETHODIMP
+nsLocalFile::GetFollowLinks(PRBool *aFollowLinks)
+{
+ *aFollowLinks = PR_TRUE;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::SetFollowLinks(PRBool aFollowLinks)
+{
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::GetDirectoryEntries(nsISimpleEnumerator **entries)
+{
+ nsDirEnumeratorUnix *dir = new nsDirEnumeratorUnix();
+ if (!dir)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ NS_ADDREF(dir);
+ nsresult rv = dir->Init(this, PR_FALSE);
+ if (NS_FAILED(rv)) {
+ *entries = nsnull;
+ NS_RELEASE(dir);
+ } else {
+ *entries = dir; // transfer reference
+ }
+
+ return rv;
+}
+
+NS_IMETHODIMP
+nsLocalFile::Load(PRLibrary **_retval)
+{
+ CHECK_mPath();
+ NS_ENSURE_ARG_POINTER(_retval);
+
+ NS_TIMELINE_START_TIMER("PR_LoadLibrary");
+
+ *_retval = PR_LoadLibrary(mPath.get());
+
+ NS_TIMELINE_STOP_TIMER("PR_LoadLibrary");
+ NS_TIMELINE_MARK_TIMER1("PR_LoadLibrary", mPath.get());
+
+ if (!*_retval)
+ return NS_ERROR_FAILURE;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::GetPersistentDescriptor(nsACString &aPersistentDescriptor)
+{
+ return GetNativePath(aPersistentDescriptor);
+}
+
+NS_IMETHODIMP
+nsLocalFile::SetPersistentDescriptor(const nsACString &aPersistentDescriptor)
+{
+ return InitWithNativePath(aPersistentDescriptor);
+}
+
+#ifdef XP_BEOS
+NS_IMETHODIMP
+nsLocalFile::Reveal()
+{
+ BPath bPath(mPath.get());
+ bPath.GetParent(&bPath);
+ entry_ref ref;
+ get_ref_for_path(bPath.Path(),&ref);
+ BMessage message(B_REFS_RECEIVED);
+ message.AddRef("refs",&ref);
+ BMessenger messenger("application/x-vnd.Be-TRAK");
+ messenger.SendMessage(&message);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::Launch()
+{
+ entry_ref ref;
+ get_ref_for_path (mPath.get(), &ref);
+ be_roster->Launch (&ref);
+
+ return NS_OK;
+}
+#else
+NS_IMETHODIMP
+nsLocalFile::Reveal()
+{
+ return NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+nsLocalFile::Launch()
+{
+ return NS_ERROR_FAILURE;
+}
+#endif
+
+nsresult
+NS_NewNativeLocalFile(const nsACString &path, PRBool followSymlinks, nsILocalFile **result)
+{
+ nsLocalFile *file = new nsLocalFile();
+ if (!file)
+ return NS_ERROR_OUT_OF_MEMORY;
+ NS_ADDREF(file);
+
+ if (!path.IsEmpty()) {
+ nsresult rv = file->InitWithNativePath(path);
+ if (NS_FAILED(rv)) {
+ NS_RELEASE(file);
+ return rv;
+ }
+ }
+ *result = file;
+ return NS_OK;
+}
+
+//-----------------------------------------------------------------------------
+// unicode support
+//-----------------------------------------------------------------------------
+
+#define SET_UCS(func, ucsArg) \
+ { \
+ nsCAutoString buf; \
+ nsresult rv = NS_CopyUnicodeToNative(ucsArg, buf); \
+ if (NS_FAILED(rv)) \
+ return rv; \
+ return (func)(buf); \
+ }
+
+#define GET_UCS(func, ucsArg) \
+ { \
+ nsCAutoString buf; \
+ nsresult rv = (func)(buf); \
+ if (NS_FAILED(rv)) return rv; \
+ return NS_CopyNativeToUnicode(buf, ucsArg); \
+ }
+
+#define SET_UCS_2ARGS_2(func, opaqueArg, ucsArg) \
+ { \
+ nsCAutoString buf; \
+ nsresult rv = NS_CopyUnicodeToNative(ucsArg, buf); \
+ if (NS_FAILED(rv)) \
+ return rv; \
+ return (func)(opaqueArg, buf); \
+ }
+
+// Unicode interface Wrapper
+nsresult
+nsLocalFile::InitWithPath(const nsAString &filePath)
+{
+ SET_UCS(InitWithNativePath, filePath);
+}
+nsresult
+nsLocalFile::Append(const nsAString &node)
+{
+ SET_UCS(AppendNative, node);
+}
+nsresult
+nsLocalFile::AppendRelativePath(const nsAString &node)
+{
+ SET_UCS(AppendRelativeNativePath, node);
+}
+nsresult
+nsLocalFile::GetLeafName(nsAString &aLeafName)
+{
+ GET_UCS(GetNativeLeafName, aLeafName);
+}
+nsresult
+nsLocalFile::SetLeafName(const nsAString &aLeafName)
+{
+ SET_UCS(SetNativeLeafName, aLeafName);
+}
+nsresult
+nsLocalFile::GetPath(nsAString &_retval)
+{
+ return NS_CopyNativeToUnicode(mPath, _retval);
+}
+nsresult
+nsLocalFile::CopyTo(nsIFile *newParentDir, const nsAString &newName)
+{
+ SET_UCS_2ARGS_2(CopyToNative , newParentDir, newName);
+}
+nsresult
+nsLocalFile::CopyToFollowingLinks(nsIFile *newParentDir, const nsAString &newName)
+{
+ SET_UCS_2ARGS_2(CopyToFollowingLinksNative , newParentDir, newName);
+}
+nsresult
+nsLocalFile::MoveTo(nsIFile *newParentDir, const nsAString &newName)
+{
+ SET_UCS_2ARGS_2(MoveToNative, newParentDir, newName);
+}
+nsresult
+nsLocalFile::GetTarget(nsAString &_retval)
+{
+ GET_UCS(GetNativeTarget, _retval);
+}
+nsresult
+NS_NewLocalFile(const nsAString &path, PRBool followLinks, nsILocalFile* *result)
+{
+ nsCAutoString buf;
+ nsresult rv = NS_CopyUnicodeToNative(path, buf);
+ if (NS_FAILED(rv))
+ return rv;
+ return NS_NewNativeLocalFile(buf, followLinks, result);
+}
+
+//-----------------------------------------------------------------------------
+// global init/shutdown
+//-----------------------------------------------------------------------------
+
+void
+nsLocalFile::GlobalInit()
+{
+}
+
+void
+nsLocalFile::GlobalShutdown()
+{
+}
diff --git a/src/libs/xpcom18a4/xpcom/io/nsLocalFileUnix.h b/src/libs/xpcom18a4/xpcom/io/nsLocalFileUnix.h
new file mode 100644
index 00000000..a994e1a1
--- /dev/null
+++ b/src/libs/xpcom18a4/xpcom/io/nsLocalFileUnix.h
@@ -0,0 +1,132 @@
+/* -*- Mode: C++; tab-width: 8; 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, released
+ * March 31, 1998.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1998-1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Mike Shaver <shaver@mozilla.org>
+ *
+ * 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 ***** */
+
+/*
+ * Implementation of nsIFile for ``Unixy'' systems.
+ */
+
+#ifndef _nsLocalFileUNIX_H_
+#define _nsLocalFileUNIX_H_
+
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "nscore.h"
+#include "nsString.h"
+#include "nsReadableUtils.h"
+
+/**
+ * we need these for statfs()
+ */
+#ifdef HAVE_SYS_STATVFS_H
+ #if defined(__osf__) && defined(__DECCXX)
+ extern "C" int statvfs(const char *, struct statvfs *);
+ #endif
+ #include <sys/statvfs.h>
+#endif
+
+#ifdef HAVE_SYS_STATFS_H
+ #include <sys/statfs.h>
+#endif
+
+#ifdef HAVE_STATVFS
+ #define STATFS statvfs
+#else
+ #define STATFS statfs
+#endif
+
+// so we can statfs on freebsd
+#if defined(__FreeBSD__)
+ #define HAVE_SYS_STATFS_H
+ #define STATFS statfs
+ #include <sys/param.h>
+ #include <sys/mount.h>
+#endif
+
+class NS_COM nsLocalFile : public nsILocalFile
+{
+public:
+ NS_DEFINE_STATIC_CID_ACCESSOR(NS_LOCAL_FILE_CID)
+
+ nsLocalFile();
+
+ static NS_METHOD nsLocalFileConstructor(nsISupports* outer, const nsIID& aIID, void* *aInstancePtr);
+
+ // nsISupports
+ NS_DECL_ISUPPORTS
+
+ // nsIFile
+ NS_DECL_NSIFILE
+
+ // nsILocalFile
+ NS_DECL_NSILOCALFILE
+
+public:
+ static void GlobalInit();
+ static void GlobalShutdown();
+
+private:
+ nsLocalFile(const nsLocalFile& other);
+ ~nsLocalFile() {}
+
+protected:
+ struct stat mCachedStat;
+ nsCString mPath;
+ PRPackedBool mHaveCachedStat;
+
+ void LocateNativeLeafName(nsACString::const_iterator &,
+ nsACString::const_iterator &);
+
+ nsresult CopyDirectoryTo(nsIFile *newParent);
+ nsresult CreateAllAncestors(PRUint32 permissions);
+ nsresult GetNativeTargetPathName(nsIFile *newParent,
+ const nsACString &newName,
+ nsACString &_retval);
+
+ void InvalidateCache() {
+ mHaveCachedStat = PR_FALSE;
+ }
+ nsresult FillStatCache();
+
+ nsresult CreateAndKeepOpen(PRUint32 type, PRIntn flags,
+ PRUint32 permissions, PRFileDesc **_retval);
+};
+
+#endif /* _nsLocalFileUNIX_H_ */
diff --git a/src/libs/xpcom18a4/xpcom/io/nsLocalFileWin.cpp b/src/libs/xpcom18a4/xpcom/io/nsLocalFileWin.cpp
new file mode 100644
index 00000000..63414fc3
--- /dev/null
+++ b/src/libs/xpcom18a4/xpcom/io/nsLocalFileWin.cpp
@@ -0,0 +1,2440 @@
+/* -*- 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, released
+ * March 31, 1998.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1998-1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Doug Turner <dougt@netscape.com>
+ * Dean Tessman <dean_tessman@hotmail.com>
+ * Brodie Thiesfield <brofield@jellycan.com>
+ *
+ * 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 "nsCOMPtr.h"
+#include "nsMemory.h"
+
+#include "nsLocalFile.h"
+#include "nsNativeCharsetUtils.h"
+
+#include "nsISimpleEnumerator.h"
+#include "nsIComponentManager.h"
+#include "prtypes.h"
+#include "prio.h"
+
+#include "nsXPIDLString.h"
+#include "nsReadableUtils.h"
+
+#include <direct.h>
+#include <windows.h>
+
+#include "shellapi.h"
+#include "shlguid.h"
+
+#include <io.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <mbstring.h>
+
+#include "nsXPIDLString.h"
+#include "prproces.h"
+#include "nsITimelineService.h"
+
+#include "nsAutoLock.h"
+#include "SpecialSystemDirectory.h"
+
+// _mbsstr isn't declared in w32api headers but it's there in the libs
+#ifdef __MINGW32__
+extern "C" {
+unsigned char *_mbsstr( const unsigned char *str,
+ const unsigned char *substr );
+}
+#endif
+
+class nsDriveEnumerator : public nsISimpleEnumerator
+{
+public:
+ nsDriveEnumerator();
+ virtual ~nsDriveEnumerator();
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSISIMPLEENUMERATOR
+ nsresult Init();
+private:
+ /* mDrives and mLetter share data
+ * Init sets them.
+ * HasMoreElements reads mLetter.
+ * GetNext advances mLetter.
+ */
+ nsCString mDrives;
+ const char *mLetter;
+};
+
+//----------------------------------------------------------------------------
+// short cut resolver
+//----------------------------------------------------------------------------
+
+class ShortcutResolver
+{
+public:
+ ShortcutResolver();
+ // nonvirtual since we're not subclassed
+ ~ShortcutResolver();
+
+ nsresult Init();
+ nsresult Resolve(const WCHAR* in, char* out);
+
+private:
+ PRLock* mLock;
+ IPersistFile* mPersistFile;
+ IShellLink* mShellLink;
+};
+
+ShortcutResolver::ShortcutResolver()
+{
+ mLock = nsnull;
+ mPersistFile = nsnull;
+ mShellLink = nsnull;
+}
+
+ShortcutResolver::~ShortcutResolver()
+{
+ if (mLock)
+ PR_DestroyLock(mLock);
+
+ // Release the pointer to the IPersistFile interface.
+ if (mPersistFile)
+ mPersistFile->Release();
+
+ // Release the pointer to the IShellLink interface.
+ if(mShellLink)
+ mShellLink->Release();
+
+ CoUninitialize();
+}
+
+nsresult
+ShortcutResolver::Init()
+{
+ CoInitialize(NULL); // FIX: we should probably move somewhere higher up during startup
+
+ mLock = PR_NewLock();
+ if (!mLock)
+ return NS_ERROR_FAILURE;
+
+ HRESULT hres = CoCreateInstance(CLSID_ShellLink,
+ NULL,
+ CLSCTX_INPROC_SERVER,
+ IID_IShellLink,
+ (void**)&mShellLink);
+ if (SUCCEEDED(hres))
+ {
+ // Get a pointer to the IPersistFile interface.
+ hres = mShellLink->QueryInterface(IID_IPersistFile, (void**)&mPersistFile);
+ }
+
+ if (mPersistFile == nsnull || mShellLink == nsnull)
+ return NS_ERROR_FAILURE;
+
+ return NS_OK;
+}
+
+// |out| must be an allocated buffer of size MAX_PATH
+nsresult
+ShortcutResolver::Resolve(const WCHAR* in, char* out)
+{
+ nsAutoLock lock(mLock);
+
+ // see if we can Load the path.
+ HRESULT hres = mPersistFile->Load(in, STGM_READ);
+
+ if (FAILED(hres))
+ return NS_ERROR_FAILURE;
+
+ // Resolve the link.
+ hres = mShellLink->Resolve(nsnull, SLR_NO_UI );
+
+ if (FAILED(hres))
+ return NS_ERROR_FAILURE;
+
+ WIN32_FIND_DATA wfd;
+ // Get the path to the link target.
+ hres = mShellLink->GetPath( out, MAX_PATH, &wfd, SLGP_UNCPRIORITY );
+ if (FAILED(hres))
+ return NS_ERROR_FAILURE;
+ return NS_OK;
+}
+
+static ShortcutResolver * gResolver = nsnull;
+
+static nsresult NS_CreateShortcutResolver()
+{
+ gResolver = new ShortcutResolver();
+ if (!gResolver)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ return gResolver->Init();
+}
+
+static void NS_DestroyShortcutResolver()
+{
+ delete gResolver;
+ gResolver = nsnull;
+}
+
+
+//-----------------------------------------------------------------------------
+// static helper functions
+//-----------------------------------------------------------------------------
+
+// certainly not all the error that can be
+// encountered, but many of them common ones
+static nsresult ConvertWinError(DWORD winErr)
+{
+ nsresult rv;
+
+ switch (winErr)
+ {
+ case ERROR_FILE_NOT_FOUND:
+ case ERROR_PATH_NOT_FOUND:
+ case ERROR_INVALID_DRIVE:
+ rv = NS_ERROR_FILE_NOT_FOUND;
+ break;
+ case ERROR_ACCESS_DENIED:
+ case ERROR_NOT_SAME_DEVICE:
+ rv = NS_ERROR_FILE_ACCESS_DENIED;
+ break;
+ case ERROR_NOT_ENOUGH_MEMORY:
+ case ERROR_INVALID_BLOCK:
+ case ERROR_INVALID_HANDLE:
+ case ERROR_ARENA_TRASHED:
+ rv = NS_ERROR_OUT_OF_MEMORY;
+ break;
+ case ERROR_CURRENT_DIRECTORY:
+ rv = NS_ERROR_FILE_DIR_NOT_EMPTY;
+ break;
+ case ERROR_WRITE_PROTECT:
+ rv = NS_ERROR_FILE_READ_ONLY;
+ break;
+ case ERROR_HANDLE_DISK_FULL:
+ rv = NS_ERROR_FILE_TOO_BIG;
+ break;
+ case ERROR_FILE_EXISTS:
+ case ERROR_ALREADY_EXISTS:
+ case ERROR_CANNOT_MAKE:
+ rv = NS_ERROR_FILE_ALREADY_EXISTS;
+ break;
+ case 0:
+ rv = NS_OK;
+ break;
+ default:
+ rv = NS_ERROR_FAILURE;
+ break;
+ }
+ return rv;
+}
+
+// definition of INVALID_SET_FILE_POINTER from VC.NET header files
+// it doesn't appear to be defined by VC6
+#ifndef INVALID_SET_FILE_POINTER
+# define INVALID_SET_FILE_POINTER ((DWORD)-1)
+#endif
+// same goes for INVALID_FILE_ATTRIBUTES
+#ifndef INVALID_FILE_ATTRIBUTES
+# define INVALID_FILE_ATTRIBUTES ((DWORD)-1)
+#endif
+
+// as suggested in the MSDN documentation on SetFilePointer
+static __int64
+MyFileSeek64(HANDLE aHandle, __int64 aDistance, DWORD aMoveMethod)
+{
+ LARGE_INTEGER li;
+
+ li.QuadPart = aDistance;
+ li.LowPart = SetFilePointer(aHandle, li.LowPart, &li.HighPart, aMoveMethod);
+ if (li.LowPart == INVALID_SET_FILE_POINTER && GetLastError() != NO_ERROR)
+ {
+ li.QuadPart = -1;
+ }
+
+ return li.QuadPart;
+}
+
+static PRBool
+IsShortcutPath(const char *path)
+{
+ // Under Windows, the shortcuts are just files with a ".lnk" extension.
+ // Note also that we don't resolve links in the middle of paths.
+ // i.e. "c:\foo.lnk\bar.txt" is invalid.
+ NS_ABORT_IF_FALSE(path, "don't pass nulls");
+ const char * ext = (const char *) _mbsrchr((const unsigned char *)path, '.');
+ if (!ext || 0 != stricmp(ext + 1, "lnk"))
+ return PR_FALSE;
+ return PR_TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// nsDirEnumerator
+//-----------------------------------------------------------------------------
+
+class nsDirEnumerator : public nsISimpleEnumerator
+{
+ public:
+
+ NS_DECL_ISUPPORTS
+
+ nsDirEnumerator() : mDir(nsnull)
+ {
+ }
+
+ nsresult Init(nsILocalFile* parent)
+ {
+ nsCAutoString filepath;
+ parent->GetNativeTarget(filepath);
+
+ if (filepath.IsEmpty())
+ {
+ parent->GetNativePath(filepath);
+ }
+
+ if (filepath.IsEmpty())
+ {
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ mDir = PR_OpenDir(filepath.get());
+ if (mDir == nsnull) // not a directory?
+ return NS_ERROR_FAILURE;
+
+ mParent = parent;
+ return NS_OK;
+ }
+
+ NS_IMETHOD HasMoreElements(PRBool *result)
+ {
+ nsresult rv;
+ if (mNext == nsnull && mDir)
+ {
+ PRDirEntry* entry = PR_ReadDir(mDir, PR_SKIP_BOTH);
+ if (entry == nsnull)
+ {
+ // end of dir entries
+
+ PRStatus status = PR_CloseDir(mDir);
+ if (status != PR_SUCCESS)
+ return NS_ERROR_FAILURE;
+ mDir = nsnull;
+
+ *result = PR_FALSE;
+ return NS_OK;
+ }
+
+ nsCOMPtr<nsIFile> file;
+ rv = mParent->Clone(getter_AddRefs(file));
+ if (NS_FAILED(rv))
+ return rv;
+
+ rv = file->AppendNative(nsDependentCString(entry->name));
+ if (NS_FAILED(rv))
+ return rv;
+
+ // make sure the thing exists. If it does, try the next one.
+ PRBool exists;
+ rv = file->Exists(&exists);
+ if (NS_FAILED(rv) || !exists)
+ {
+ return HasMoreElements(result);
+ }
+
+ mNext = do_QueryInterface(file);
+ }
+ *result = mNext != nsnull;
+ return NS_OK;
+ }
+
+ NS_IMETHOD GetNext(nsISupports **result)
+ {
+ nsresult rv;
+ PRBool hasMore;
+ rv = HasMoreElements(&hasMore);
+ if (NS_FAILED(rv)) return rv;
+
+ *result = mNext; // might return nsnull
+ NS_IF_ADDREF(*result);
+
+ mNext = nsnull;
+ return NS_OK;
+ }
+
+ // dtor can be non-virtual since there are no subclasses, but must be
+ // public to use the class on the stack.
+ ~nsDirEnumerator()
+ {
+ if (mDir)
+ {
+ PRStatus status = PR_CloseDir(mDir);
+ NS_ASSERTION(status == PR_SUCCESS, "close failed");
+ }
+ }
+
+ protected:
+ PRDir* mDir;
+ nsCOMPtr<nsILocalFile> mParent;
+ nsCOMPtr<nsILocalFile> mNext;
+};
+
+NS_IMPL_ISUPPORTS1(nsDirEnumerator, nsISimpleEnumerator)
+
+
+//-----------------------------------------------------------------------------
+// nsLocalFile <public>
+//-----------------------------------------------------------------------------
+
+nsLocalFile::nsLocalFile()
+ : mFollowSymlinks(PR_FALSE)
+{
+ MakeDirty();
+ memset(&mFileInfo64, 0, sizeof(mFileInfo64));
+}
+
+NS_METHOD
+nsLocalFile::nsLocalFileConstructor(nsISupports* outer, const nsIID& aIID, void* *aInstancePtr)
+{
+ NS_ENSURE_ARG_POINTER(aInstancePtr);
+ NS_ENSURE_NO_AGGREGATION(outer);
+
+ nsLocalFile* inst = new nsLocalFile();
+ if (inst == NULL)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ nsresult rv = inst->QueryInterface(aIID, aInstancePtr);
+ if (NS_FAILED(rv))
+ {
+ delete inst;
+ return rv;
+ }
+ return NS_OK;
+}
+
+
+//-----------------------------------------------------------------------------
+// nsLocalFile::nsISupports
+//-----------------------------------------------------------------------------
+
+NS_IMPL_THREADSAFE_ISUPPORTS2(nsLocalFile, nsILocalFile, nsIFile)
+
+
+//-----------------------------------------------------------------------------
+// nsLocalFile <private>
+//-----------------------------------------------------------------------------
+
+nsLocalFile::nsLocalFile(const nsLocalFile& other)
+ : mDirty(other.mDirty)
+ , mFollowSymlinks(other.mFollowSymlinks)
+ , mWorkingPath(other.mWorkingPath)
+ , mResolvedPath(other.mResolvedPath)
+ , mFileInfo64(other.mFileInfo64)
+{
+}
+
+// Resolve the shortcut file from mWorkingPath and write the path
+// it points to into mResolvedPath.
+nsresult
+nsLocalFile::ResolveShortcut()
+{
+ // we can't do anything without the resolver
+ if (!gResolver)
+ return NS_ERROR_FAILURE;
+
+ // allocate the memory for the result of the resolution
+ nsAutoString ucsBuf;
+ NS_CopyNativeToUnicode(mWorkingPath, ucsBuf);
+
+ mResolvedPath.SetLength(MAX_PATH);
+ char *resolvedPath = mResolvedPath.BeginWriting();
+
+ // resolve this shortcut
+ nsresult rv = gResolver->Resolve(ucsBuf.get(), resolvedPath);
+
+ size_t len = NS_FAILED(rv) ? 0 : strlen(resolvedPath);
+ mResolvedPath.SetLength(len);
+
+ return rv;
+}
+
+// Resolve any shortcuts and stat the resolved path. After a successful return
+// the path is guaranteed valid and the members of mFileInfo64 can be used.
+nsresult
+nsLocalFile::ResolveAndStat()
+{
+ // if we aren't dirty then we are already done
+ if (!mDirty)
+ return NS_OK;
+
+ // we can't resolve/stat anything that isn't a valid NSPR addressable path
+ if (mWorkingPath.IsEmpty())
+ return NS_ERROR_FILE_INVALID_PATH;
+
+ // this is usually correct
+ mResolvedPath.Assign(mWorkingPath);
+
+ // slutty hack designed to work around bug 134796 until it is fixed
+ char temp[4];
+ const char *nsprPath = mWorkingPath.get();
+ if (mWorkingPath.Length() == 2 && mWorkingPath.CharAt(1) == ':')
+ {
+ temp[0] = mWorkingPath[0];
+ temp[1] = mWorkingPath[1];
+ temp[2] = '\\';
+ temp[3] = '\0';
+ nsprPath = temp;
+ }
+
+ // first we will see if the working path exists. If it doesn't then
+ // there is nothing more that can be done
+ PRStatus status = PR_GetFileInfo64(nsprPath, &mFileInfo64);
+ if (status != PR_SUCCESS)
+ return NS_ERROR_FILE_NOT_FOUND;
+
+ // if this isn't a shortcut file or we aren't following symlinks then we're done
+ if (!mFollowSymlinks
+ || mFileInfo64.type != PR_FILE_FILE
+ || !IsShortcutPath(mWorkingPath.get()))
+ {
+ mDirty = PR_FALSE;
+ return NS_OK;
+ }
+
+ // we need to resolve this shortcut to what it points to, this will
+ // set mResolvedPath. Even if it fails we need to have the resolved
+ // path equal to working path for those functions that always use
+ // the resolved path.
+ nsresult rv = ResolveShortcut();
+ if (NS_FAILED(rv))
+ {
+ mResolvedPath.Assign(mWorkingPath);
+ return rv;
+ }
+
+ // get the details of the resolved path
+ status = PR_GetFileInfo64(mResolvedPath.get(), &mFileInfo64);
+ if (status != PR_SUCCESS)
+ return NS_ERROR_FILE_NOT_FOUND;
+
+ mDirty = PR_FALSE;
+ return NS_OK;
+}
+
+
+//-----------------------------------------------------------------------------
+// nsLocalFile::nsIFile,nsILocalFile
+//-----------------------------------------------------------------------------
+
+NS_IMETHODIMP
+nsLocalFile::Clone(nsIFile **file)
+{
+ // Just copy-construct ourselves
+ *file = new nsLocalFile(*this);
+ if (!*file)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ NS_ADDREF(*file);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::InitWithNativePath(const nsACString &filePath)
+{
+ MakeDirty();
+
+ nsACString::const_iterator begin, end;
+ filePath.BeginReading(begin);
+ filePath.EndReading(end);
+
+ // input string must not be empty
+ if (begin == end)
+ return NS_ERROR_FAILURE;
+
+ char firstChar = *begin;
+ char secondChar = *(++begin);
+
+ // just do a sanity check. if it has any forward slashes, it is not a Native path
+ // on windows. Also, it must have a colon at after the first char.
+
+ char *path = nsnull;
+ PRInt32 pathLen = 0;
+
+ if ( ( (secondChar == ':') && !FindCharInReadable('/', begin, end) ) || // normal path
+ ( (firstChar == '\\') && (secondChar == '\\') ) ) // network path
+ {
+ // This is a native path
+ path = ToNewCString(filePath);
+ pathLen = filePath.Length();
+ }
+
+ if (path == nsnull)
+ return NS_ERROR_FILE_UNRECOGNIZED_PATH;
+
+ // kill any trailing '\' provided it isn't the second char of DBCS
+ PRInt32 len = pathLen - 1;
+ if (path[len] == '\\' &&
+ (!::IsDBCSLeadByte(path[len-1]) ||
+ _mbsrchr((const unsigned char *)path, '\\') == (const unsigned char *)path+len))
+ {
+ path[len] = '\0';
+ pathLen = len;
+ }
+
+ mWorkingPath.Adopt(path, pathLen);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::OpenNSPRFileDesc(PRInt32 flags, PRInt32 mode, PRFileDesc **_retval)
+{
+ nsresult rv = ResolveAndStat();
+ if (NS_FAILED(rv) && rv != NS_ERROR_FILE_NOT_FOUND)
+ return rv;
+
+ *_retval = PR_Open(mResolvedPath.get(), flags, mode);
+ if (*_retval)
+ return NS_OK;
+
+ return NS_ErrorAccordingToNSPR();
+}
+
+
+NS_IMETHODIMP
+nsLocalFile::OpenANSIFileDesc(const char *mode, FILE * *_retval)
+{
+ nsresult rv = ResolveAndStat();
+ if (NS_FAILED(rv) && rv != NS_ERROR_FILE_NOT_FOUND)
+ return rv;
+
+ *_retval = fopen(mResolvedPath.get(), mode);
+ if (*_retval)
+ return NS_OK;
+
+ return NS_ERROR_FAILURE;
+}
+
+
+
+NS_IMETHODIMP
+nsLocalFile::Create(PRUint32 type, PRUint32 attributes)
+{
+ if (type != NORMAL_FILE_TYPE && type != DIRECTORY_TYPE)
+ return NS_ERROR_FILE_UNKNOWN_TYPE;
+
+ nsresult rv = ResolveAndStat();
+ if (NS_FAILED(rv) && rv != NS_ERROR_FILE_NOT_FOUND)
+ return rv;
+
+ // create directories to target
+ //
+ // A given local file can be either one of these forms:
+ //
+ // - normal: X:\some\path\on\this\drive
+ // ^--- start here
+ //
+ // - UNC path: \\machine\volume\some\path\on\this\drive
+ // ^--- start here
+ //
+ // Skip the first 'X:\' for the first form, and skip the first full
+ // '\\machine\volume\' segment for the second form.
+
+ const unsigned char* path = (const unsigned char*) mResolvedPath.get();
+ if (path[0] == '\\' && path[1] == '\\')
+ {
+ // dealing with a UNC path here; skip past '\\machine\'
+ path = _mbschr(path + 2, '\\');
+ if (!path)
+ return NS_ERROR_FILE_INVALID_PATH;
+ ++path;
+ }
+
+ // search for first slash after the drive (or volume) name
+ unsigned char* slash = _mbschr(path, '\\');
+
+ if (slash)
+ {
+ // skip the first '\\'
+ ++slash;
+ slash = _mbschr(slash, '\\');
+
+ while (slash)
+ {
+ *slash = '\0';
+
+ if (!CreateDirectoryA(mResolvedPath.get(), NULL)) {
+ rv = ConvertWinError(GetLastError());
+ // perhaps the base path already exists, or perhaps we don't have
+ // permissions to create the directory. NOTE: access denied could
+ // occur on a parent directory even though it exists.
+ if (rv != NS_ERROR_FILE_ALREADY_EXISTS &&
+ rv != NS_ERROR_FILE_ACCESS_DENIED)
+ return rv;
+ }
+ *slash = '\\';
+ ++slash;
+ slash = _mbschr(slash, '\\');
+ }
+ }
+
+ if (type == NORMAL_FILE_TYPE)
+ {
+ PRFileDesc* file = PR_Open(mResolvedPath.get(), PR_RDONLY | PR_CREATE_FILE | PR_APPEND | PR_EXCL, attributes);
+ if (!file) return NS_ERROR_FILE_ALREADY_EXISTS;
+
+ PR_Close(file);
+ return NS_OK;
+ }
+
+ if (type == DIRECTORY_TYPE)
+ {
+ if (!CreateDirectoryA(mResolvedPath.get(), NULL))
+ return ConvertWinError(GetLastError());
+ else
+ return NS_OK;
+ }
+
+ return NS_ERROR_FILE_UNKNOWN_TYPE;
+}
+
+NS_IMETHODIMP
+nsLocalFile::AppendNative(const nsACString &node)
+{
+ // append this path, multiple components are not permitted
+ return AppendNativeInternal(PromiseFlatCString(node), PR_FALSE);
+}
+
+NS_IMETHODIMP
+nsLocalFile::AppendRelativeNativePath(const nsACString &node)
+{
+ // append this path, multiple components are permitted
+ return AppendNativeInternal(PromiseFlatCString(node), PR_TRUE);
+}
+
+nsresult
+nsLocalFile::AppendNativeInternal(const nsAFlatCString &node, PRBool multipleComponents)
+{
+ if (node.IsEmpty())
+ return NS_OK;
+
+ // check the relative path for validity
+ const unsigned char * nodePath = (const unsigned char *) node.get();
+ if (*nodePath == '\\' // can't start with an '\'
+ || _mbschr(nodePath, '/') // can't contain /
+ || node.Equals("..")) // can't be ..
+ return NS_ERROR_FILE_UNRECOGNIZED_PATH;
+
+ if (multipleComponents)
+ {
+ // can't contain .. as a path component. Ensure that the valid components
+ // "foo..foo", "..foo", and "foo.." are not falsely detected, but the invalid
+ // paths "..\", "foo\..", "foo\..\foo", "..\foo", etc are.
+ unsigned char * doubleDot = _mbsstr(nodePath, (unsigned char *)"\\..");
+ while (doubleDot)
+ {
+ doubleDot += 3;
+ if (*doubleDot == '\0' || *doubleDot == '\\')
+ return NS_ERROR_FILE_UNRECOGNIZED_PATH;
+ doubleDot = _mbsstr(doubleDot, (unsigned char *)"\\..");
+ }
+ if (0 == _mbsncmp(nodePath, (unsigned char *)"..\\", 3)) // catches the remaining cases of prefixes
+ return NS_ERROR_FILE_UNRECOGNIZED_PATH;
+ }
+ else if (_mbschr(nodePath, '\\')) // single components can't contain '\'
+ return NS_ERROR_FILE_UNRECOGNIZED_PATH;
+
+ MakeDirty();
+
+ mWorkingPath.Append(NS_LITERAL_CSTRING("\\") + node);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::Normalize()
+{
+ // XXX See bug 187957 comment 18 for possible problems with this implementation.
+
+ if (mWorkingPath.IsEmpty())
+ return NS_OK;
+
+ // work in unicode for ease
+ nsAutoString path;
+ NS_CopyNativeToUnicode(mWorkingPath, path);
+
+ // find the index of the root backslash for the path. Everything before
+ // this is considered fully normalized and cannot be ascended beyond
+ // using ".." For a local drive this is the first slash (e.g. "c:\").
+ // For a UNC path it is the slash following the share name
+ // (e.g. "\\server\share\").
+ PRInt32 rootIdx = 2; // default to local drive
+ if (path.First() == '\\') // if a share then calculate the rootIdx
+ {
+ rootIdx = path.FindChar('\\', 2); // skip \\ in front of the server
+ if (rootIdx == kNotFound)
+ return NS_OK; // already normalized
+ rootIdx = path.FindChar('\\', rootIdx+1);
+ if (rootIdx == kNotFound)
+ return NS_OK; // already normalized
+ }
+ else if (path.CharAt(rootIdx) != '\\')
+ {
+ // The path has been specified relative to the current working directory
+ // for that drive. To normalize it, the current working directory for
+ // that drive needs to be inserted before the supplied relative path
+ // which will provide an absolute path (and the rootIdx will still be 2).
+ char cwd[MAX_PATH];
+ char * pcwd = cwd;
+ int drive = toupper(path.First()) - 'A' + 1;
+ if (!_getdcwd(drive, pcwd, MAX_PATH))
+ pcwd = _getdcwd(drive, 0, 0);
+ if (!pcwd)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ nsAutoString currentDir;
+ NS_CopyNativeToUnicode(nsDependentCString(pcwd), currentDir);
+ if (pcwd != cwd)
+ free(pcwd);
+
+ if (currentDir.Last() == '\\')
+ path.Replace(0, 2, currentDir);
+ else
+ path.Replace(0, 2, currentDir + NS_LITERAL_STRING("\\"));
+ }
+ NS_POSTCONDITION(0 < rootIdx && rootIdx < (PRInt32)path.Length(), "rootIdx is invalid");
+ NS_POSTCONDITION(path.CharAt(rootIdx) == '\\', "rootIdx is invalid");
+
+ // if there is nothing following the root path then it is already normalized
+ if (rootIdx + 1 == (PRInt32)path.Length())
+ return NS_OK;
+
+ // assign the root
+ nsAutoString normal;
+ const PRUnichar * pathBuffer = path.get(); // simplify access to the buffer
+ normal.SetCapacity(path.Length()); // it won't ever grow longer
+ normal.Assign(pathBuffer, rootIdx);
+
+ // Normalize the path components. The actions taken are:
+ //
+ // "\\" condense to single backslash
+ // "." remove from path
+ // ".." up a directory
+ // "..." remove from path (any number of dots > 2)
+ //
+ // The last form is something that Windows 95 and 98 supported and
+ // is a shortcut for changing up multiple directories. Windows XP
+ // and ilk ignore it in a path, as is done here.
+ PRInt32 len, begin, end = rootIdx;
+ while (end < (PRInt32)path.Length())
+ {
+ // find the current segment (text between the backslashes) to
+ // be examined, this will set the following variables:
+ // begin == index of first char in segment
+ // end == index 1 char after last char in segment
+ // len == length of segment
+ begin = end + 1;
+ end = path.FindChar('\\', begin);
+ if (end == kNotFound)
+ end = path.Length();
+ len = end - begin;
+
+ // ignore double backslashes
+ if (len == 0)
+ continue;
+
+ // len != 0, and interesting paths always begin with a dot
+ if (pathBuffer[begin] == '.')
+ {
+ // ignore single dots
+ if (len == 1)
+ continue;
+
+ // handle multiple dots
+ if (len >= 2 && pathBuffer[begin+1] == '.')
+ {
+ // back up a path component on double dot
+ if (len == 2)
+ {
+ PRInt32 prev = normal.RFindChar('\\');
+ if (prev >= rootIdx)
+ normal.Truncate(prev);
+ continue;
+ }
+
+ // length is > 2 and the first two characters are dots.
+ // if the rest of the string is dots, then ignore it.
+ int idx = len - 1;
+ for (; idx >= 2; --idx)
+ {
+ if (pathBuffer[begin+idx] != '.')
+ break;
+ }
+
+ // this is true if the loop above didn't break
+ // and all characters in this segment are dots.
+ if (idx < 2)
+ continue;
+ }
+ }
+
+ // add the current component to the path, including the preceding backslash
+ normal.Append(pathBuffer + begin - 1, len + 1);
+ }
+
+ NS_CopyUnicodeToNative(normal, mWorkingPath);
+ MakeDirty();
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::GetNativeLeafName(nsACString &aLeafName)
+{
+ aLeafName.Truncate();
+
+ const char* temp = mWorkingPath.get();
+ if(temp == nsnull)
+ return NS_ERROR_FILE_UNRECOGNIZED_PATH;
+
+ const char* leaf = (const char*) _mbsrchr((const unsigned char*) temp, '\\');
+
+ // if the working path is just a node without any lashes.
+ if (leaf == nsnull)
+ leaf = temp;
+ else
+ leaf++;
+
+ aLeafName.Assign(leaf);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::SetNativeLeafName(const nsACString &aLeafName)
+{
+ MakeDirty();
+
+ const unsigned char* temp = (const unsigned char*) mWorkingPath.get();
+ if(temp == nsnull)
+ return NS_ERROR_FILE_UNRECOGNIZED_PATH;
+
+ // cannot use nsCString::RFindChar() due to 0x5c problem
+ PRInt32 offset = (PRInt32) (_mbsrchr(temp, '\\') - temp);
+ if (offset)
+ {
+ mWorkingPath.Truncate(offset+1);
+ }
+ mWorkingPath.Append(aLeafName);
+
+ return NS_OK;
+}
+
+
+NS_IMETHODIMP
+nsLocalFile::GetNativePath(nsACString &_retval)
+{
+ _retval = mWorkingPath;
+ return NS_OK;
+}
+
+nsresult
+nsLocalFile::CopySingleFile(nsIFile *sourceFile, nsIFile *destParent, const nsACString &newName,
+ PRBool followSymlinks, PRBool move)
+{
+ nsresult rv;
+ nsCAutoString filePath;
+
+ // get the path that we are going to copy to.
+ // Since windows does not know how to auto
+ // resolve shortcuts, we must work with the
+ // target.
+ nsCAutoString destPath;
+ destParent->GetNativeTarget(destPath);
+
+ destPath.Append("\\");
+
+ if (newName.IsEmpty())
+ {
+ nsCAutoString aFileName;
+ sourceFile->GetNativeLeafName(aFileName);
+ destPath.Append(aFileName);
+ }
+ else
+ {
+ destPath.Append(newName);
+ }
+
+
+ if (followSymlinks)
+ {
+ rv = sourceFile->GetNativeTarget(filePath);
+ if (filePath.IsEmpty())
+ rv = sourceFile->GetNativePath(filePath);
+ }
+ else
+ {
+ rv = sourceFile->GetNativePath(filePath);
+ }
+
+ if (NS_FAILED(rv))
+ return rv;
+
+ int copyOK;
+
+ if (!move)
+ copyOK = CopyFile(filePath.get(), destPath.get(), PR_TRUE);
+ else
+ {
+ // What we have to do is check to see if the destPath exists. If it
+ // does, we have to move it out of the say so that MoveFile will
+ // succeed. However, we don't want to just remove it since MoveFile
+ // can fail leaving us without a file.
+
+ nsCAutoString backup;
+ PRFileInfo64 fileInfo64;
+ PRStatus status = PR_GetFileInfo64(destPath.get(), &fileInfo64);
+ if (status == PR_SUCCESS)
+ {
+
+ // the file exists. Check to make sure it is not a directory,
+ // then move it out of the way.
+ if (fileInfo64.type == PR_FILE_FILE)
+ {
+ backup.Append(destPath);
+ backup.Append(".moztmp");
+
+ // remove any existing backup file that we may already have.
+ // maybe we should be doing some kind of unique naming here,
+ // but why bother.
+ remove(backup.get());
+
+ // move destination file to backup file
+ copyOK = MoveFile(destPath.get(), backup.get());
+ if (!copyOK)
+ {
+ // I guess we can't do the backup copy, so return.
+ rv = ConvertWinError(GetLastError());
+ return rv;
+ }
+ }
+ }
+ // move source file to destination file
+ copyOK = MoveFile(filePath.get(), destPath.get());
+
+ if (!backup.IsEmpty())
+ {
+ if (copyOK)
+ {
+ // remove the backup copy.
+ remove(backup.get());
+ }
+ else
+ {
+ // restore backup
+ int backupOk = MoveFile(backup.get(), destPath.get());
+ NS_ASSERTION(backupOk, "move backup failed");
+ }
+ }
+ }
+ if (!copyOK) // CopyFile and MoveFile returns non-zero if succeeds (backward if you ask me).
+ rv = ConvertWinError(GetLastError());
+
+ return rv;
+}
+
+
+nsresult
+nsLocalFile::CopyMove(nsIFile *aParentDir, const nsACString &newName, PRBool followSymlinks, PRBool move)
+{
+ nsCOMPtr<nsIFile> newParentDir = aParentDir;
+ // check to see if this exists, otherwise return an error.
+ // we will check this by resolving. If the user wants us
+ // to follow links, then we are talking about the target,
+ // hence we can use the |followSymlinks| parameter.
+ nsresult rv = ResolveAndStat();
+ if (NS_FAILED(rv))
+ return rv;
+
+ if (!newParentDir)
+ {
+ // no parent was specified. We must rename.
+
+ if (newName.IsEmpty())
+ return NS_ERROR_INVALID_ARG;
+
+ rv = GetParent(getter_AddRefs(newParentDir));
+ if (NS_FAILED(rv))
+ return rv;
+ }
+
+ if (!newParentDir)
+ return NS_ERROR_FILE_DESTINATION_NOT_DIR;
+
+ // make sure it exists and is a directory. Create it if not there.
+ PRBool exists;
+ newParentDir->Exists(&exists);
+ if (!exists)
+ {
+ rv = newParentDir->Create(DIRECTORY_TYPE, 0644); // TODO, what permissions should we use
+ if (NS_FAILED(rv))
+ return rv;
+ }
+ else
+ {
+ PRBool isDir;
+ newParentDir->IsDirectory(&isDir);
+ if (isDir == PR_FALSE)
+ {
+ if (followSymlinks)
+ {
+ PRBool isLink;
+ newParentDir->IsSymlink(&isLink);
+ if (isLink)
+ {
+ nsCAutoString target;
+ newParentDir->GetNativeTarget(target);
+
+ nsCOMPtr<nsILocalFile> realDest = new nsLocalFile();
+ if (realDest == nsnull)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ rv = realDest->InitWithNativePath(target);
+
+ if (NS_FAILED(rv))
+ return rv;
+
+ return CopyMove(realDest, newName, followSymlinks, move);
+ }
+ }
+ else
+ {
+ return NS_ERROR_FILE_DESTINATION_NOT_DIR;
+ }
+ }
+ }
+
+ // check to see if we are a directory, if so enumerate it.
+
+ PRBool isDir;
+ IsDirectory(&isDir);
+ PRBool isSymlink;
+ IsSymlink(&isSymlink);
+
+ if (!isDir || (isSymlink && !followSymlinks))
+ {
+ rv = CopySingleFile(this, newParentDir, newName, followSymlinks, move);
+ if (NS_FAILED(rv))
+ return rv;
+ }
+ else
+ {
+ // create a new target destination in the new parentDir;
+ nsCOMPtr<nsIFile> target;
+ rv = newParentDir->Clone(getter_AddRefs(target));
+
+ if (NS_FAILED(rv))
+ return rv;
+
+ nsCAutoString allocatedNewName;
+ if (newName.IsEmpty())
+ {
+ PRBool isLink;
+ IsSymlink(&isLink);
+ if (isLink)
+ {
+ nsCAutoString temp;
+ GetNativeTarget(temp);
+ const char* leaf = (const char*) _mbsrchr((const unsigned char*) temp.get(), '\\');
+ if (leaf[0] == '\\')
+ leaf++;
+ allocatedNewName = leaf;
+ }
+ else
+ {
+ GetNativeLeafName(allocatedNewName);// this should be the leaf name of the
+ }
+ }
+ else
+ {
+ allocatedNewName = newName;
+ }
+
+ rv = target->AppendNative(allocatedNewName);
+ if (NS_FAILED(rv))
+ return rv;
+
+ allocatedNewName.Truncate();
+
+ // check if the destination directory already exists
+ target->Exists(&exists);
+ if (!exists)
+ {
+ // if the destination directory cannot be created, return an error
+ rv = target->Create(DIRECTORY_TYPE, 0644); // TODO, what permissions should we use
+ if (NS_FAILED(rv))
+ return rv;
+ }
+ else
+ {
+ // check if the destination directory is writable and empty
+ PRBool isWritable;
+
+ target->IsWritable(&isWritable);
+ if (!isWritable)
+ return NS_ERROR_FILE_ACCESS_DENIED;
+
+ nsCOMPtr<nsISimpleEnumerator> targetIterator;
+ rv = target->GetDirectoryEntries(getter_AddRefs(targetIterator));
+
+ PRBool more;
+ targetIterator->HasMoreElements(&more);
+ // return error if target directory is not empty
+ if (more)
+ return NS_ERROR_FILE_DIR_NOT_EMPTY;
+ }
+
+ nsDirEnumerator dirEnum;
+
+ rv = dirEnum.Init(this);
+ if (NS_FAILED(rv)) {
+ NS_WARNING("dirEnum initalization failed");
+ return rv;
+ }
+
+ PRBool more;
+ while (NS_SUCCEEDED(dirEnum.HasMoreElements(&more)) && more)
+ {
+ nsCOMPtr<nsISupports> item;
+ nsCOMPtr<nsIFile> file;
+ dirEnum.GetNext(getter_AddRefs(item));
+ file = do_QueryInterface(item);
+ if (file)
+ {
+ PRBool isDir, isLink;
+
+ file->IsDirectory(&isDir);
+ file->IsSymlink(&isLink);
+
+ if (move)
+ {
+ if (followSymlinks)
+ return NS_ERROR_FAILURE;
+
+ rv = file->MoveToNative(target, nsCString());
+ NS_ENSURE_SUCCESS(rv,rv);
+ }
+ else
+ {
+ if (followSymlinks)
+ rv = file->CopyToFollowingLinksNative(target, nsCString());
+ else
+ rv = file->CopyToNative(target, nsCString());
+ NS_ENSURE_SUCCESS(rv,rv);
+ }
+ }
+ }
+ // we've finished moving all the children of this directory
+ // in the new directory. so now delete the directory
+ // note, we don't need to do a recursive delete.
+ // MoveTo() is recursive. At this point,
+ // we've already moved the children of the current folder
+ // to the new location. nothing should be left in the folder.
+ if (move)
+ {
+ rv = Remove(PR_FALSE /* recursive */);
+ NS_ENSURE_SUCCESS(rv,rv);
+ }
+ }
+
+
+ // If we moved, we want to adjust this.
+ if (move)
+ {
+ MakeDirty();
+
+ nsCAutoString newParentPath;
+ newParentDir->GetNativePath(newParentPath);
+
+ if (newParentPath.IsEmpty())
+ return NS_ERROR_FAILURE;
+
+ if (newName.IsEmpty())
+ {
+ nsCAutoString aFileName;
+ GetNativeLeafName(aFileName);
+
+ InitWithNativePath(newParentPath);
+ AppendNative(aFileName);
+ }
+ else
+ {
+ InitWithNativePath(newParentPath);
+ AppendNative(newName);
+ }
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::CopyToNative(nsIFile *newParentDir, const nsACString &newName)
+{
+ return CopyMove(newParentDir, newName, PR_FALSE, PR_FALSE);
+}
+
+NS_IMETHODIMP
+nsLocalFile::CopyToFollowingLinksNative(nsIFile *newParentDir, const nsACString &newName)
+{
+ return CopyMove(newParentDir, newName, PR_TRUE, PR_FALSE);
+}
+
+NS_IMETHODIMP
+nsLocalFile::MoveToNative(nsIFile *newParentDir, const nsACString &newName)
+{
+ return CopyMove(newParentDir, newName, PR_FALSE, PR_TRUE);
+}
+
+NS_IMETHODIMP
+nsLocalFile::Load(PRLibrary * *_retval)
+{
+ PRBool isFile;
+ nsresult rv = IsFile(&isFile);
+
+ if (NS_FAILED(rv))
+ return rv;
+
+ if (! isFile)
+ return NS_ERROR_FILE_IS_DIRECTORY;
+
+ NS_TIMELINE_START_TIMER("PR_LoadLibrary");
+ *_retval = PR_LoadLibrary(mResolvedPath.get());
+ NS_TIMELINE_STOP_TIMER("PR_LoadLibrary");
+ NS_TIMELINE_MARK_TIMER1("PR_LoadLibrary", mResolvedPath.get());
+
+ if (*_retval)
+ return NS_OK;
+
+ return NS_ERROR_NULL_POINTER;
+}
+
+NS_IMETHODIMP
+nsLocalFile::Remove(PRBool recursive)
+{
+ // NOTE:
+ //
+ // if the working path points to a shortcut, then we will only
+ // delete the shortcut itself. even if the shortcut points to
+ // a directory, we will not recurse into that directory or
+ // delete that directory itself. likewise, if the shortcut
+ // points to a normal file, we will not delete the real file.
+ // this is done to be consistent with the other platforms that
+ // behave this way. we do this even if the followLinks attribute
+ // is set to true. this helps protect against misuse that could
+ // lead to security bugs (e.g., bug 210588).
+ //
+ // Since shortcut files are no longer permitted to be used as unix-like
+ // symlinks interspersed in the path (e.g. "c:/file.lnk/foo/bar.txt")
+ // this processing is a lot simpler. Even if the shortcut file is
+ // pointing to a directory, only the mWorkingPath value is used and so
+ // only the shortcut file will be deleted.
+
+ PRBool isDir, isLink;
+ nsresult rv;
+
+ isDir = PR_FALSE;
+ rv = IsSymlink(&isLink);
+ if (NS_FAILED(rv))
+ return rv;
+
+ // only check to see if we have a directory if it isn't a link
+ if (!isLink)
+ {
+ rv = IsDirectory(&isDir);
+ if (NS_FAILED(rv))
+ return rv;
+ }
+
+ if (isDir)
+ {
+ if (recursive)
+ {
+ nsDirEnumerator dirEnum;
+
+ rv = dirEnum.Init(this);
+ if (NS_FAILED(rv))
+ return rv;
+
+ PRBool more;
+ while (NS_SUCCEEDED(dirEnum.HasMoreElements(&more)) && more)
+ {
+ nsCOMPtr<nsISupports> item;
+ dirEnum.GetNext(getter_AddRefs(item));
+ nsCOMPtr<nsIFile> file = do_QueryInterface(item);
+ if (file)
+ file->Remove(recursive);
+ }
+ }
+ rv = rmdir(mWorkingPath.get());
+ }
+ else
+ {
+ rv = remove(mWorkingPath.get());
+ }
+
+ // fixup error code if necessary...
+ if (rv == (nsresult)-1)
+ rv = NSRESULT_FOR_ERRNO();
+
+ MakeDirty();
+ return rv;
+}
+
+NS_IMETHODIMP
+nsLocalFile::GetLastModifiedTime(PRInt64 *aLastModifiedTime)
+{
+ NS_ENSURE_ARG(aLastModifiedTime);
+
+ // get the modified time of the target as determined by mFollowSymlinks
+ // If PR_TRUE, then this will be for the target of the shortcut file,
+ // otherwise it will be for the shortcut file itself (i.e. the same
+ // results as GetLastModifiedTimeOfLink)
+
+ nsresult rv = ResolveAndStat();
+ if (NS_FAILED(rv))
+ return rv;
+
+ // microseconds -> milliseconds
+ PRInt64 usecPerMsec;
+ LL_I2L(usecPerMsec, PR_USEC_PER_MSEC);
+ LL_DIV(*aLastModifiedTime, mFileInfo64.modifyTime, usecPerMsec);
+ return NS_OK;
+}
+
+
+NS_IMETHODIMP
+nsLocalFile::GetLastModifiedTimeOfLink(PRInt64 *aLastModifiedTime)
+{
+ NS_ENSURE_ARG(aLastModifiedTime);
+
+ // The caller is assumed to have already called IsSymlink
+ // and to have found that this file is a link.
+
+ PRFileInfo64 info;
+ if (PR_GetFileInfo64(mWorkingPath.get(), &info) != PR_SUCCESS)
+ return NSRESULT_FOR_ERRNO();
+
+ // microseconds -> milliseconds
+ PRInt64 usecPerMsec;
+ LL_I2L(usecPerMsec, PR_USEC_PER_MSEC);
+ LL_DIV(*aLastModifiedTime, info.modifyTime, usecPerMsec);
+ return NS_OK;
+}
+
+
+NS_IMETHODIMP
+nsLocalFile::SetLastModifiedTime(PRInt64 aLastModifiedTime)
+{
+ nsresult rv = ResolveAndStat();
+ if (NS_FAILED(rv))
+ return rv;
+
+ // set the modified time of the target as determined by mFollowSymlinks
+ // If PR_TRUE, then this will be for the target of the shortcut file,
+ // otherwise it will be for the shortcut file itself (i.e. the same
+ // results as SetLastModifiedTimeOfLink)
+
+ rv = SetModDate(aLastModifiedTime, mResolvedPath.get());
+ if (NS_SUCCEEDED(rv))
+ MakeDirty();
+
+ return rv;
+}
+
+
+NS_IMETHODIMP
+nsLocalFile::SetLastModifiedTimeOfLink(PRInt64 aLastModifiedTime)
+{
+ // The caller is assumed to have already called IsSymlink
+ // and to have found that this file is a link.
+
+ nsresult rv = SetModDate(aLastModifiedTime, mWorkingPath.get());
+ if (NS_SUCCEEDED(rv))
+ MakeDirty();
+
+ return rv;
+}
+
+nsresult
+nsLocalFile::SetModDate(PRInt64 aLastModifiedTime, const char *filePath)
+{
+ HANDLE file = CreateFile(filePath, // pointer to name of the file
+ GENERIC_WRITE, // access (write) mode
+ 0, // share mode
+ NULL, // pointer to security attributes
+ OPEN_EXISTING, // how to create
+ 0, // file attributes
+ NULL);
+ if (file == INVALID_HANDLE_VALUE)
+ {
+ return ConvertWinError(GetLastError());
+ }
+
+ FILETIME lft, ft;
+ SYSTEMTIME st;
+ PRExplodedTime pret;
+
+ // PR_ExplodeTime expects usecs...
+ PR_ExplodeTime(aLastModifiedTime * PR_USEC_PER_MSEC, PR_LocalTimeParameters, &pret);
+ st.wYear = pret.tm_year;
+ st.wMonth = pret.tm_month + 1; // Convert start offset -- Win32: Jan=1; NSPR: Jan=0
+ st.wDayOfWeek = pret.tm_wday;
+ st.wDay = pret.tm_mday;
+ st.wHour = pret.tm_hour;
+ st.wMinute = pret.tm_min;
+ st.wSecond = pret.tm_sec;
+ st.wMilliseconds = pret.tm_usec/1000;
+
+ nsresult rv = NS_OK;
+ if ( 0 != SystemTimeToFileTime(&st, &lft)
+ || 0 != LocalFileTimeToFileTime(&lft, &ft)
+ || 0 != SetFileTime(file, NULL, &ft, &ft) )
+ {
+ rv = ConvertWinError(GetLastError());
+ }
+
+ CloseHandle(file);
+ return rv;
+}
+
+NS_IMETHODIMP
+nsLocalFile::GetPermissions(PRUint32 *aPermissions)
+{
+ NS_ENSURE_ARG(aPermissions);
+
+ // get the permissions of the target as determined by mFollowSymlinks
+ // If PR_TRUE, then this will be for the target of the shortcut file,
+ // otherwise it will be for the shortcut file itself (i.e. the same
+ // results as GetPermissionsOfLink)
+ nsresult rv = ResolveAndStat();
+ if (NS_FAILED(rv))
+ return rv;
+
+ PRBool isWritable, isExecutable;
+ IsWritable(&isWritable);
+ IsExecutable(&isExecutable);
+
+ *aPermissions = PR_IRUSR|PR_IRGRP|PR_IROTH; // all read
+ if (isWritable)
+ *aPermissions |= PR_IWUSR|PR_IWGRP|PR_IWOTH; // all write
+ if (isExecutable)
+ *aPermissions |= PR_IXUSR|PR_IXGRP|PR_IXOTH; // all execute
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::GetPermissionsOfLink(PRUint32 *aPermissions)
+{
+ NS_ENSURE_ARG(aPermissions);
+
+ // The caller is assumed to have already called IsSymlink
+ // and to have found that this file is a link. It is not
+ // possible for a link file to be executable.
+
+ DWORD word = GetFileAttributes(mWorkingPath.get());
+ if (word == INVALID_FILE_ATTRIBUTES)
+ return NS_ERROR_FILE_INVALID_PATH;
+
+ PRBool isWritable = !(word & FILE_ATTRIBUTE_READONLY);
+ *aPermissions = PR_IRUSR|PR_IRGRP|PR_IROTH; // all read
+ if (isWritable)
+ *aPermissions |= PR_IWUSR|PR_IWGRP|PR_IWOTH; // all write
+
+ return NS_OK;
+}
+
+
+NS_IMETHODIMP
+nsLocalFile::SetPermissions(PRUint32 aPermissions)
+{
+ // set the permissions of the target as determined by mFollowSymlinks
+ // If PR_TRUE, then this will be for the target of the shortcut file,
+ // otherwise it will be for the shortcut file itself (i.e. the same
+ // results as SetPermissionsOfLink)
+ nsresult rv = ResolveAndStat();
+ if (NS_FAILED(rv))
+ return rv;
+
+ // windows only knows about the following permissions
+ int mode = 0;
+ if (aPermissions & (PR_IRUSR|PR_IRGRP|PR_IROTH)) // any read
+ mode |= _S_IREAD;
+ if (aPermissions & (PR_IWUSR|PR_IWGRP|PR_IWOTH)) // any write
+ mode |= _S_IWRITE;
+
+ if (chmod(mResolvedPath.get(), mode) == -1)
+ return NS_ERROR_FAILURE;
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::SetPermissionsOfLink(PRUint32 aPermissions)
+{
+ // The caller is assumed to have already called IsSymlink
+ // and to have found that this file is a link.
+
+ // windows only knows about the following permissions
+ int mode = 0;
+ if (aPermissions & (PR_IRUSR|PR_IRGRP|PR_IROTH)) // any read
+ mode |= _S_IREAD;
+ if (aPermissions & (PR_IWUSR|PR_IWGRP|PR_IWOTH)) // any write
+ mode |= _S_IWRITE;
+
+ if (chmod(mWorkingPath.get(), mode) == -1)
+ return NS_ERROR_FAILURE;
+
+ return NS_OK;
+}
+
+
+NS_IMETHODIMP
+nsLocalFile::GetFileSize(PRInt64 *aFileSize)
+{
+ NS_ENSURE_ARG(aFileSize);
+
+ nsresult rv = ResolveAndStat();
+ if (NS_FAILED(rv))
+ return rv;
+
+ *aFileSize = mFileInfo64.size;
+ return NS_OK;
+}
+
+
+NS_IMETHODIMP
+nsLocalFile::GetFileSizeOfLink(PRInt64 *aFileSize)
+{
+ NS_ENSURE_ARG(aFileSize);
+
+ // The caller is assumed to have already called IsSymlink
+ // and to have found that this file is a link.
+
+ PRFileInfo64 info;
+ if (!PR_GetFileInfo64(mWorkingPath.get(), &info))
+ return NS_ERROR_FILE_INVALID_PATH;
+
+ *aFileSize = info.size;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::SetFileSize(PRInt64 aFileSize)
+{
+ nsresult rv = ResolveAndStat();
+ if (NS_FAILED(rv))
+ return rv;
+
+ HANDLE hFile = CreateFile(mResolvedPath.get(), // pointer to name of the file
+ GENERIC_WRITE, // access (write) mode
+ FILE_SHARE_READ, // share mode
+ NULL, // pointer to security attributes
+ OPEN_EXISTING, // how to create
+ FILE_ATTRIBUTE_NORMAL, // file attributes
+ NULL);
+ if (hFile == INVALID_HANDLE_VALUE)
+ {
+ return ConvertWinError(GetLastError());
+ }
+
+ // seek the file pointer to the new, desired end of file
+ // and then truncate the file at that position
+ rv = NS_ERROR_FAILURE;
+ aFileSize = MyFileSeek64(hFile, aFileSize, FILE_BEGIN);
+ if (aFileSize != -1 && SetEndOfFile(hFile))
+ {
+ MakeDirty();
+ rv = NS_OK;
+ }
+
+ CloseHandle(hFile);
+ return rv;
+}
+
+typedef BOOL (WINAPI *fpGetDiskFreeSpaceExA)(LPCTSTR lpDirectoryName,
+ PULARGE_INTEGER lpFreeBytesAvailableToCaller,
+ PULARGE_INTEGER lpTotalNumberOfBytes,
+ PULARGE_INTEGER lpTotalNumberOfFreeBytes);
+
+NS_IMETHODIMP
+nsLocalFile::GetDiskSpaceAvailable(PRInt64 *aDiskSpaceAvailable)
+{
+ NS_ENSURE_ARG(aDiskSpaceAvailable);
+
+ ResolveAndStat();
+
+ // Attempt to check disk space using the GetDiskFreeSpaceExA function.
+ // --- FROM MSDN ---
+ // The GetDiskFreeSpaceEx function is available beginning with Windows 95 OEM Service
+ // Release 2 (OSR2). To determine whether GetDiskFreeSpaceEx is available, call
+ // GetModuleHandle to get the handle to Kernel32.dll. Then you can call GetProcAddress.
+ // It is not necessary to call LoadLibrary on Kernel32.dll because it is already loaded
+ // into every process address space.
+ fpGetDiskFreeSpaceExA pGetDiskFreeSpaceExA = (fpGetDiskFreeSpaceExA)
+ GetProcAddress(GetModuleHandle("kernel32.dll"), "GetDiskFreeSpaceExA");
+ if (pGetDiskFreeSpaceExA)
+ {
+ ULARGE_INTEGER liFreeBytesAvailableToCaller, liTotalNumberOfBytes;
+ if (pGetDiskFreeSpaceExA(mResolvedPath.get(), &liFreeBytesAvailableToCaller,
+ &liTotalNumberOfBytes, NULL))
+ {
+ *aDiskSpaceAvailable = liFreeBytesAvailableToCaller.QuadPart;
+ return NS_OK;
+ }
+ }
+
+ // use the old method of getting available disk space
+ char aDrive[_MAX_DRIVE + 2];
+ _splitpath( mResolvedPath.get(), aDrive, NULL, NULL, NULL);
+ strcat(aDrive, "\\");
+
+ DWORD dwSecPerClus, dwBytesPerSec, dwFreeClus, dwTotalClus;
+ if (GetDiskFreeSpace(aDrive, &dwSecPerClus, &dwBytesPerSec, &dwFreeClus, &dwTotalClus))
+ {
+ __int64 bytes = dwFreeClus;
+ bytes *= dwSecPerClus;
+ bytes *= dwBytesPerSec;
+
+ *aDiskSpaceAvailable = bytes;
+ return NS_OK;
+ }
+
+ return NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+nsLocalFile::GetParent(nsIFile * *aParent)
+{
+ NS_ENSURE_ARG_POINTER(aParent);
+
+ nsCAutoString parentPath(mWorkingPath);
+
+ // cannot use nsCString::RFindChar() due to 0x5c problem
+ PRInt32 offset = (PRInt32) (_mbsrchr((const unsigned char *) parentPath.get(), '\\')
+ - (const unsigned char *) parentPath.get());
+ // adding this offset check that was removed in bug 241708 fixes mail
+ // directories that aren't relative to/underneath the profile dir.
+ // e.g., on a different drive. Before you remove them, please make
+ // sure local mail directories that aren't underneath the profile dir work.
+ if (offset < 0)
+ return NS_ERROR_FILE_UNRECOGNIZED_PATH;
+
+ if (offset == 1 && parentPath[0] == '\\') {
+ aParent = nsnull;
+ return NS_OK;
+ }
+ if (offset > 0)
+ parentPath.Truncate(offset);
+ else
+ parentPath.AssignLiteral("\\\\.");
+
+ nsCOMPtr<nsILocalFile> localFile;
+ nsresult rv = NS_NewNativeLocalFile(parentPath, mFollowSymlinks, getter_AddRefs(localFile));
+
+ if(NS_SUCCEEDED(rv) && localFile)
+ {
+ return CallQueryInterface(localFile, aParent);
+ }
+ return rv;
+}
+
+NS_IMETHODIMP
+nsLocalFile::Exists(PRBool *_retval)
+{
+ NS_ENSURE_ARG(_retval);
+ *_retval = PR_FALSE;
+
+ MakeDirty();
+ nsresult rv = ResolveAndStat();
+ *_retval = NS_SUCCEEDED(rv);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::IsWritable(PRBool *aIsWritable)
+{
+ //TODO: extend to support NTFS file permissions
+
+ // The read-only attribute on a FAT directory only means that it can't
+ // be deleted. It is still possible to modify the contents of the directory.
+ nsresult rv = IsDirectory(aIsWritable);
+ if (NS_FAILED(rv))
+ return rv;
+ if (*aIsWritable)
+ return NS_OK;
+
+ // writable if the file doesn't have the readonly attribute
+ rv = HasFileAttribute(FILE_ATTRIBUTE_READONLY, aIsWritable);
+ if (NS_FAILED(rv))
+ return rv;
+ *aIsWritable = !*aIsWritable;
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::IsReadable(PRBool *_retval)
+{
+ NS_ENSURE_ARG(_retval);
+ *_retval = PR_FALSE;
+
+ nsresult rv = ResolveAndStat();
+ if (NS_FAILED(rv))
+ return rv;
+
+ *_retval = PR_TRUE;
+ return NS_OK;
+}
+
+
+NS_IMETHODIMP
+nsLocalFile::IsExecutable(PRBool *_retval)
+{
+ NS_ENSURE_ARG(_retval);
+ *_retval = PR_FALSE;
+
+ nsresult rv;
+
+ // only files can be executables
+ PRBool isFile;
+ rv = IsFile(&isFile);
+ if (NS_FAILED(rv))
+ return rv;
+ if (!isFile)
+ return NS_OK;
+
+ //TODO: shouldn't we be checking mFollowSymlinks here?
+ PRBool symLink;
+ rv = IsSymlink(&symLink);
+ if (NS_FAILED(rv))
+ return rv;
+
+ nsCAutoString path;
+ if (symLink)
+ GetNativeTarget(path);
+ else
+ GetNativePath(path);
+
+ // Get extension.
+ char * ext = (char *) _mbsrchr((unsigned char *)path.BeginWriting(), '.');
+ if ( ext ) {
+ // Convert extension to lower case.
+ for( unsigned char *p = (unsigned char *)ext; *p; p++ )
+ *p = _mbctolower( *p );
+
+ // Search for any of the set of executable extensions.
+ const char * const executableExts[] = {
+ ".ad",
+ ".adp",
+ ".asp",
+ ".bas",
+ ".bat",
+ ".chm",
+ ".cmd",
+ ".com",
+ ".cpl",
+ ".crt",
+ ".exe",
+ ".hlp",
+ ".hta",
+ ".inf",
+ ".ins",
+ ".isp",
+ ".js",
+ ".jse",
+ ".lnk",
+ ".mdb",
+ ".mde",
+ ".msc",
+ ".msi",
+ ".msp",
+ ".mst",
+ ".pcd",
+ ".pif",
+ ".reg",
+ ".scr",
+ ".sct",
+ ".shb",
+ ".shs",
+ ".url",
+ ".vb",
+ ".vbe",
+ ".vbs",
+ ".vsd",
+ ".vss",
+ ".vst",
+ ".vsw",
+ ".ws",
+ ".wsc",
+ ".wsf",
+ ".wsh",
+ 0 };
+ for ( int i = 0; executableExts[i]; i++ ) {
+ if ( ::strcmp( executableExts[i], ext ) == 0 ) {
+ // Found a match. Set result and quit.
+ *_retval = PR_TRUE;
+ break;
+ }
+ }
+ }
+
+ return NS_OK;
+}
+
+
+NS_IMETHODIMP
+nsLocalFile::IsDirectory(PRBool *_retval)
+{
+ NS_ENSURE_ARG(_retval);
+
+ nsresult rv = ResolveAndStat();
+ if (NS_FAILED(rv))
+ return rv;
+
+ *_retval = (mFileInfo64.type == PR_FILE_DIRECTORY);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::IsFile(PRBool *_retval)
+{
+ NS_ENSURE_ARG(_retval);
+
+ nsresult rv = ResolveAndStat();
+ if (NS_FAILED(rv))
+ return rv;
+
+ *_retval = (mFileInfo64.type == PR_FILE_FILE);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::IsHidden(PRBool *_retval)
+{
+ return HasFileAttribute(FILE_ATTRIBUTE_HIDDEN, _retval);
+}
+
+nsresult
+nsLocalFile::HasFileAttribute(DWORD fileAttrib, PRBool *_retval)
+{
+ NS_ENSURE_ARG(_retval);
+
+ nsresult rv = ResolveAndStat();
+ if (NS_FAILED(rv))
+ return rv;
+
+ // get the file attributes for the correct item depending on following symlinks
+ const char *filePath = mFollowSymlinks ? mResolvedPath.get() : mWorkingPath.get();
+ DWORD word = GetFileAttributes(filePath);
+
+ *_retval = ((word & fileAttrib) != 0);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::IsSymlink(PRBool *_retval)
+{
+ NS_ENSURE_ARG(_retval);
+
+ // unless it is a valid shortcut path it's not a symlink
+ if (!IsShortcutPath(mWorkingPath.get()))
+ {
+ *_retval = PR_FALSE;
+ return NS_OK;
+ }
+
+ // we need to know if this is a file or directory
+ nsresult rv = ResolveAndStat();
+ if (NS_FAILED(rv))
+ return rv;
+
+ // it's only a shortcut if it is a file
+ *_retval = (mFileInfo64.type == PR_FILE_FILE);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::IsSpecial(PRBool *_retval)
+{
+ return HasFileAttribute(FILE_ATTRIBUTE_SYSTEM, _retval);
+}
+
+NS_IMETHODIMP
+nsLocalFile::Equals(nsIFile *inFile, PRBool *_retval)
+{
+ NS_ENSURE_ARG(inFile);
+ NS_ENSURE_ARG(_retval);
+
+ nsCAutoString inFilePath;
+ inFile->GetNativePath(inFilePath);
+
+ *_retval = inFilePath.Equals(mWorkingPath);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::Contains(nsIFile *inFile, PRBool recur, PRBool *_retval)
+{
+ *_retval = PR_FALSE;
+
+ nsCAutoString myFilePath;
+ if ( NS_FAILED(GetNativeTarget(myFilePath)))
+ GetNativePath(myFilePath);
+
+ PRInt32 myFilePathLen = myFilePath.Length();
+
+ nsCAutoString inFilePath;
+ if ( NS_FAILED(inFile->GetNativeTarget(inFilePath)))
+ inFile->GetNativePath(inFilePath);
+
+ if ( strnicmp( myFilePath.get(), inFilePath.get(), myFilePathLen) == 0)
+ {
+ // now make sure that the |inFile|'s path has a trailing
+ // separator.
+
+ if (inFilePath[myFilePathLen] == '\\')
+ {
+ *_retval = PR_TRUE;
+ }
+
+ }
+
+ return NS_OK;
+}
+
+
+
+NS_IMETHODIMP
+nsLocalFile::GetNativeTarget(nsACString &_retval)
+{
+ _retval.Truncate();
+#if STRICT_FAKE_SYMLINKS
+ PRBool symLink;
+
+ nsresult rv = IsSymlink(&symLink);
+ if (NS_FAILED(rv))
+ return rv;
+
+ if (!symLink)
+ {
+ return NS_ERROR_FILE_INVALID_PATH;
+ }
+#endif
+ ResolveAndStat();
+
+ _retval = mResolvedPath;
+ return NS_OK;
+}
+
+
+/* attribute PRBool followLinks; */
+NS_IMETHODIMP
+nsLocalFile::GetFollowLinks(PRBool *aFollowLinks)
+{
+ *aFollowLinks = mFollowSymlinks;
+ return NS_OK;
+}
+NS_IMETHODIMP
+nsLocalFile::SetFollowLinks(PRBool aFollowLinks)
+{
+ MakeDirty();
+ mFollowSymlinks = aFollowLinks;
+ return NS_OK;
+}
+
+
+NS_IMETHODIMP
+nsLocalFile::GetDirectoryEntries(nsISimpleEnumerator * *entries)
+{
+ nsresult rv;
+
+ *entries = nsnull;
+ if (mWorkingPath.EqualsLiteral("\\\\.")) {
+ nsDriveEnumerator *drives = new nsDriveEnumerator;
+ if (!drives)
+ return NS_ERROR_OUT_OF_MEMORY;
+ NS_ADDREF(drives);
+ rv = drives->Init();
+ if (NS_FAILED(rv)) {
+ NS_RELEASE(drives);
+ return rv;
+ }
+ *entries = drives;
+ return NS_OK;
+ }
+
+ PRBool isDir;
+ rv = IsDirectory(&isDir);
+ if (NS_FAILED(rv))
+ return rv;
+ if (!isDir)
+ return NS_ERROR_FILE_NOT_DIRECTORY;
+
+ nsDirEnumerator* dirEnum = new nsDirEnumerator();
+ if (dirEnum == nsnull)
+ return NS_ERROR_OUT_OF_MEMORY;
+ NS_ADDREF(dirEnum);
+ rv = dirEnum->Init(this);
+ if (NS_FAILED(rv))
+ {
+ NS_RELEASE(dirEnum);
+ return rv;
+ }
+
+ *entries = dirEnum;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::GetPersistentDescriptor(nsACString &aPersistentDescriptor)
+{
+ return GetNativePath(aPersistentDescriptor);
+}
+
+NS_IMETHODIMP
+nsLocalFile::SetPersistentDescriptor(const nsACString &aPersistentDescriptor)
+{
+ return InitWithNativePath(aPersistentDescriptor);
+}
+
+NS_IMETHODIMP
+nsLocalFile::Reveal()
+{
+ // make sure mResolvedPath is set
+ nsresult rv = ResolveAndStat();
+ if (NS_FAILED(rv) && rv != NS_ERROR_FILE_NOT_FOUND)
+ return rv;
+
+ // use the full path to explorer for security
+ nsCOMPtr<nsILocalFile> winDir;
+ rv = GetSpecialSystemDirectory(Win_WindowsDirectory, getter_AddRefs(winDir));
+ NS_ENSURE_SUCCESS(rv, rv);
+ nsCAutoString explorerPath;
+ rv = winDir->GetNativePath(explorerPath);
+ NS_ENSURE_SUCCESS(rv, rv);
+ explorerPath.Append("\\explorer.exe");
+
+ // Always open a new window for files because Win2K doesn't appear to select
+ // the file if a window showing that folder was already open. If the resolved
+ // path is a directory then instead of opening the parent and selecting it,
+ // we open the directory itself.
+ nsCAutoString explorerParams;
+ if (mFileInfo64.type != PR_FILE_DIRECTORY) // valid because we ResolveAndStat above
+ explorerParams.Append("/n,/select,");
+ explorerParams.Append('\"');
+ explorerParams.Append(mResolvedPath);
+ explorerParams.Append('\"');
+
+ if (::ShellExecute(NULL, "open", explorerPath.get(), explorerParams.get(),
+ NULL, SW_SHOWNORMAL) <= (HINSTANCE) 32)
+ return NS_ERROR_FAILURE;
+
+ return NS_OK;
+}
+
+
+NS_IMETHODIMP
+nsLocalFile::Launch()
+{
+ const nsCString &path = mWorkingPath;
+
+ // use the app registry name to launch a shell execute....
+ LONG r = (LONG) ::ShellExecute( NULL, NULL, path.get(), NULL, NULL, SW_SHOWNORMAL);
+
+ // if the file has no association, we launch windows' "what do you want to do" dialog
+ if (r == SE_ERR_NOASSOC) {
+ nsCAutoString shellArg;
+ shellArg.Assign(NS_LITERAL_CSTRING("shell32.dll,OpenAs_RunDLL ") + path);
+ r = (LONG) ::ShellExecute(NULL, NULL, "RUNDLL32.EXE", shellArg.get(),
+ NULL, SW_SHOWNORMAL);
+ }
+ if (r < 32) {
+ switch (r) {
+ case 0:
+ case SE_ERR_OOM:
+ return NS_ERROR_OUT_OF_MEMORY;
+ case ERROR_FILE_NOT_FOUND:
+ return NS_ERROR_FILE_NOT_FOUND;
+ case ERROR_PATH_NOT_FOUND:
+ return NS_ERROR_FILE_UNRECOGNIZED_PATH;
+ case ERROR_BAD_FORMAT:
+ return NS_ERROR_FILE_CORRUPTED;
+ case SE_ERR_ACCESSDENIED:
+ return NS_ERROR_FILE_ACCESS_DENIED;
+ case SE_ERR_ASSOCINCOMPLETE:
+ case SE_ERR_NOASSOC:
+ return NS_ERROR_UNEXPECTED;
+ case SE_ERR_DDEBUSY:
+ case SE_ERR_DDEFAIL:
+ case SE_ERR_DDETIMEOUT:
+ return NS_ERROR_NOT_AVAILABLE;
+ case SE_ERR_DLLNOTFOUND:
+ return NS_ERROR_FAILURE;
+ case SE_ERR_SHARE:
+ return NS_ERROR_FILE_IS_LOCKED;
+ default:
+ return NS_ERROR_FILE_EXECUTION_FAILED;
+ }
+ }
+ return NS_OK;
+}
+
+nsresult
+NS_NewNativeLocalFile(const nsACString &path, PRBool followLinks, nsILocalFile* *result)
+{
+ nsLocalFile* file = new nsLocalFile();
+ if (file == nsnull)
+ return NS_ERROR_OUT_OF_MEMORY;
+ NS_ADDREF(file);
+
+ file->SetFollowLinks(followLinks);
+
+ if (!path.IsEmpty()) {
+ nsresult rv = file->InitWithNativePath(path);
+ if (NS_FAILED(rv)) {
+ NS_RELEASE(file);
+ return rv;
+ }
+ }
+
+ *result = file;
+ return NS_OK;
+}
+
+//-----------------------------------------------------------------------------
+// UCS2 interface
+//-----------------------------------------------------------------------------
+
+nsresult
+nsLocalFile::InitWithPath(const nsAString &filePath)
+{
+ nsCAutoString tmp;
+ nsresult rv = NS_CopyUnicodeToNative(filePath, tmp);
+ if (NS_SUCCEEDED(rv))
+ return InitWithNativePath(tmp);
+
+ return rv;
+}
+
+nsresult
+nsLocalFile::Append(const nsAString &node)
+{
+ nsCAutoString tmp;
+ nsresult rv = NS_CopyUnicodeToNative(node, tmp);
+ if (NS_SUCCEEDED(rv))
+ return AppendNative(tmp);
+
+ return rv;
+}
+
+nsresult
+nsLocalFile::AppendRelativePath(const nsAString &node)
+{
+ nsCAutoString tmp;
+ nsresult rv = NS_CopyUnicodeToNative(node, tmp);
+ if (NS_SUCCEEDED(rv))
+ return AppendRelativeNativePath(tmp);
+ return rv;
+}
+
+nsresult
+nsLocalFile::GetLeafName(nsAString &aLeafName)
+{
+ nsCAutoString tmp;
+ nsresult rv = GetNativeLeafName(tmp);
+ if (NS_SUCCEEDED(rv))
+ rv = NS_CopyNativeToUnicode(tmp, aLeafName);
+
+ return rv;
+}
+
+nsresult
+nsLocalFile::SetLeafName(const nsAString &aLeafName)
+{
+ nsCAutoString tmp;
+ nsresult rv = NS_CopyUnicodeToNative(aLeafName, tmp);
+ if (NS_SUCCEEDED(rv))
+ return SetNativeLeafName(tmp);
+
+ return rv;
+}
+
+nsresult
+nsLocalFile::GetPath(nsAString &_retval)
+{
+ return NS_CopyNativeToUnicode(mWorkingPath, _retval);
+}
+
+nsresult
+nsLocalFile::CopyTo(nsIFile *newParentDir, const nsAString &newName)
+{
+ if (newName.IsEmpty())
+ return CopyToNative(newParentDir, nsCString());
+
+ nsCAutoString tmp;
+ nsresult rv = NS_CopyUnicodeToNative(newName, tmp);
+ if (NS_SUCCEEDED(rv))
+ return CopyToNative(newParentDir, tmp);
+
+ return rv;
+}
+
+nsresult
+nsLocalFile::CopyToFollowingLinks(nsIFile *newParentDir, const nsAString &newName)
+{
+ if (newName.IsEmpty())
+ return CopyToFollowingLinksNative(newParentDir, nsCString());
+
+ nsCAutoString tmp;
+ nsresult rv = NS_CopyUnicodeToNative(newName, tmp);
+ if (NS_SUCCEEDED(rv))
+ return CopyToFollowingLinksNative(newParentDir, tmp);
+
+ return rv;
+}
+
+nsresult
+nsLocalFile::MoveTo(nsIFile *newParentDir, const nsAString &newName)
+{
+ if (newName.IsEmpty())
+ return MoveToNative(newParentDir, nsCString());
+
+ nsCAutoString tmp;
+ nsresult rv = NS_CopyUnicodeToNative(newName, tmp);
+ if (NS_SUCCEEDED(rv))
+ return MoveToNative(newParentDir, tmp);
+
+ return rv;
+}
+
+nsresult
+nsLocalFile::GetTarget(nsAString &_retval)
+{
+ nsCAutoString tmp;
+ nsresult rv = GetNativeTarget(tmp);
+ if (NS_SUCCEEDED(rv))
+ rv = NS_CopyNativeToUnicode(tmp, _retval);
+
+ return rv;
+}
+
+nsresult
+NS_NewLocalFile(const nsAString &path, PRBool followLinks, nsILocalFile* *result)
+{
+ nsCAutoString buf;
+ nsresult rv = NS_CopyUnicodeToNative(path, buf);
+ if (NS_FAILED(rv)) {
+ *result = nsnull;
+ return rv;
+ }
+ return NS_NewNativeLocalFile(buf, followLinks, result);
+}
+
+//-----------------------------------------------------------------------------
+// nsLocalFile <static members>
+//-----------------------------------------------------------------------------
+
+void
+nsLocalFile::GlobalInit()
+{
+ nsresult rv = NS_CreateShortcutResolver();
+ NS_ASSERTION(NS_SUCCEEDED(rv), "Shortcut resolver could not be created");
+}
+
+void
+nsLocalFile::GlobalShutdown()
+{
+ NS_DestroyShortcutResolver();
+}
+
+NS_IMPL_ISUPPORTS1(nsDriveEnumerator, nsISimpleEnumerator)
+
+nsDriveEnumerator::nsDriveEnumerator()
+ : mLetter(0)
+{
+}
+
+nsDriveEnumerator::~nsDriveEnumerator()
+{
+}
+
+nsresult nsDriveEnumerator::Init()
+{
+ /* If the length passed to GetLogicalDriveStrings is smaller
+ * than the length of the string it would return, it returns
+ * the length required for the string. */
+ DWORD length = GetLogicalDriveStrings(0, 0);
+ /* The string is null terminated */
+ mDrives.SetLength(length+1);
+ if (!GetLogicalDriveStrings(length, mDrives.BeginWriting()))
+ return NS_ERROR_FAILURE;
+ mLetter = mDrives.get();
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsDriveEnumerator::HasMoreElements(PRBool *aHasMore)
+{
+ *aHasMore = *mLetter != '\0';
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsDriveEnumerator::GetNext(nsISupports **aNext)
+{
+ /* GetLogicalDrives stored in mLetter is a concatenation
+ * of null terminated strings, followed by a null terminator. */
+ if (!*mLetter) {
+ *aNext = nsnull;
+ return NS_OK;
+ }
+ const char *drive = mLetter;
+ mLetter += strlen(drive) + 1;
+ nsILocalFile *file;
+ nsresult rv =
+ NS_NewNativeLocalFile(nsDependentCString(drive), PR_FALSE, &file);
+
+ *aNext = file;
+ return rv;
+}
diff --git a/src/libs/xpcom18a4/xpcom/io/nsLocalFileWin.h b/src/libs/xpcom18a4/xpcom/io/nsLocalFileWin.h
new file mode 100644
index 00000000..0ef1d9f0
--- /dev/null
+++ b/src/libs/xpcom18a4/xpcom/io/nsLocalFileWin.h
@@ -0,0 +1,113 @@
+/* -*- Mode: C++; tab-width: 2; 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 client code, released
+ * March 31, 1998.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1998-1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Doug Turner <dougt@netscape.com>
+ * Brodie Thiesfield <brofield@jellycan.com>
+ *
+ * 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 ***** */
+
+#ifndef _nsLocalFileWIN_H_
+#define _nsLocalFileWIN_H_
+
+#include "nscore.h"
+#include "nsError.h"
+#include "nsString.h"
+#include "nsCRT.h"
+#include "nsIFile.h"
+#include "nsIFactory.h"
+
+#include "windows.h"
+
+// For older version (<6.0) of the VC Compiler
+#if (_MSC_VER == 1100)
+#include <objbase.h>
+DEFINE_OLEGUID(IID_IPersistFile, 0x0000010BL, 0, 0);
+#endif
+
+#include "shlobj.h"
+
+#include <sys/stat.h>
+
+class nsLocalFile : public nsILocalFile
+{
+public:
+ NS_DEFINE_STATIC_CID_ACCESSOR(NS_LOCAL_FILE_CID)
+
+ nsLocalFile();
+
+ static NS_METHOD nsLocalFileConstructor(nsISupports* outer, const nsIID& aIID, void* *aInstancePtr);
+
+ // nsISupports interface
+ NS_DECL_ISUPPORTS
+
+ // nsIFile interface
+ NS_DECL_NSIFILE
+
+ // nsILocalFile interface
+ NS_DECL_NSILOCALFILE
+
+public:
+ static void GlobalInit();
+ static void GlobalShutdown();
+
+private:
+ nsLocalFile(const nsLocalFile& other);
+ ~nsLocalFile() {}
+
+ PRPackedBool mDirty; // cached information can only be used when this is PR_FALSE
+ PRPackedBool mFollowSymlinks; // should we follow symlinks when working on this file
+
+ // this string will always be in native format!
+ nsCString mWorkingPath;
+
+ // this will be the resolved path of shortcuts, it will *NEVER* be returned to the user
+ nsCString mResolvedPath;
+
+ PRFileInfo64 mFileInfo64;
+
+ void MakeDirty() { mDirty = PR_TRUE; }
+
+ nsresult ResolveAndStat();
+ nsresult ResolveShortcut();
+
+ nsresult CopyMove(nsIFile *newParentDir, const nsACString &newName, PRBool followSymlinks, PRBool move);
+ nsresult CopySingleFile(nsIFile *source, nsIFile* dest, const nsACString &newName, PRBool followSymlinks, PRBool move);
+
+ nsresult SetModDate(PRInt64 aLastModifiedTime, const char *filePath);
+ nsresult HasFileAttribute(DWORD fileAttrib, PRBool *_retval);
+ nsresult AppendNativeInternal(const nsAFlatCString &node, PRBool multipleComponents);
+};
+
+#endif
diff --git a/src/libs/xpcom18a4/xpcom/io/nsMultiplexInputStream.cpp b/src/libs/xpcom18a4/xpcom/io/nsMultiplexInputStream.cpp
new file mode 100644
index 00000000..ba04a842
--- /dev/null
+++ b/src/libs/xpcom18a4/xpcom/io/nsMultiplexInputStream.cpp
@@ -0,0 +1,389 @@
+/* -*- 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 frightening to behold.
+ *
+ * The Initial Developer of the Original Code is
+ * Jonas Sicking.
+ * Portions created by the Initial Developer are Copyright (C) 2001
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Jonas Sicking <sicking@bigfoot.com>
+ *
+ * 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 ***** */
+
+/**
+ * The multiplex stream concatenates a list of input streams into a single
+ * stream.
+ */
+
+#include "nsMultiplexInputStream.h"
+#include "nsIMultiplexInputStream.h"
+#include "nsISeekableStream.h"
+#include "nsSupportsArray.h"
+#include "nsInt64.h"
+
+class nsMultiplexInputStream : public nsIMultiplexInputStream,
+ public nsISeekableStream
+{
+public:
+ nsMultiplexInputStream();
+
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIINPUTSTREAM
+ NS_DECL_NSIMULTIPLEXINPUTSTREAM
+ NS_DECL_NSISEEKABLESTREAM
+
+ static NS_METHOD Create(nsISupports *outer, REFNSIID iid, void **result);
+
+private:
+ ~nsMultiplexInputStream() {}
+
+
+ struct ReadSegmentsState {
+ nsIInputStream* mThisStream;
+ PRUint32 mOffset;
+ nsWriteSegmentFun mWriter;
+ void* mClosure;
+ PRBool mDone;
+ };
+
+ static NS_METHOD ReadSegCb(nsIInputStream* aIn, void* aClosure,
+ const char* aFromRawSegment, PRUint32 aToOffset,
+ PRUint32 aCount, PRUint32 *aWriteCount);
+
+ nsSupportsArray mStreams;
+ PRUint32 mCurrentStream;
+ PRBool mStartedReadingCurrent;
+};
+
+
+NS_IMPL_THREADSAFE_ISUPPORTS3(nsMultiplexInputStream,
+ nsIMultiplexInputStream,
+ nsIInputStream,
+ nsISeekableStream)
+
+nsMultiplexInputStream::nsMultiplexInputStream()
+ : mCurrentStream(0),
+ mStartedReadingCurrent(PR_FALSE)
+{
+}
+
+/* readonly attribute unsigned long count; */
+NS_IMETHODIMP
+nsMultiplexInputStream::GetCount(PRUint32 *aCount)
+{
+ mStreams.Count(aCount);
+ return NS_OK;
+}
+
+/* void appendStream (in nsIInputStream stream); */
+NS_IMETHODIMP
+nsMultiplexInputStream::AppendStream(nsIInputStream *aStream)
+{
+ return mStreams.AppendElement(aStream);
+}
+
+/* void insertStream (in nsIInputStream stream, in unsigned long index); */
+NS_IMETHODIMP
+nsMultiplexInputStream::InsertStream(nsIInputStream *aStream, PRUint32 aIndex)
+{
+ nsresult rv = mStreams.InsertElementAt(aStream, aIndex);
+ NS_ENSURE_SUCCESS(rv, rv);
+ if (mCurrentStream > aIndex ||
+ (mCurrentStream == aIndex && mStartedReadingCurrent))
+ ++mCurrentStream;
+ return rv;
+}
+
+/* void removeStream (in unsigned long index); */
+NS_IMETHODIMP
+nsMultiplexInputStream::RemoveStream(PRUint32 aIndex)
+{
+ nsresult rv = mStreams.RemoveElementAt(aIndex);
+ NS_ENSURE_SUCCESS(rv, rv);
+ if (mCurrentStream > aIndex)
+ --mCurrentStream;
+ else if (mCurrentStream == aIndex)
+ mStartedReadingCurrent = PR_FALSE;
+
+ return rv;
+}
+
+/* nsIInputStream getStream (in unsigned long index); */
+NS_IMETHODIMP
+nsMultiplexInputStream::GetStream(PRUint32 aIndex, nsIInputStream **_retval)
+{
+ return mStreams.QueryElementAt(aIndex,
+ NS_GET_IID(nsIInputStream),
+ (void**)_retval);
+}
+
+/* void close (); */
+NS_IMETHODIMP
+nsMultiplexInputStream::Close()
+{
+ PRUint32 len, i;
+ nsresult rv = NS_OK;
+
+ mStreams.Count(&len);
+ for (i = 0; i < len; ++i) {
+ nsCOMPtr<nsIInputStream> stream(do_QueryElementAt(&mStreams, i));
+ nsresult rv2 = stream->Close();
+ // We still want to close all streams, but we should return an error
+ if (NS_FAILED(rv2))
+ rv = rv2;
+ }
+ return rv;
+}
+
+/* unsigned long available (); */
+NS_IMETHODIMP
+nsMultiplexInputStream::Available(PRUint32 *_retval)
+{
+ nsresult rv;
+ PRUint32 i, len, avail = 0;
+
+ mStreams.Count(&len);
+ for (i = mCurrentStream; i < len; i++) {
+ nsCOMPtr<nsIInputStream> stream(do_QueryElementAt(&mStreams, i));
+
+ PRUint32 streamAvail;
+ rv = stream->Available(&streamAvail);
+ NS_ENSURE_SUCCESS(rv, rv);
+ avail += streamAvail;
+ }
+ *_retval = avail;
+ return NS_OK;
+}
+
+/* [noscript] unsigned long read (in charPtr buf, in unsigned long count); */
+NS_IMETHODIMP
+nsMultiplexInputStream::Read(char * aBuf, PRUint32 aCount, PRUint32 *_retval)
+{
+ nsresult rv = NS_OK;
+ PRUint32 len, read;
+
+ *_retval = 0;
+
+ mStreams.Count(&len);
+ while (mCurrentStream < len && aCount) {
+ nsCOMPtr<nsIInputStream> stream(do_QueryElementAt(&mStreams,
+ mCurrentStream));
+ rv = stream->Read(aBuf, aCount, &read);
+
+ // XXX some streams return NS_BASE_STREAM_CLOSED to indicate EOF.
+ if (rv == NS_BASE_STREAM_CLOSED) {
+ rv = NS_OK;
+ read = 0;
+ }
+ else if (NS_FAILED(rv))
+ break;
+
+ if (read == 0) {
+ ++mCurrentStream;
+ mStartedReadingCurrent = PR_FALSE;
+ }
+ else {
+ NS_ASSERTION(aCount >= read, "Read more than requested");
+ *_retval += read;
+ aCount -= read;
+ aBuf += read;
+ mStartedReadingCurrent = PR_TRUE;
+ }
+ }
+ return *_retval ? NS_OK : rv;
+}
+
+/* [noscript] unsigned long readSegments (in nsWriteSegmentFun writer,
+ * in voidPtr closure,
+ * in unsigned long count); */
+NS_IMETHODIMP
+nsMultiplexInputStream::ReadSegments(nsWriteSegmentFun aWriter, void *aClosure,
+ PRUint32 aCount, PRUint32 *_retval)
+{
+ NS_ASSERTION(aWriter, "missing aWriter");
+
+ nsresult rv = NS_OK;
+ ReadSegmentsState state;
+ state.mThisStream = this;
+ state.mOffset = 0;
+ state.mWriter = aWriter;
+ state.mClosure = aClosure;
+ state.mDone = PR_FALSE;
+
+ PRUint32 len;
+ mStreams.Count(&len);
+ while (mCurrentStream < len && aCount) {
+ nsCOMPtr<nsIInputStream> stream(do_QueryElementAt(&mStreams,
+ mCurrentStream));
+ PRUint32 read;
+ rv = stream->ReadSegments(ReadSegCb, &state, aCount, &read);
+
+ // XXX some streams return NS_BASE_STREAM_CLOSED to indicate EOF.
+ if (rv == NS_BASE_STREAM_CLOSED) {
+ rv = NS_OK;
+ read = 0;
+ }
+
+ // if |aWriter| decided to stop reading segments...
+ if (state.mDone || NS_FAILED(rv))
+ break;
+
+ // if stream is empty, then advance to the next stream.
+ if (read == 0) {
+ ++mCurrentStream;
+ mStartedReadingCurrent = PR_FALSE;
+ }
+ else {
+ NS_ASSERTION(aCount >= read, "Read more than requested");
+ state.mOffset += read;
+ aCount -= read;
+ mStartedReadingCurrent = PR_TRUE;
+ }
+ }
+
+ // if we successfully read some data, then this call succeeded.
+ *_retval = state.mOffset;
+ return state.mOffset ? NS_OK : rv;
+}
+
+NS_METHOD
+nsMultiplexInputStream::ReadSegCb(nsIInputStream* aIn, void* aClosure,
+ const char* aFromRawSegment,
+ PRUint32 aToOffset, PRUint32 aCount,
+ PRUint32 *aWriteCount)
+{
+ nsresult rv;
+ ReadSegmentsState* state = (ReadSegmentsState*)aClosure;
+ rv = (state->mWriter)(state->mThisStream,
+ state->mClosure,
+ aFromRawSegment,
+ aToOffset + state->mOffset,
+ aCount,
+ aWriteCount);
+ if (NS_FAILED(rv))
+ state->mDone = PR_TRUE;
+ return rv;
+}
+
+/* readonly attribute boolean nonBlocking; */
+NS_IMETHODIMP
+nsMultiplexInputStream::IsNonBlocking(PRBool *aNonBlocking)
+{
+ nsresult rv;
+ PRUint32 i, len;
+
+ mStreams.Count(&len);
+ for (i = 0; i < len; ++i) {
+ nsCOMPtr<nsIInputStream> stream(do_QueryElementAt(&mStreams, i));
+ rv = stream->IsNonBlocking(aNonBlocking);
+ NS_ENSURE_SUCCESS(rv, rv);
+ // If one is non-blocking the entire stream becomes non-blocking
+ if (*aNonBlocking)
+ return NS_OK;
+ }
+ return NS_OK;
+}
+
+/* void seek (in PRInt32 whence, in PRInt32 offset); */
+NS_IMETHODIMP
+nsMultiplexInputStream::Seek(PRInt32 aWhence, PRInt64 aOffset)
+{
+ nsresult rv;
+
+ // rewinding to start is easy, and should be the most common case
+ if (aWhence == NS_SEEK_SET && aOffset == 0)
+ {
+ PRUint32 i, last;
+ last = mStartedReadingCurrent ? mCurrentStream+1 : mCurrentStream;
+ for (i = 0; i < last; ++i) {
+ nsCOMPtr<nsISeekableStream> stream(do_QueryElementAt(&mStreams, i));
+ NS_ENSURE_TRUE(stream, NS_ERROR_NO_INTERFACE);
+
+ rv = stream->Seek(NS_SEEK_SET, 0);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+ mCurrentStream = 0;
+ mStartedReadingCurrent = PR_FALSE;
+ return NS_OK;
+ }
+
+ // other Seeks not implemented yet
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+/* PRUint32 tell (); */
+NS_IMETHODIMP
+nsMultiplexInputStream::Tell(PRInt64 *_retval)
+{
+ nsresult rv;
+ nsInt64 ret64 = 0;
+ PRUint32 i, last;
+ last = mStartedReadingCurrent ? mCurrentStream+1 : mCurrentStream;
+ for (i = 0; i < last; ++i) {
+ nsCOMPtr<nsISeekableStream> stream(do_QueryElementAt(&mStreams, i));
+ NS_ENSURE_TRUE(stream, NS_ERROR_NO_INTERFACE);
+
+ PRInt64 pos;
+ rv = stream->Tell(&pos);
+ NS_ENSURE_SUCCESS(rv, rv);
+ ret64 += pos;
+ }
+ *_retval = ret64;
+
+ return NS_OK;
+}
+
+/* void setEOF (); */
+NS_IMETHODIMP
+nsMultiplexInputStream::SetEOF()
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_METHOD
+nsMultiplexInputStreamConstructor(nsISupports *outer,
+ REFNSIID iid,
+ void **result)
+{
+ *result = nsnull;
+
+ if (outer)
+ return NS_ERROR_NO_AGGREGATION;
+
+ nsMultiplexInputStream *inst;
+ NS_NEWXPCOM(inst, nsMultiplexInputStream);
+ if (!inst)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ NS_ADDREF(inst);
+ nsresult rv = inst->QueryInterface(iid, result);
+ NS_RELEASE(inst);
+
+ return rv;
+}
diff --git a/src/libs/xpcom18a4/xpcom/io/nsMultiplexInputStream.h b/src/libs/xpcom18a4/xpcom/io/nsMultiplexInputStream.h
new file mode 100644
index 00000000..32d3026d
--- /dev/null
+++ b/src/libs/xpcom18a4/xpcom/io/nsMultiplexInputStream.h
@@ -0,0 +1,63 @@
+/* -*- 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 frightening to behold.
+ *
+ * The Initial Developer of the Original Code is
+ * Jonas Sicking.
+ * Portions created by the Initial Developer are Copyright (C) 2001
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Jonas Sicking <sicking@bigfoot.com>
+ *
+ * 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 ***** */
+
+/**
+ * The multiplex stream concatinates a list of input streams into a single
+ * stream.
+ */
+
+#ifndef _nsMultiplexInputStream_h_
+#define _nsMultiplexInputStream_h_
+
+#include "nsIMultiplexInputStream.h"
+
+#define NS_MULTIPLEXINPUTSTREAM_CLASSNAME "nsMultiplexInputStream"
+#define NS_MULTIPLEXINPUTSTREAM_CONTRACTID "@mozilla.org/io/multiplex-input-stream;1"
+#define NS_MULTIPLEXINPUTSTREAM_CID \
+{ /* 565e3a2c-1dd2-11b2-8da1-b4cef17e568d */ \
+ 0x565e3a2c, \
+ 0x1dd2, \
+ 0x11b2, \
+ {0x8d, 0xa1, 0xb4, 0xce, 0xf1, 0x7e, 0x56, 0x8d} \
+}
+
+extern NS_METHOD nsMultiplexInputStreamConstructor(nsISupports *outer,
+ REFNSIID iid,
+ void **result);
+
+#endif // _nsMultiplexInputStream_h_
diff --git a/src/libs/xpcom18a4/xpcom/io/nsNativeCharsetUtils.cpp b/src/libs/xpcom18a4/xpcom/io/nsNativeCharsetUtils.cpp
new file mode 100644
index 00000000..55b5c63c
--- /dev/null
+++ b/src/libs/xpcom18a4/xpcom/io/nsNativeCharsetUtils.cpp
@@ -0,0 +1,1280 @@
+/* ***** 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.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2002
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Darin Fisher <darin@netscape.com>
+ * Brian Stell <bstell@ix.netcom.com>
+ * Frank Tang <ftang@netscape.com>
+ * Brendan Eich <brendan@mozilla.org>
+ * Sergei Dolgov <sergei_d@fi.fi.tartu.ee>
+ *
+ * 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 "xpcom-private.h"
+
+//-----------------------------------------------------------------------------
+// XP_UNIX
+//-----------------------------------------------------------------------------
+#if defined(XP_UNIX)
+
+#include <stdlib.h> // mbtowc, wctomb
+#include <locale.h> // setlocale
+#include "nscore.h"
+#include "prlock.h"
+#include "nsAString.h"
+#include "nsReadableUtils.h"
+
+//
+// choose a conversion library. we used to use mbrtowc/wcrtomb under Linux,
+// but that doesn't work for non-BMP characters whether we use '-fshort-wchar'
+// or not (see bug 206811 and
+// news://news.mozilla.org:119/bajml3$fvr1@ripley.netscape.com). we now use
+// iconv for all platforms where nltypes.h and nllanginfo.h are present
+// along with iconv.
+//
+#if defined(HAVE_ICONV) && defined(HAVE_NL_TYPES_H) && defined(HAVE_LANGINFO_CODESET)
+#define USE_ICONV 1
+#else
+#define USE_STDCONV 1
+#endif
+
+static void
+isolatin1_to_utf16(const char **input, PRUint32 *inputLeft, PRUnichar **output, PRUint32 *outputLeft)
+{
+ while (*inputLeft && *outputLeft) {
+ **output = (unsigned char) **input;
+ (*input)++;
+ (*inputLeft)--;
+ (*output)++;
+ (*outputLeft)--;
+ }
+}
+
+static void
+utf16_to_isolatin1(const PRUnichar **input, PRUint32 *inputLeft, char **output, PRUint32 *outputLeft)
+{
+ while (*inputLeft && *outputLeft) {
+ **output = (unsigned char) **input;
+ (*input)++;
+ (*inputLeft)--;
+ (*output)++;
+ (*outputLeft)--;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// conversion using iconv
+//-----------------------------------------------------------------------------
+#if defined(USE_ICONV)
+#include <nl_types.h> // CODESET
+#include <langinfo.h> // nl_langinfo
+#include <iconv.h> // iconv_open, iconv, iconv_close
+#include <errno.h>
+
+#if defined(HAVE_ICONV_WITH_CONST_INPUT)
+#define ICONV_INPUT(x) (x)
+#else
+#define ICONV_INPUT(x) ((char **)x)
+#endif
+
+// solaris definitely needs this, but we'll enable it by default
+// just in case... but we know for sure that iconv(3) in glibc
+// doesn't need this.
+#if !defined(__GLIBC__)
+#define ENABLE_UTF8_FALLBACK_SUPPORT
+#endif
+
+#define INVALID_ICONV_T ((iconv_t) -1)
+
+static inline size_t
+xp_iconv(iconv_t converter,
+ const char **input,
+ size_t *inputLeft,
+ char **output,
+ size_t *outputLeft)
+{
+ size_t res, outputAvail = outputLeft ? *outputLeft : 0;
+ res = iconv(converter, ICONV_INPUT(input), inputLeft, output, outputLeft);
+ if (res == (size_t) -1) {
+ // on some platforms (e.g., linux) iconv will fail with
+ // E2BIG if it cannot convert _all_ of its input. it'll
+ // still adjust all of the in/out params correctly, so we
+ // can ignore this error. the assumption is that we will
+ // be called again to complete the conversion.
+ if ((errno == E2BIG) && (*outputLeft < outputAvail))
+ res = 0;
+ }
+ return res;
+}
+
+static inline void
+xp_iconv_reset(iconv_t converter)
+{
+ // NOTE: the man pages on Solaris claim that you can pass NULL
+ // for all parameter to reset the converter, but beware the
+ // evil Solaris crash if you go down this route >:-)
+
+ const char *zero_char_in_ptr = NULL;
+ char *zero_char_out_ptr = NULL;
+ size_t zero_size_in = 0,
+ zero_size_out = 0;
+
+ xp_iconv(converter, &zero_char_in_ptr,
+ &zero_size_in,
+ &zero_char_out_ptr,
+ &zero_size_out);
+}
+
+static inline iconv_t
+xp_iconv_open(const char **to_list, const char **from_list)
+{
+ iconv_t res;
+ const char **from_name;
+ const char **to_name;
+
+ // try all possible combinations to locate a converter.
+ to_name = to_list;
+ while (*to_name) {
+ if (**to_name) {
+ from_name = from_list;
+ while (*from_name) {
+ if (**from_name) {
+ res = iconv_open(*to_name, *from_name);
+ if (res != INVALID_ICONV_T)
+ return res;
+ }
+ from_name++;
+ }
+ }
+ to_name++;
+ }
+
+ return INVALID_ICONV_T;
+}
+
+/*
+ * PRUnichar[] is NOT a UCS-2 array BUT a UTF-16 string. Therefore, we
+ * have to use UTF-16 with iconv(3) on platforms where it's supported.
+ * However, the way UTF-16 and UCS-2 are interpreted varies across platforms
+ * and implementations of iconv(3). On Tru64, it also depends on the environment
+ * variable. To avoid the trouble arising from byte-swapping
+ * (bug 208809), we have to try UTF-16LE/BE and UCS-2LE/BE before falling
+ * back to UTF-16 and UCS-2 and variants. We assume that UTF-16 and UCS-2
+ * on systems without UTF-16LE/BE and UCS-2LE/BE have the native endianness,
+ * which isn't the case of glibc 2.1.x, for which we use 'UNICODELITTLE'
+ * and 'UNICODEBIG'. It's also not true of Tru64 V4 when the environment
+ * variable ICONV_BYTEORDER is set to 'big-endian', about which not much
+ * can be done other than adding a note in the release notes. (bug 206811)
+ */
+static const char *UTF_16_NAMES[] = {
+#if defined(IS_LITTLE_ENDIAN)
+ "UTF-16LE",
+#if defined(__GLIBC__)
+ "UNICODELITTLE",
+#endif
+ "UCS-2LE",
+#else
+ "UTF-16BE",
+#if defined(__GLIBC__)
+ "UNICODEBIG",
+#endif
+ "UCS-2BE",
+#endif
+ "UTF-16",
+ "UCS-2",
+ "UCS2",
+ "UCS_2",
+ "ucs-2",
+ "ucs2",
+ "ucs_2",
+ NULL
+};
+
+#if defined(ENABLE_UTF8_FALLBACK_SUPPORT)
+static const char *UTF_8_NAMES[] = {
+ "UTF-8",
+ "UTF8",
+ "UTF_8",
+ "utf-8",
+ "utf8",
+ "utf_8",
+ NULL
+};
+#endif
+
+static const char *ISO_8859_1_NAMES[] = {
+ "ISO-8859-1",
+#if !defined(__GLIBC__)
+ "ISO8859-1",
+ "ISO88591",
+ "ISO_8859_1",
+ "ISO8859_1",
+ "iso-8859-1",
+ "iso8859-1",
+ "iso88591",
+ "iso_8859_1",
+ "iso8859_1",
+#endif
+ NULL
+};
+
+class nsNativeCharsetConverter
+{
+public:
+ nsNativeCharsetConverter();
+ ~nsNativeCharsetConverter();
+
+ nsresult NativeToUnicode(const char **input , PRUint32 *inputLeft,
+ PRUnichar **output, PRUint32 *outputLeft);
+ nsresult UnicodeToNative(const PRUnichar **input , PRUint32 *inputLeft,
+ char **output, PRUint32 *outputLeft);
+
+ static void GlobalInit();
+ static void GlobalShutdown();
+
+private:
+ static iconv_t gNativeToUnicode;
+ static iconv_t gUnicodeToNative;
+#if defined(ENABLE_UTF8_FALLBACK_SUPPORT)
+ static iconv_t gNativeToUTF8;
+ static iconv_t gUTF8ToNative;
+ static iconv_t gUnicodeToUTF8;
+ static iconv_t gUTF8ToUnicode;
+#endif
+ static PRLock *gLock;
+ static PRBool gInitialized;
+
+ static void LazyInit();
+
+ static void Lock() { if (gLock) PR_Lock(gLock); }
+ static void Unlock() { if (gLock) PR_Unlock(gLock); }
+};
+
+iconv_t nsNativeCharsetConverter::gNativeToUnicode = INVALID_ICONV_T;
+iconv_t nsNativeCharsetConverter::gUnicodeToNative = INVALID_ICONV_T;
+#if defined(ENABLE_UTF8_FALLBACK_SUPPORT)
+iconv_t nsNativeCharsetConverter::gNativeToUTF8 = INVALID_ICONV_T;
+iconv_t nsNativeCharsetConverter::gUTF8ToNative = INVALID_ICONV_T;
+iconv_t nsNativeCharsetConverter::gUnicodeToUTF8 = INVALID_ICONV_T;
+iconv_t nsNativeCharsetConverter::gUTF8ToUnicode = INVALID_ICONV_T;
+#endif
+PRLock *nsNativeCharsetConverter::gLock = nsnull;
+PRBool nsNativeCharsetConverter::gInitialized = PR_FALSE;
+
+void
+nsNativeCharsetConverter::LazyInit()
+{
+ const char *blank_list[] = { "", NULL };
+ const char **native_charset_list = blank_list;
+ const char *native_charset = nl_langinfo(CODESET);
+ if (native_charset == nsnull) {
+ NS_ERROR("native charset is unknown");
+ // fallback to ISO-8859-1
+ native_charset_list = ISO_8859_1_NAMES;
+ }
+ else
+ native_charset_list[0] = native_charset;
+
+ gNativeToUnicode = xp_iconv_open(UTF_16_NAMES, native_charset_list);
+ gUnicodeToNative = xp_iconv_open(native_charset_list, UTF_16_NAMES);
+
+#if defined(ENABLE_UTF8_FALLBACK_SUPPORT)
+ if (gNativeToUnicode == INVALID_ICONV_T) {
+ gNativeToUTF8 = xp_iconv_open(UTF_8_NAMES, native_charset_list);
+ gUTF8ToUnicode = xp_iconv_open(UTF_16_NAMES, UTF_8_NAMES);
+ NS_ASSERTION(gNativeToUTF8 != INVALID_ICONV_T, "no native to utf-8 converter");
+ NS_ASSERTION(gUTF8ToUnicode != INVALID_ICONV_T, "no utf-8 to utf-16 converter");
+ }
+ if (gUnicodeToNative == INVALID_ICONV_T) {
+ gUnicodeToUTF8 = xp_iconv_open(UTF_8_NAMES, UTF_16_NAMES);
+ gUTF8ToNative = xp_iconv_open(native_charset_list, UTF_8_NAMES);
+ NS_ASSERTION(gUnicodeToUTF8 != INVALID_ICONV_T, "no utf-16 to utf-8 converter");
+ NS_ASSERTION(gUTF8ToNative != INVALID_ICONV_T, "no utf-8 to native converter");
+ }
+#else
+ NS_ASSERTION(gNativeToUnicode != INVALID_ICONV_T, "no native to utf-16 converter");
+ NS_ASSERTION(gUnicodeToNative != INVALID_ICONV_T, "no utf-16 to native converter");
+#endif
+
+ /*
+ * On Solaris 8 (and newer?), the iconv modules converting to UCS-2
+ * prepend a byte order mark unicode character (BOM, u+FEFF) during
+ * the first use of the iconv converter. The same is the case of
+ * glibc 2.2.9x and Tru64 V5 (see bug 208809) when 'UTF-16' is used.
+ * However, we use 'UTF-16LE/BE' in both cases, instead so that we
+ * should be safe. But just in case...
+ *
+ * This dummy conversion gets rid of the BOMs and fixes bug 153562.
+ */
+ char dummy_input[1] = { ' ' };
+ char dummy_output[4];
+
+ if (gNativeToUnicode != INVALID_ICONV_T) {
+ const char *input = dummy_input;
+ size_t input_left = sizeof(dummy_input);
+ char *output = dummy_output;
+ size_t output_left = sizeof(dummy_output);
+
+ xp_iconv(gNativeToUnicode, &input, &input_left, &output, &output_left);
+ }
+#if defined(ENABLE_UTF8_FALLBACK_SUPPORT)
+ if (gUTF8ToUnicode != INVALID_ICONV_T) {
+ const char *input = dummy_input;
+ size_t input_left = sizeof(dummy_input);
+ char *output = dummy_output;
+ size_t output_left = sizeof(dummy_output);
+
+ xp_iconv(gUTF8ToUnicode, &input, &input_left, &output, &output_left);
+ }
+#endif
+
+ gInitialized = PR_TRUE;
+}
+
+void
+nsNativeCharsetConverter::GlobalInit()
+{
+ gLock = PR_NewLock();
+ NS_ASSERTION(gLock, "lock creation failed");
+}
+
+void
+nsNativeCharsetConverter::GlobalShutdown()
+{
+ if (gLock) {
+ PR_DestroyLock(gLock);
+ gLock = nsnull;
+ }
+
+ if (gNativeToUnicode != INVALID_ICONV_T) {
+ iconv_close(gNativeToUnicode);
+ gNativeToUnicode = INVALID_ICONV_T;
+ }
+
+ if (gUnicodeToNative != INVALID_ICONV_T) {
+ iconv_close(gUnicodeToNative);
+ gUnicodeToNative = INVALID_ICONV_T;
+ }
+
+#if defined(ENABLE_UTF8_FALLBACK_SUPPORT)
+ if (gNativeToUTF8 != INVALID_ICONV_T) {
+ iconv_close(gNativeToUTF8);
+ gNativeToUTF8 = INVALID_ICONV_T;
+ }
+ if (gUTF8ToNative != INVALID_ICONV_T) {
+ iconv_close(gUTF8ToNative);
+ gUTF8ToNative = INVALID_ICONV_T;
+ }
+ if (gUnicodeToUTF8 != INVALID_ICONV_T) {
+ iconv_close(gUnicodeToUTF8);
+ gUnicodeToUTF8 = INVALID_ICONV_T;
+ }
+ if (gUTF8ToUnicode != INVALID_ICONV_T) {
+ iconv_close(gUTF8ToUnicode);
+ gUTF8ToUnicode = INVALID_ICONV_T;
+ }
+#endif
+
+ gInitialized = PR_FALSE;
+}
+
+nsNativeCharsetConverter::nsNativeCharsetConverter()
+{
+ Lock();
+ if (!gInitialized)
+ LazyInit();
+}
+
+nsNativeCharsetConverter::~nsNativeCharsetConverter()
+{
+ // reset converters for next time
+ if (gNativeToUnicode != INVALID_ICONV_T)
+ xp_iconv_reset(gNativeToUnicode);
+ if (gUnicodeToNative != INVALID_ICONV_T)
+ xp_iconv_reset(gUnicodeToNative);
+#if defined(ENABLE_UTF8_FALLBACK_SUPPORT)
+ if (gNativeToUTF8 != INVALID_ICONV_T)
+ xp_iconv_reset(gNativeToUTF8);
+ if (gUTF8ToNative != INVALID_ICONV_T)
+ xp_iconv_reset(gUTF8ToNative);
+ if (gUnicodeToUTF8 != INVALID_ICONV_T)
+ xp_iconv_reset(gUnicodeToUTF8);
+ if (gUTF8ToUnicode != INVALID_ICONV_T)
+ xp_iconv_reset(gUTF8ToUnicode);
+#endif
+ Unlock();
+}
+
+nsresult
+nsNativeCharsetConverter::NativeToUnicode(const char **input,
+ PRUint32 *inputLeft,
+ PRUnichar **output,
+ PRUint32 *outputLeft)
+{
+ size_t res = 0;
+ size_t inLeft = (size_t) *inputLeft;
+ size_t outLeft = (size_t) *outputLeft * 2;
+
+ if (gNativeToUnicode != INVALID_ICONV_T) {
+
+ res = xp_iconv(gNativeToUnicode, input, &inLeft, (char **) output, &outLeft);
+
+ *inputLeft = inLeft;
+ *outputLeft = outLeft / 2;
+ if (res != (size_t) -1)
+ return NS_OK;
+
+ NS_WARNING("conversion from native to utf-16 failed");
+
+ // reset converter
+ xp_iconv_reset(gNativeToUnicode);
+ }
+#if defined(ENABLE_UTF8_FALLBACK_SUPPORT)
+ else if ((gNativeToUTF8 != INVALID_ICONV_T) &&
+ (gUTF8ToUnicode != INVALID_ICONV_T)) {
+ // convert first to UTF8, then from UTF8 to UCS2
+ const char *in = *input;
+
+ char ubuf[1024];
+
+ // we assume we're always called with enough space in |output|,
+ // so convert many chars at a time...
+ while (inLeft) {
+ char *p = ubuf;
+ size_t n = sizeof(ubuf);
+ res = xp_iconv(gNativeToUTF8, &in, &inLeft, &p, &n);
+ if (res == (size_t) -1) {
+ NS_ERROR("conversion from native to utf-8 failed");
+ break;
+ }
+ NS_ASSERTION(outLeft > 0, "bad assumption");
+ p = ubuf;
+ n = sizeof(ubuf) - n;
+ res = xp_iconv(gUTF8ToUnicode, (const char **) &p, &n, (char **) output, &outLeft);
+ if (res == (size_t) -1) {
+ NS_ERROR("conversion from utf-8 to utf-16 failed");
+ break;
+ }
+ }
+
+ (*input) += (*inputLeft - inLeft);
+ *inputLeft = inLeft;
+ *outputLeft = outLeft / 2;
+
+ if (res != (size_t) -1)
+ return NS_OK;
+
+ // reset converters
+ xp_iconv_reset(gNativeToUTF8);
+ xp_iconv_reset(gUTF8ToUnicode);
+ }
+#endif
+
+ // fallback: zero-pad and hope for the best
+ // XXX This is lame and we have to do better.
+ isolatin1_to_utf16(input, inputLeft, output, outputLeft);
+
+ return NS_OK;
+}
+
+nsresult
+nsNativeCharsetConverter::UnicodeToNative(const PRUnichar **input,
+ PRUint32 *inputLeft,
+ char **output,
+ PRUint32 *outputLeft)
+{
+ size_t res = 0;
+ size_t inLeft = (size_t) *inputLeft * 2;
+ size_t outLeft = (size_t) *outputLeft;
+
+ if (gUnicodeToNative != INVALID_ICONV_T) {
+ res = xp_iconv(gUnicodeToNative, (const char **) input, &inLeft, output, &outLeft);
+
+ if (res != (size_t) -1) {
+ *inputLeft = inLeft / 2;
+ *outputLeft = outLeft;
+ return NS_OK;
+ }
+
+ NS_ERROR("iconv failed");
+
+ // reset converter
+ xp_iconv_reset(gUnicodeToNative);
+ }
+#if defined(ENABLE_UTF8_FALLBACK_SUPPORT)
+ else if ((gUnicodeToUTF8 != INVALID_ICONV_T) &&
+ (gUTF8ToNative != INVALID_ICONV_T)) {
+ const char *in = (const char *) *input;
+
+ char ubuf[6]; // max utf-8 char length (really only needs to be 4 bytes)
+
+ // convert one uchar at a time...
+ while (inLeft && outLeft) {
+ char *p = ubuf;
+ size_t n = sizeof(ubuf), one_uchar = sizeof(PRUnichar);
+ res = xp_iconv(gUnicodeToUTF8, &in, &one_uchar, &p, &n);
+ if (res == (size_t) -1) {
+ NS_ERROR("conversion from utf-16 to utf-8 failed");
+ break;
+ }
+ p = ubuf;
+ n = sizeof(ubuf) - n;
+ res = xp_iconv(gUTF8ToNative, (const char **) &p, &n, output, &outLeft);
+ if (res == (size_t) -1) {
+ if (errno == E2BIG) {
+ // not enough room for last uchar... back up and return.
+ in -= sizeof(PRUnichar);
+ res = 0;
+ }
+ else
+ NS_ERROR("conversion from utf-8 to native failed");
+ break;
+ }
+ inLeft -= sizeof(PRUnichar);
+ }
+
+ if (res != (size_t) -1) {
+ (*input) += (*inputLeft - inLeft/2);
+ *inputLeft = inLeft/2;
+ *outputLeft = outLeft;
+ return NS_OK;
+ }
+
+ // reset converters
+ xp_iconv_reset(gUnicodeToUTF8);
+ xp_iconv_reset(gUTF8ToNative);
+ }
+#endif
+
+ // fallback: truncate and hope for the best
+ utf16_to_isolatin1(input, inputLeft, output, outputLeft);
+
+ return NS_OK;
+}
+
+#endif // USE_ICONV
+
+//-----------------------------------------------------------------------------
+// conversion using mb[r]towc/wc[r]tomb
+//-----------------------------------------------------------------------------
+#if defined(USE_STDCONV)
+#if defined(HAVE_WCRTOMB) || defined(HAVE_MBRTOWC)
+#include <wchar.h> // mbrtowc, wcrtomb
+#endif
+
+class nsNativeCharsetConverter
+{
+public:
+ nsNativeCharsetConverter();
+
+ nsresult NativeToUnicode(const char **input , PRUint32 *inputLeft,
+ PRUnichar **output, PRUint32 *outputLeft);
+ nsresult UnicodeToNative(const PRUnichar **input , PRUint32 *inputLeft,
+ char **output, PRUint32 *outputLeft);
+
+ static void GlobalInit();
+ static void GlobalShutdown() { }
+
+private:
+ static PRBool gWCharIsUnicode;
+
+#if defined(HAVE_WCRTOMB) || defined(HAVE_MBRTOWC)
+ mbstate_t ps;
+#endif
+};
+
+PRBool nsNativeCharsetConverter::gWCharIsUnicode = PR_FALSE;
+
+nsNativeCharsetConverter::nsNativeCharsetConverter()
+{
+#if defined(HAVE_WCRTOMB) || defined(HAVE_MBRTOWC)
+ memset(&ps, 0, sizeof(ps));
+#endif
+}
+
+void
+nsNativeCharsetConverter::GlobalInit()
+{
+ // verify that wchar_t for the current locale is actually unicode.
+ // if it is not, then we should avoid calling mbtowc/wctomb and
+ // just fallback on zero-pad/truncation conversion.
+ //
+ // this test cannot be done at build time because the encoding of
+ // wchar_t may depend on the runtime locale. sad, but true!!
+ //
+ // so, if wchar_t is unicode then converting an ASCII character
+ // to wchar_t should not change its numeric value. we'll just
+ // check what happens with the ASCII 'a' character.
+ //
+ // this test is not perfect... obviously, it could yield false
+ // positives, but then at least ASCII text would be converted
+ // properly (or maybe just the 'a' character) -- oh well :(
+
+ char a = 'a';
+ unsigned int w = 0;
+
+ int res = mbtowc((wchar_t *) &w, &a, 1);
+
+ gWCharIsUnicode = (res != -1 && w == 'a');
+
+#ifdef DEBUG
+ if (!gWCharIsUnicode)
+ NS_WARNING("wchar_t is not unicode (unicode conversion will be lossy)");
+#endif
+}
+
+nsresult
+nsNativeCharsetConverter::NativeToUnicode(const char **input,
+ PRUint32 *inputLeft,
+ PRUnichar **output,
+ PRUint32 *outputLeft)
+{
+ if (gWCharIsUnicode) {
+ int incr;
+
+ // cannot use wchar_t here since it may have been redefined (e.g.,
+ // via -fshort-wchar). hopefully, sizeof(tmp) is sufficient XP.
+ unsigned int tmp = 0;
+ while (*inputLeft && *outputLeft) {
+#ifdef HAVE_MBRTOWC
+ incr = (int) mbrtowc((wchar_t *) &tmp, *input, *inputLeft, &ps);
+#else
+ // XXX is this thread-safe?
+ incr = (int) mbtowc((wchar_t *) &tmp, *input, *inputLeft);
+#endif
+ if (incr < 0) {
+ NS_WARNING("mbtowc failed: possible charset mismatch");
+ // zero-pad and hope for the best
+ tmp = (unsigned char) **input;
+ incr = 1;
+ }
+ **output = (PRUnichar) tmp;
+ (*input) += incr;
+ (*inputLeft) -= incr;
+ (*output)++;
+ (*outputLeft)--;
+ }
+ }
+ else {
+ // wchar_t isn't unicode, so the best we can do is treat the
+ // input as if it is isolatin1 :(
+ isolatin1_to_utf16(input, inputLeft, output, outputLeft);
+ }
+
+ return NS_OK;
+}
+
+nsresult
+nsNativeCharsetConverter::UnicodeToNative(const PRUnichar **input,
+ PRUint32 *inputLeft,
+ char **output,
+ PRUint32 *outputLeft)
+{
+ if (gWCharIsUnicode) {
+ int incr;
+
+ while (*inputLeft && *outputLeft >= MB_CUR_MAX) {
+#ifdef HAVE_WCRTOMB
+ incr = (int) wcrtomb(*output, (wchar_t) **input, &ps);
+#else
+ // XXX is this thread-safe?
+ incr = (int) wctomb(*output, (wchar_t) **input);
+#endif
+ if (incr < 0) {
+ NS_WARNING("mbtowc failed: possible charset mismatch");
+ **output = (unsigned char) **input; // truncate
+ incr = 1;
+ }
+ // most likely we're dead anyways if this assertion should fire
+ NS_ASSERTION(PRUint32(incr) <= *outputLeft, "wrote beyond end of string");
+ (*output) += incr;
+ (*outputLeft) -= incr;
+ (*input)++;
+ (*inputLeft)--;
+ }
+ }
+ else {
+ // wchar_t isn't unicode, so the best we can do is treat the
+ // input as if it is isolatin1 :(
+ utf16_to_isolatin1(input, inputLeft, output, outputLeft);
+ }
+
+ return NS_OK;
+}
+
+#endif // USE_STDCONV
+
+//-----------------------------------------------------------------------------
+// API implementation
+//-----------------------------------------------------------------------------
+
+NS_COM nsresult
+NS_CopyNativeToUnicode(const nsACString &input, nsAString &output)
+{
+ output.Truncate();
+
+ PRUint32 inputLen = input.Length();
+
+ nsACString::const_iterator iter;
+ input.BeginReading(iter);
+
+ //
+ // OPTIMIZATION: preallocate space for largest possible result; convert
+ // directly into the result buffer to avoid intermediate buffer copy.
+ //
+ // this will generally result in a larger allocation, but that seems
+ // better than an extra buffer copy.
+ //
+ output.SetLength(inputLen);
+ nsAString::iterator out_iter;
+ output.BeginWriting(out_iter);
+
+ PRUnichar *result = out_iter.get();
+ PRUint32 resultLeft = inputLen;
+
+ const char *buf = iter.get();
+ PRUint32 bufLeft = inputLen;
+
+ nsNativeCharsetConverter conv;
+ nsresult rv = conv.NativeToUnicode(&buf, &bufLeft, &result, &resultLeft);
+ if (NS_SUCCEEDED(rv)) {
+ NS_ASSERTION(bufLeft == 0, "did not consume entire input buffer");
+ output.SetLength(inputLen - resultLeft);
+ }
+ return rv;
+}
+
+NS_COM nsresult
+NS_CopyUnicodeToNative(const nsAString &input, nsACString &output)
+{
+ output.Truncate();
+
+ nsAString::const_iterator iter, end;
+ input.BeginReading(iter);
+ input.EndReading(end);
+
+ // cannot easily avoid intermediate buffer copy.
+ char temp[4096];
+
+ nsNativeCharsetConverter conv;
+
+ const PRUnichar *buf = iter.get();
+ PRUint32 bufLeft = Distance(iter, end);
+ while (bufLeft) {
+ char *p = temp;
+ PRUint32 tempLeft = sizeof(temp);
+
+ nsresult rv = conv.UnicodeToNative(&buf, &bufLeft, &p, &tempLeft);
+ if (NS_FAILED(rv)) return rv;
+
+ if (tempLeft < sizeof(temp))
+ output.Append(temp, sizeof(temp) - tempLeft);
+ }
+ return NS_OK;
+}
+
+void
+NS_StartupNativeCharsetUtils()
+{
+ //
+ // need to initialize the locale or else charset conversion will fail.
+ // better not delay this in case some other component alters the locale
+ // settings.
+ //
+ // XXX we assume that we are called early enough that we should
+ // always be the first to care about the locale's charset.
+ //
+ setlocale(LC_CTYPE, "");
+
+ nsNativeCharsetConverter::GlobalInit();
+}
+
+void
+NS_ShutdownNativeCharsetUtils()
+{
+ nsNativeCharsetConverter::GlobalShutdown();
+}
+
+//-----------------------------------------------------------------------------
+// XP_BEOS
+//-----------------------------------------------------------------------------
+#elif defined(XP_BEOS)
+
+#include "nsAString.h"
+#include "nsReadableUtils.h"
+#include "nsString.h"
+
+NS_COM nsresult
+NS_CopyNativeToUnicode(const nsACString &input, nsAString &output)
+{
+ CopyUTF8toUTF16(input, output);
+ return NS_OK;
+}
+
+NS_COM nsresult
+NS_CopyUnicodeToNative(const nsAString &input, nsACString &output)
+{
+ CopyUTF16toUTF8(input, output);
+ return NS_OK;
+}
+
+void
+NS_StartupNativeCharsetUtils()
+{
+}
+
+void
+NS_ShutdownNativeCharsetUtils()
+{
+}
+
+//-----------------------------------------------------------------------------
+// XP_WIN
+//-----------------------------------------------------------------------------
+#elif defined(XP_WIN)
+
+#include <windows.h>
+#include "nsAString.h"
+
+NS_COM nsresult
+NS_CopyNativeToUnicode(const nsACString &input, nsAString &output)
+{
+ PRUint32 inputLen = input.Length();
+
+ nsACString::const_iterator iter;
+ input.BeginReading(iter);
+
+ const char *buf = iter.get();
+
+ // determine length of result
+ PRUint32 resultLen = 0;
+ int n = ::MultiByteToWideChar(CP_ACP, 0, buf, inputLen, NULL, 0);
+ if (n > 0)
+ resultLen += n;
+
+ // allocate sufficient space
+ output.SetLength(resultLen);
+ if (resultLen > 0) {
+ nsAString::iterator out_iter;
+ output.BeginWriting(out_iter);
+
+ PRUnichar *result = out_iter.get();
+
+ ::MultiByteToWideChar(CP_ACP, 0, buf, inputLen, result, resultLen);
+ }
+ return NS_OK;
+}
+
+NS_COM nsresult
+NS_CopyUnicodeToNative(const nsAString &input, nsACString &output)
+{
+ PRUint32 inputLen = input.Length();
+
+ nsAString::const_iterator iter;
+ input.BeginReading(iter);
+
+ const PRUnichar *buf = iter.get();
+
+ // determine length of result
+ PRUint32 resultLen = 0;
+
+ int n = ::WideCharToMultiByte(CP_ACP, 0, buf, inputLen, NULL, 0, NULL, NULL);
+ if (n > 0)
+ resultLen += n;
+
+ // allocate sufficient space
+ output.SetLength(resultLen);
+ if (resultLen > 0) {
+ nsACString::iterator out_iter;
+ output.BeginWriting(out_iter);
+
+ // default "defaultChar" is '?', which is an illegal character on windows
+ // file system. That will cause file uncreatable. Change it to '_'
+ const char defaultChar = '_';
+
+ char *result = out_iter.get();
+
+ ::WideCharToMultiByte(CP_ACP, 0, buf, inputLen, result, resultLen,
+ &defaultChar, NULL);
+ }
+ return NS_OK;
+}
+
+void
+NS_StartupNativeCharsetUtils()
+{
+}
+
+void
+NS_ShutdownNativeCharsetUtils()
+{
+}
+
+//-----------------------------------------------------------------------------
+// XP_OS2
+//-----------------------------------------------------------------------------
+#elif defined(XP_OS2)
+
+#define INCL_DOS
+#include <os2.h>
+#include <uconv.h>
+#include "nsAString.h"
+#include <ulserrno.h>
+#include "nsNativeCharsetUtils.h"
+
+static UconvObject UnicodeConverter = NULL;
+
+NS_COM nsresult
+NS_CopyNativeToUnicode(const nsACString &input, nsAString &output)
+{
+ PRUint32 inputLen = input.Length();
+
+ nsACString::const_iterator iter;
+ input.BeginReading(iter);
+ const char *inputStr = iter.get();
+
+ // determine length of result
+ PRUint32 resultLen = inputLen;
+ output.SetLength(resultLen);
+
+ nsAString::iterator out_iter;
+ output.BeginWriting(out_iter);
+ UniChar *result = (UniChar*)out_iter.get();
+
+ size_t cSubs = 0;
+ size_t resultLeft = resultLen;
+
+ if (!UnicodeConverter)
+ NS_StartupNativeCharsetUtils();
+
+ int unirc = ::UniUconvToUcs(UnicodeConverter, (void**)&inputStr, &inputLen,
+ &result, &resultLeft, &cSubs);
+
+ NS_ASSERTION(unirc != UCONV_E2BIG, "Path too big");
+
+ if (unirc != ULS_SUCCESS) {
+ output.Truncate();
+ return NS_ERROR_FAILURE;
+ }
+
+ // Need to update string length to reflect how many bytes were actually
+ // written.
+ output.Truncate(resultLen - resultLeft);
+ return NS_OK;
+}
+
+NS_COM nsresult
+NS_CopyUnicodeToNative(const nsAString &input, nsACString &output)
+{
+ size_t inputLen = input.Length();
+
+ nsAString::const_iterator iter;
+ input.BeginReading(iter);
+ UniChar* inputStr = (UniChar*) NS_CONST_CAST(PRUnichar*, iter.get());
+
+ // maximum length of unicode string of length x converted to native
+ // codepage is x*2
+ size_t resultLen = inputLen * 2;
+ output.SetLength(resultLen);
+
+ nsACString::iterator out_iter;
+ output.BeginWriting(out_iter);
+ char *result = out_iter.get();
+
+ size_t cSubs = 0;
+ size_t resultLeft = resultLen;
+
+ if (!UnicodeConverter)
+ NS_StartupNativeCharsetUtils();
+
+ int unirc = ::UniUconvFromUcs(UnicodeConverter, &inputStr, &inputLen,
+ (void**)&result, &resultLeft, &cSubs);
+
+ NS_ASSERTION(unirc != UCONV_E2BIG, "Path too big");
+
+ if (unirc != ULS_SUCCESS) {
+ output.Truncate();
+ return NS_ERROR_FAILURE;
+ }
+
+ // Need to update string length to reflect how many bytes were actually
+ // written.
+ output.Truncate(resultLen - resultLeft);
+ return NS_OK;
+}
+
+void
+NS_StartupNativeCharsetUtils()
+{
+ ULONG ulLength;
+ ULONG ulCodePage;
+ DosQueryCp(sizeof(ULONG), &ulCodePage, &ulLength);
+
+ UniChar codepage[20];
+ int unirc = ::UniMapCpToUcsCp(ulCodePage, codepage, 20);
+ if (unirc == ULS_SUCCESS) {
+ unirc = ::UniCreateUconvObject(codepage, &UnicodeConverter);
+ if (unirc == ULS_SUCCESS) {
+ uconv_attribute_t attr;
+ ::UniQueryUconvObject(UnicodeConverter, &attr, sizeof(uconv_attribute_t),
+ NULL, NULL, NULL);
+ attr.options = UCONV_OPTION_SUBSTITUTE_BOTH;
+ attr.subchar_len=1;
+ attr.subchar[0]='_';
+ ::UniSetUconvObject(UnicodeConverter, &attr);
+ }
+ }
+}
+
+void
+NS_ShutdownNativeCharsetUtils()
+{
+ ::UniFreeUconvObject(UnicodeConverter);
+}
+
+//-----------------------------------------------------------------------------
+// XP_MAC
+//-----------------------------------------------------------------------------
+#elif defined(XP_MAC)
+
+#include <UnicodeConverter.h>
+#include <TextCommon.h>
+#include <Script.h>
+#include <MacErrors.h>
+#include "nsAString.h"
+
+class nsFSStringConversionMac {
+public:
+ static nsresult UCSToFS(const nsAString& aIn, nsACString& aOut);
+ static nsresult FSToUCS(const nsACString& ain, nsAString& aOut);
+
+ static void CleanUp();
+
+private:
+ static TextEncoding GetSystemEncoding();
+ static nsresult PrepareEncoder();
+ static nsresult PrepareDecoder();
+
+ static UnicodeToTextInfo sEncoderInfo;
+ static TextToUnicodeInfo sDecoderInfo;
+};
+
+UnicodeToTextInfo nsFSStringConversionMac::sEncoderInfo = nsnull;
+TextToUnicodeInfo nsFSStringConversionMac::sDecoderInfo = nsnull;
+
+nsresult nsFSStringConversionMac::UCSToFS(const nsAString& aIn, nsACString& aOut)
+{
+ nsresult rv = PrepareEncoder();
+ if (NS_FAILED(rv)) return rv;
+
+ OSStatus err = noErr;
+ char stackBuffer[512];
+
+ aOut.Truncate();
+
+ // for each chunk of |aIn|...
+ nsReadingIterator<PRUnichar> iter;
+ aIn.BeginReading(iter);
+
+ PRUint32 fragmentLength = PRUint32(iter.size_forward());
+ UInt32 bytesLeft = fragmentLength * sizeof(UniChar);
+
+ do {
+ UInt32 bytesRead = 0, bytesWritten = 0;
+ err = ::ConvertFromUnicodeToText(sEncoderInfo,
+ bytesLeft,
+ (const UniChar*)iter.get(),
+ kUnicodeUseFallbacksMask | kUnicodeLooseMappingsMask,
+ 0, nsnull, nsnull, nsnull,
+ sizeof(stackBuffer),
+ &bytesRead,
+ &bytesWritten,
+ stackBuffer);
+ if (err == kTECUsedFallbacksStatus)
+ err = noErr;
+ else if (err == kTECOutputBufferFullStatus) {
+ bytesLeft -= bytesRead;
+ iter.advance(bytesRead / sizeof(UniChar));
+ }
+ aOut.Append(stackBuffer, bytesWritten);
+ }
+ while (err == kTECOutputBufferFullStatus);
+
+ return (err == noErr) ? NS_OK : NS_ERROR_FAILURE;
+}
+
+nsresult nsFSStringConversionMac::FSToUCS(const nsACString& aIn, nsAString& aOut)
+{
+ nsresult rv = PrepareDecoder();
+ if (NS_FAILED(rv)) return rv;
+
+ OSStatus err = noErr;
+ UniChar stackBuffer[512];
+
+ aOut.Truncate(0);
+
+ // for each chunk of |aIn|...
+ nsReadingIterator<char> iter;
+ aIn.BeginReading(iter);
+
+ PRUint32 fragmentLength = PRUint32(iter.size_forward());
+ UInt32 bytesLeft = fragmentLength;
+
+ do {
+ UInt32 bytesRead = 0, bytesWritten = 0;
+ err = ::ConvertFromTextToUnicode(sDecoderInfo,
+ bytesLeft,
+ iter.get(),
+ kUnicodeUseFallbacksMask | kUnicodeLooseMappingsMask,
+ 0, nsnull, nsnull, nsnull,
+ sizeof(stackBuffer),
+ &bytesRead,
+ &bytesWritten,
+ stackBuffer);
+ if (err == kTECUsedFallbacksStatus)
+ err = noErr;
+ else if (err == kTECOutputBufferFullStatus) {
+ bytesLeft -= bytesRead;
+ iter.advance(bytesRead);
+ }
+ aOut.Append((PRUnichar *)stackBuffer, bytesWritten / sizeof(PRUnichar));
+ }
+ while (err == kTECOutputBufferFullStatus);
+
+ return (err == noErr) ? NS_OK : NS_ERROR_FAILURE;
+}
+
+void nsFSStringConversionMac::CleanUp()
+{
+ if (sDecoderInfo) {
+ ::DisposeTextToUnicodeInfo(&sDecoderInfo);
+ sDecoderInfo = nsnull;
+ }
+ if (sEncoderInfo) {
+ ::DisposeUnicodeToTextInfo(&sEncoderInfo);
+ sEncoderInfo = nsnull;
+ }
+}
+
+TextEncoding nsFSStringConversionMac::GetSystemEncoding()
+{
+ OSStatus err;
+ TextEncoding theEncoding;
+
+ err = ::UpgradeScriptInfoToTextEncoding(smSystemScript, kTextLanguageDontCare,
+ kTextRegionDontCare, NULL, &theEncoding);
+
+ if (err != noErr)
+ theEncoding = kTextEncodingMacRoman;
+
+ return theEncoding;
+}
+
+nsresult nsFSStringConversionMac::PrepareEncoder()
+{
+ nsresult rv = NS_OK;
+ if (!sEncoderInfo) {
+ OSStatus err;
+ err = ::CreateUnicodeToTextInfoByEncoding(GetSystemEncoding(), &sEncoderInfo);
+ if (err)
+ rv = NS_ERROR_FAILURE;
+ }
+ return rv;
+}
+
+nsresult nsFSStringConversionMac::PrepareDecoder()
+{
+ nsresult rv = NS_OK;
+ if (!sDecoderInfo) {
+ OSStatus err;
+ err = ::CreateTextToUnicodeInfoByEncoding(GetSystemEncoding(), &sDecoderInfo);
+ if (err)
+ rv = NS_ERROR_FAILURE;
+ }
+ return rv;
+}
+
+NS_COM nsresult
+NS_CopyNativeToUnicode(const nsACString &input, nsAString &output)
+{
+ return nsFSStringConversionMac::FSToUCS(input, output);
+}
+
+NS_COM nsresult
+NS_CopyUnicodeToNative(const nsAString &input, nsACString &output)
+{
+ return nsFSStringConversionMac::UCSToFS(input, output);
+}
+
+void
+NS_StartupNativeCharsetUtils()
+{
+}
+
+void
+NS_ShutdownNativeCharsetUtils()
+{
+ nsFSStringConversionMac::CleanUp();
+}
+
+//-----------------------------------------------------------------------------
+// default : truncate/zeropad
+//-----------------------------------------------------------------------------
+#else
+
+#include "nsReadableUtils.h"
+
+NS_COM nsresult
+NS_CopyNativeToUnicode(const nsACString &input, nsAString &output)
+{
+ CopyASCIItoUCS2(input, output);
+ return NS_OK;
+}
+
+NS_COM nsresult
+NS_CopyUnicodeToNative(const nsAString &input, nsACString &output)
+{
+ CopyUCS2toASCII(input, output);
+ return NS_OK;
+}
+
+void
+NS_StartupNativeCharsetUtils()
+{
+}
+
+void
+NS_ShutdownNativeCharsetUtils()
+{
+}
+
+#endif
diff --git a/src/libs/xpcom18a4/xpcom/io/nsNativeCharsetUtils.h b/src/libs/xpcom18a4/xpcom/io/nsNativeCharsetUtils.h
new file mode 100644
index 00000000..539fa37f
--- /dev/null
+++ b/src/libs/xpcom18a4/xpcom/io/nsNativeCharsetUtils.h
@@ -0,0 +1,66 @@
+/* ***** 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.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2002
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Darin Fisher <darin@netscape.com>
+ *
+ * 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 nsNativeCharsetUtils_h__
+#define nsNativeCharsetUtils_h__
+
+
+/*****************************************************************************\
+ * *
+ * **** NOTICE **** *
+ * *
+ * *** THESE ARE NOT GENERAL PURPOSE CONVERTERS *** *
+ * *
+ * NS_CopyNativeToUnicode / NS_CopyUnicodeToNative should only be used *
+ * for converting *FILENAMES* between native and unicode. They are not *
+ * designed or tested for general encoding converter use. *
+ * *
+\*****************************************************************************/
+
+/**
+ * thread-safe conversion routines that do not depend on uconv libraries.
+ */
+NS_COM nsresult NS_CopyNativeToUnicode(const nsACString &input, nsAString &output);
+NS_COM nsresult NS_CopyUnicodeToNative(const nsAString &input, nsACString &output);
+
+/**
+ * internal
+ */
+void NS_StartupNativeCharsetUtils();
+void NS_ShutdownNativeCharsetUtils();
+
+#endif // nsNativeCharsetUtils_h__
diff --git a/src/libs/xpcom18a4/xpcom/io/nsPipe3.cpp b/src/libs/xpcom18a4/xpcom/io/nsPipe3.cpp
new file mode 100644
index 00000000..be06179f
--- /dev/null
+++ b/src/libs/xpcom18a4/xpcom/io/nsPipe3.cpp
@@ -0,0 +1,1276 @@
+/* ***** 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.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2002
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Darin Fisher <darin@netscape.com>
+ *
+ * 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 "nsIPipe.h"
+#include "nsIEventTarget.h"
+#include "nsISeekableStream.h"
+#include "nsSegmentedBuffer.h"
+#include "nsStreamUtils.h"
+#include "nsAutoLock.h"
+#include "nsCOMPtr.h"
+#include "nsCRT.h"
+#include "prlog.h"
+#include "nsInt64.h"
+
+#if defined(PR_LOGGING)
+//
+// set NSPR_LOG_MODULES=nsPipe:5
+//
+static PRLogModuleInfo *gPipeLog = nsnull;
+#define LOG(args) PR_LOG(gPipeLog, PR_LOG_DEBUG, args)
+#else
+#define LOG(args)
+#endif
+
+#define DEFAULT_SEGMENT_SIZE 4096
+#define DEFAULT_SEGMENT_COUNT 16
+
+class nsPipe;
+class nsPipeEvents;
+class nsPipeInputStream;
+class nsPipeOutputStream;
+
+//-----------------------------------------------------------------------------
+
+// this class is used to delay notifications until the end of a particular
+// scope. it helps avoid the complexity of issuing callbacks while inside
+// a critical section.
+class nsPipeEvents
+{
+public:
+ nsPipeEvents() { }
+ ~nsPipeEvents();
+
+ inline void NotifyInputReady(nsIAsyncInputStream *stream,
+ nsIInputStreamCallback *callback)
+ {
+ NS_ASSERTION(!mInputCallback, "already have an input event");
+ mInputStream = stream;
+ mInputCallback = callback;
+ }
+
+ inline void NotifyOutputReady(nsIAsyncOutputStream *stream,
+ nsIOutputStreamCallback *callback)
+ {
+ NS_ASSERTION(!mOutputCallback, "already have an output event");
+ mOutputStream = stream;
+ mOutputCallback = callback;
+ }
+
+private:
+ nsCOMPtr<nsIAsyncInputStream> mInputStream;
+ nsCOMPtr<nsIInputStreamCallback> mInputCallback;
+ nsCOMPtr<nsIAsyncOutputStream> mOutputStream;
+ nsCOMPtr<nsIOutputStreamCallback> mOutputCallback;
+};
+
+//-----------------------------------------------------------------------------
+
+// the input end of a pipe (allocated as a member of the pipe).
+class nsPipeInputStream : public nsIAsyncInputStream
+ , public nsISeekableStream
+ , public nsISearchableInputStream
+{
+public:
+ // since this class will be allocated as a member of the pipe, we do not
+ // need our own ref count. instead, we share the lifetime (the ref count)
+ // of the entire pipe. this macro is just convenience since it does not
+ // declare a mRefCount variable; however, don't let the name fool you...
+ // we are not inheriting from nsPipe ;-)
+ NS_DECL_ISUPPORTS_INHERITED
+
+ NS_DECL_NSIINPUTSTREAM
+ NS_DECL_NSIASYNCINPUTSTREAM
+ NS_DECL_NSISEEKABLESTREAM
+ NS_DECL_NSISEARCHABLEINPUTSTREAM
+
+ nsPipeInputStream(nsPipe *pipe)
+ : mPipe(pipe)
+ , mReaderRefCnt(0)
+ , mLogicalOffset(0)
+ , mBlocking(PR_TRUE)
+ , mBlocked(PR_FALSE)
+ , mAvailable(0)
+ , mCallbackFlags(0)
+ { }
+
+ nsresult Fill();
+ void SetNonBlocking(PRBool aNonBlocking) { mBlocking = !aNonBlocking; }
+
+ PRUint32 Available() { return mAvailable; }
+ void ReduceAvailable(PRUint32 avail) { mAvailable -= avail; }
+
+ // synchronously wait for the pipe to become readable.
+ nsresult Wait();
+
+ // these functions return true to indicate that the pipe's monitor should
+ // be notified, to wake up a blocked reader if any.
+ PRBool OnInputReadable(PRUint32 bytesWritten, nsPipeEvents &);
+ PRBool OnInputException(nsresult, nsPipeEvents &);
+
+private:
+ nsPipe *mPipe;
+
+ // separate refcnt so that we know when to close the consumer
+ nsrefcnt mReaderRefCnt;
+ nsInt64 mLogicalOffset;
+ PRPackedBool mBlocking;
+
+ // these variables can only be accessed while inside the pipe's monitor
+ PRPackedBool mBlocked;
+ PRUint32 mAvailable;
+ nsCOMPtr<nsIInputStreamCallback> mCallback;
+ PRUint32 mCallbackFlags;
+};
+
+//-----------------------------------------------------------------------------
+
+// the output end of a pipe (allocated as a member of the pipe).
+class nsPipeOutputStream : public nsIAsyncOutputStream
+ , public nsISeekableStream
+{
+public:
+ // since this class will be allocated as a member of the pipe, we do not
+ // need our own ref count. instead, we share the lifetime (the ref count)
+ // of the entire pipe. this macro is just convenience since it does not
+ // declare a mRefCount variable; however, don't let the name fool you...
+ // we are not inheriting from nsPipe ;-)
+ NS_DECL_ISUPPORTS_INHERITED
+
+ NS_DECL_NSIOUTPUTSTREAM
+ NS_DECL_NSIASYNCOUTPUTSTREAM
+ NS_DECL_NSISEEKABLESTREAM
+
+ nsPipeOutputStream(nsPipe *pipe)
+ : mPipe(pipe)
+ , mWriterRefCnt(0)
+ , mLogicalOffset(0)
+ , mBlocking(PR_TRUE)
+ , mBlocked(PR_FALSE)
+ , mWritable(PR_TRUE)
+ , mCallbackFlags(0)
+ { }
+
+ void SetNonBlocking(PRBool aNonBlocking) { mBlocking = !aNonBlocking; }
+ void SetWritable(PRBool writable) { mWritable = writable; }
+
+ // synchronously wait for the pipe to become writable.
+ nsresult Wait();
+
+ // these functions return true to indicate that the pipe's monitor should
+ // be notified, to wake up a blocked writer if any.
+ PRBool OnOutputWritable(nsPipeEvents &);
+ PRBool OnOutputException(nsresult, nsPipeEvents &);
+
+private:
+ nsPipe *mPipe;
+
+ // separate refcnt so that we know when to close the producer
+ nsrefcnt mWriterRefCnt;
+ nsInt64 mLogicalOffset;
+ PRPackedBool mBlocking;
+
+ // these variables can only be accessed while inside the pipe's monitor
+ PRPackedBool mBlocked;
+ PRPackedBool mWritable;
+ nsCOMPtr<nsIOutputStreamCallback> mCallback;
+ PRUint32 mCallbackFlags;
+};
+
+//-----------------------------------------------------------------------------
+
+class nsPipe : public nsIPipe
+{
+public:
+ friend class nsPipeInputStream;
+ friend class nsPipeOutputStream;
+
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIPIPE
+
+ // nsPipe methods:
+ nsPipe();
+
+private:
+ ~nsPipe();
+
+public:
+ //
+ // methods below may only be called while inside the pipe's monitor
+ //
+
+ void PeekSegment(PRUint32 n, char *&cursor, char *&limit);
+
+ //
+ // methods below may be called while outside the pipe's monitor
+ //
+
+ nsresult GetReadSegment(const char *&segment, PRUint32 &segmentLen);
+ void AdvanceReadCursor(PRUint32 count);
+
+ nsresult GetWriteSegment(char *&segment, PRUint32 &segmentLen);
+ void AdvanceWriteCursor(PRUint32 count);
+
+ void OnPipeException(nsresult reason, PRBool outputOnly = PR_FALSE);
+
+protected:
+ // We can't inherit from both nsIInputStream and nsIOutputStream
+ // because they collide on their Close method. Consequently we nest their
+ // implementations to avoid the extra object allocation.
+ nsPipeInputStream mInput;
+ nsPipeOutputStream mOutput;
+
+ PRMonitor* mMonitor;
+ nsSegmentedBuffer mBuffer;
+
+ char* mReadCursor;
+ char* mReadLimit;
+
+ PRInt32 mWriteSegment;
+ char* mWriteCursor;
+ char* mWriteLimit;
+
+ nsresult mStatus;
+};
+
+//
+// NOTES on buffer architecture:
+//
+// +-----------------+ - - mBuffer.GetSegment(0)
+// | |
+// + - - - - - - - - + - - mReadCursor
+// |/////////////////|
+// |/////////////////|
+// |/////////////////|
+// |/////////////////|
+// +-----------------+ - - mReadLimit
+// |
+// +-----------------+
+// |/////////////////|
+// |/////////////////|
+// |/////////////////|
+// |/////////////////|
+// |/////////////////|
+// |/////////////////|
+// +-----------------+
+// |
+// +-----------------+ - - mBuffer.GetSegment(mWriteSegment)
+// |/////////////////|
+// |/////////////////|
+// |/////////////////|
+// + - - - - - - - - + - - mWriteCursor
+// | |
+// | |
+// +-----------------+ - - mWriteLimit
+//
+// (shaded region contains data)
+//
+// NOTE: on some systems (notably OS/2), the heap allocator uses an arena for
+// small allocations (e.g., 64 byte allocations). this means that buffers may
+// be allocated back-to-back. in the diagram above, for example, mReadLimit
+// would actually be pointing at the beginning of the next segment. when
+// making changes to this file, please keep this fact in mind.
+//
+
+//-----------------------------------------------------------------------------
+// nsPipe methods:
+//-----------------------------------------------------------------------------
+
+nsPipe::nsPipe()
+ : mInput(this)
+ , mOutput(this)
+ , mMonitor(nsnull)
+ , mReadCursor(nsnull)
+ , mReadLimit(nsnull)
+ , mWriteSegment(-1)
+ , mWriteCursor(nsnull)
+ , mWriteLimit(nsnull)
+ , mStatus(NS_OK)
+{
+}
+
+nsPipe::~nsPipe()
+{
+ if (mMonitor)
+ PR_DestroyMonitor(mMonitor);
+}
+
+NS_IMPL_THREADSAFE_ISUPPORTS1(nsPipe, nsIPipe)
+
+NS_IMETHODIMP
+nsPipe::Init(PRBool nonBlockingIn,
+ PRBool nonBlockingOut,
+ PRUint32 segmentSize,
+ PRUint32 segmentCount,
+ nsIMemory *segmentAlloc)
+{
+ mMonitor = PR_NewMonitor();
+ if (!mMonitor)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ if (segmentSize == 0)
+ segmentSize = DEFAULT_SEGMENT_SIZE;
+ if (segmentCount == 0)
+ segmentCount = DEFAULT_SEGMENT_COUNT;
+
+ // protect against overflow
+ PRUint32 maxCount = PRUint32(-1) / segmentSize;
+ if (segmentCount > maxCount)
+ segmentCount = maxCount;
+
+ nsresult rv = mBuffer.Init(segmentSize, segmentSize * segmentCount, segmentAlloc);
+ if (NS_FAILED(rv))
+ return rv;
+
+ mInput.SetNonBlocking(nonBlockingIn);
+ mOutput.SetNonBlocking(nonBlockingOut);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsPipe::GetInputStream(nsIAsyncInputStream **aInputStream)
+{
+ NS_ADDREF(*aInputStream = &mInput);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsPipe::GetOutputStream(nsIAsyncOutputStream **aOutputStream)
+{
+ NS_ADDREF(*aOutputStream = &mOutput);
+ return NS_OK;
+}
+
+void
+nsPipe::PeekSegment(PRUint32 index, char *&cursor, char *&limit)
+{
+ if (index == 0) {
+ NS_ASSERTION(!mReadCursor || mBuffer.GetSegmentCount(), "unexpected state");
+ cursor = mReadCursor;
+ limit = mReadLimit;
+ }
+ else {
+ PRUint32 numSegments = mBuffer.GetSegmentCount();
+ if (index >= numSegments)
+ cursor = limit = nsnull;
+ else {
+ cursor = mBuffer.GetSegment(index);
+ if (mWriteSegment == (PRInt32) index)
+ limit = mWriteCursor;
+ else
+ limit = cursor + mBuffer.GetSegmentSize();
+ }
+ }
+}
+
+nsresult
+nsPipe::GetReadSegment(const char *&segment, PRUint32 &segmentLen)
+{
+ nsAutoMonitor mon(mMonitor);
+
+ if (mReadCursor == mReadLimit)
+ return NS_FAILED(mStatus) ? mStatus : NS_BASE_STREAM_WOULD_BLOCK;
+
+ segment = mReadCursor;
+ segmentLen = mReadLimit - mReadCursor;
+ return NS_OK;
+}
+
+void
+nsPipe::AdvanceReadCursor(PRUint32 bytesRead)
+{
+ NS_ASSERTION(bytesRead, "dont call if no bytes read");
+
+ nsPipeEvents events;
+ {
+ nsAutoMonitor mon(mMonitor);
+
+ LOG(("III advancing read cursor by %u\n", bytesRead));
+ NS_ASSERTION(bytesRead <= mBuffer.GetSegmentSize(), "read too much");
+
+ mReadCursor += bytesRead;
+ NS_ASSERTION(mReadCursor <= mReadLimit, "read cursor exceeds limit");
+
+ mInput.ReduceAvailable(bytesRead);
+
+ if (mReadCursor == mReadLimit) {
+ // we've reached the limit of how much we can read from this segment.
+ // if at the end of this segment, then we must discard this segment.
+
+ // if still writing in this segment then bail because we're not done
+ // with the segment and have to wait for now...
+ if (mWriteSegment == 0 && mWriteLimit > mWriteCursor) {
+ NS_ASSERTION(mReadLimit == mWriteCursor, "unexpected state");
+ return;
+ }
+
+ // shift write segment index (-1 indicates an empty buffer).
+ --mWriteSegment;
+
+ // done with this segment
+ mBuffer.DeleteFirstSegment();
+ LOG(("III deleting first segment\n"));
+
+ if (mWriteSegment == -1) {
+ // buffer is completely empty
+ mReadCursor = nsnull;
+ mReadLimit = nsnull;
+ mWriteCursor = nsnull;
+ mWriteLimit = nsnull;
+ }
+ else {
+ // advance read cursor and limit to next buffer segment
+ mReadCursor = mBuffer.GetSegment(0);
+ if (mWriteSegment == 0)
+ mReadLimit = mWriteCursor;
+ else
+ mReadLimit = mReadCursor + mBuffer.GetSegmentSize();
+ }
+
+ // we've free'd up a segment, so notify output stream that pipe has
+ // room for a new segment.
+ if (mOutput.OnOutputWritable(events))
+ mon.Notify();
+ }
+ }
+}
+
+nsresult
+nsPipe::GetWriteSegment(char *&segment, PRUint32 &segmentLen)
+{
+ nsAutoMonitor mon(mMonitor);
+
+ if (NS_FAILED(mStatus))
+ return mStatus;
+
+ // write cursor and limit may both be null indicating an empty buffer.
+ if (mWriteCursor == mWriteLimit) {
+ char *seg = mBuffer.AppendNewSegment();
+ // pipe is full
+ if (seg == nsnull)
+ return NS_BASE_STREAM_WOULD_BLOCK;
+ LOG(("OOO appended new segment\n"));
+ mWriteCursor = seg;
+ mWriteLimit = mWriteCursor + mBuffer.GetSegmentSize();
+ ++mWriteSegment;
+ }
+
+ // make sure read cursor is initialized
+ if (mReadCursor == nsnull) {
+ NS_ASSERTION(mWriteSegment == 0, "unexpected null read cursor");
+ mReadCursor = mReadLimit = mWriteCursor;
+ }
+
+ // check to see if we can roll-back our read and write cursors to the
+ // beginning of the current/first segment. this is purely an optimization.
+ if (mReadCursor == mWriteCursor && mWriteSegment == 0) {
+ char *head = mBuffer.GetSegment(0);
+ LOG(("OOO rolling back write cursor %u bytes\n", mWriteCursor - head));
+ mWriteCursor = mReadCursor = mReadLimit = head;
+ }
+
+ segment = mWriteCursor;
+ segmentLen = mWriteLimit - mWriteCursor;
+ return NS_OK;
+}
+
+void
+nsPipe::AdvanceWriteCursor(PRUint32 bytesWritten)
+{
+ NS_ASSERTION(bytesWritten, "dont call if no bytes written");
+
+ nsPipeEvents events;
+ {
+ nsAutoMonitor mon(mMonitor);
+
+ LOG(("OOO advancing write cursor by %u\n", bytesWritten));
+
+ char *newWriteCursor = mWriteCursor + bytesWritten;
+ NS_ASSERTION(newWriteCursor <= mWriteLimit, "write cursor exceeds limit");
+
+ // update read limit if reading in the same segment
+ if (mWriteSegment == 0 && mReadLimit == mWriteCursor)
+ mReadLimit = newWriteCursor;
+
+ mWriteCursor = newWriteCursor;
+
+ NS_ASSERTION(mReadCursor != mWriteCursor, "read cursor is bad");
+
+ // update the writable flag on the output stream
+ if (mWriteCursor == mWriteLimit) {
+ if (mBuffer.GetSize() >= mBuffer.GetMaxSize())
+ mOutput.SetWritable(PR_FALSE);
+ }
+
+ // notify input stream that pipe now contains additional data
+ if (mInput.OnInputReadable(bytesWritten, events))
+ mon.Notify();
+ }
+}
+
+void
+nsPipe::OnPipeException(nsresult reason, PRBool outputOnly)
+{
+ LOG(("PPP nsPipe::OnPipeException [reason=%x output-only=%d]\n",
+ reason, outputOnly));
+
+ nsPipeEvents events;
+ {
+ nsAutoMonitor mon(mMonitor);
+
+ // if we've already hit an exception, then ignore this one.
+ if (NS_FAILED(mStatus))
+ return;
+
+ mStatus = reason;
+
+ // an output-only exception applies to the input end if the pipe has
+ // zero bytes available.
+ if (outputOnly && !mInput.Available())
+ outputOnly = PR_FALSE;
+
+ if (!outputOnly)
+ if (mInput.OnInputException(reason, events))
+ mon.Notify();
+
+ if (mOutput.OnOutputException(reason, events))
+ mon.Notify();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// nsPipeEvents methods:
+//-----------------------------------------------------------------------------
+
+nsPipeEvents::~nsPipeEvents()
+{
+ // dispatch any pending events
+
+ if (mInputCallback) {
+ mInputCallback->OnInputStreamReady(mInputStream);
+ mInputCallback = 0;
+ mInputStream = 0;
+ }
+ if (mOutputCallback) {
+ mOutputCallback->OnOutputStreamReady(mOutputStream);
+ mOutputCallback = 0;
+ mOutputStream = 0;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// nsPipeInputStream methods:
+//-----------------------------------------------------------------------------
+
+nsresult
+nsPipeInputStream::Wait()
+{
+ NS_ASSERTION(mBlocking, "wait on non-blocking pipe input stream");
+
+ nsAutoMonitor mon(mPipe->mMonitor);
+
+ while (NS_SUCCEEDED(mPipe->mStatus) && (mAvailable == 0)) {
+ LOG(("III pipe input: waiting for data\n"));
+
+ mBlocked = PR_TRUE;
+ mon.Wait();
+ mBlocked = PR_FALSE;
+
+ LOG(("III pipe input: woke up [pipe-status=%x available=%u]\n",
+ mPipe->mStatus, mAvailable));
+ }
+
+ return mPipe->mStatus == NS_BASE_STREAM_CLOSED ? NS_OK : mPipe->mStatus;
+}
+
+PRBool
+nsPipeInputStream::OnInputReadable(PRUint32 bytesWritten, nsPipeEvents &events)
+{
+ PRBool result = PR_FALSE;
+
+ mAvailable += bytesWritten;
+
+ if (mCallback && !(mCallbackFlags & WAIT_CLOSURE_ONLY)) {
+ events.NotifyInputReady(this, mCallback);
+ mCallback = 0;
+ mCallbackFlags = 0;
+ }
+ else if (mBlocked)
+ result = PR_TRUE;
+
+ return result;
+}
+
+PRBool
+nsPipeInputStream::OnInputException(nsresult reason, nsPipeEvents &events)
+{
+ LOG(("nsPipeInputStream::OnInputException [this=%x reason=%x]\n",
+ this, reason));
+
+ PRBool result = PR_FALSE;
+
+ NS_ASSERTION(NS_FAILED(reason), "huh? successful exception");
+
+ // force count of available bytes to zero.
+ mAvailable = 0;
+
+ if (mCallback) {
+ events.NotifyInputReady(this, mCallback);
+ mCallback = 0;
+ mCallbackFlags = 0;
+ }
+ else if (mBlocked)
+ result = PR_TRUE;
+
+ return result;
+}
+
+NS_IMETHODIMP_(nsrefcnt)
+nsPipeInputStream::AddRef(void)
+{
+ ++mReaderRefCnt;
+ return mPipe->AddRef();
+}
+
+NS_IMETHODIMP_(nsrefcnt)
+nsPipeInputStream::Release(void)
+{
+ if (--mReaderRefCnt == 0)
+ Close();
+ return mPipe->Release();
+}
+
+NS_IMPL_QUERY_INTERFACE4(nsPipeInputStream,
+ nsIInputStream,
+ nsIAsyncInputStream,
+ nsISeekableStream,
+ nsISearchableInputStream)
+
+NS_IMETHODIMP
+nsPipeInputStream::CloseWithStatus(nsresult reason)
+{
+ LOG(("III CloseWithStatus [this=%x reason=%x]\n", this, reason));
+
+ if (NS_SUCCEEDED(reason))
+ reason = NS_BASE_STREAM_CLOSED;
+
+ mPipe->OnPipeException(reason);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsPipeInputStream::Close()
+{
+ return CloseWithStatus(NS_BASE_STREAM_CLOSED);
+}
+
+NS_IMETHODIMP
+nsPipeInputStream::Available(PRUint32 *result)
+{
+ nsAutoMonitor mon(mPipe->mMonitor);
+
+ // return error if pipe closed
+ if (!mAvailable && NS_FAILED(mPipe->mStatus))
+ return mPipe->mStatus;
+
+ *result = mAvailable;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsPipeInputStream::ReadSegments(nsWriteSegmentFun writer,
+ void *closure,
+ PRUint32 count,
+ PRUint32 *readCount)
+{
+ LOG(("III ReadSegments [this=%x count=%u]\n", this, count));
+
+ nsresult rv = NS_OK;
+
+ const char *segment;
+ PRUint32 segmentLen;
+
+ *readCount = 0;
+ while (count) {
+ rv = mPipe->GetReadSegment(segment, segmentLen);
+ if (NS_FAILED(rv)) {
+ // ignore this error if we've already read something.
+ if (*readCount > 0) {
+ rv = NS_OK;
+ break;
+ }
+ if (rv == NS_BASE_STREAM_WOULD_BLOCK) {
+ // pipe is empty
+ if (!mBlocking)
+ break;
+ // wait for some data to be written to the pipe
+ rv = Wait();
+ if (NS_SUCCEEDED(rv))
+ continue;
+ }
+ // ignore this error, just return.
+ if (rv == NS_BASE_STREAM_CLOSED) {
+ rv = NS_OK;
+ break;
+ }
+ mPipe->OnPipeException(rv);
+ break;
+ }
+
+ // read no more than count
+ if (segmentLen > count)
+ segmentLen = count;
+
+ PRUint32 writeCount, originalLen = segmentLen;
+ while (segmentLen) {
+ writeCount = 0;
+
+ rv = writer(this, closure, segment, *readCount, segmentLen, &writeCount);
+
+ if (NS_FAILED(rv) || writeCount == 0) {
+ count = 0;
+ // any errors returned from the writer end here: do not
+ // propogate to the caller of ReadSegments.
+ rv = NS_OK;
+ break;
+ }
+
+ NS_ASSERTION(writeCount <= segmentLen, "wrote more than expected");
+ segment += writeCount;
+ segmentLen -= writeCount;
+ count -= writeCount;
+ *readCount += writeCount;
+ mLogicalOffset += writeCount;
+ }
+
+ if (segmentLen < originalLen)
+ mPipe->AdvanceReadCursor(originalLen - segmentLen);
+ }
+
+ return rv;
+}
+
+static NS_METHOD
+nsWriteToRawBuffer(nsIInputStream* inStr,
+ void *closure,
+ const char *fromRawSegment,
+ PRUint32 offset,
+ PRUint32 count,
+ PRUint32 *writeCount)
+{
+ char *toBuf = (char*)closure;
+ memcpy(&toBuf[offset], fromRawSegment, count);
+ *writeCount = count;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsPipeInputStream::Read(char* toBuf, PRUint32 bufLen, PRUint32 *readCount)
+{
+ return ReadSegments(nsWriteToRawBuffer, toBuf, bufLen, readCount);
+}
+
+NS_IMETHODIMP
+nsPipeInputStream::IsNonBlocking(PRBool *aNonBlocking)
+{
+ *aNonBlocking = !mBlocking;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsPipeInputStream::AsyncWait(nsIInputStreamCallback *callback,
+ PRUint32 flags,
+ PRUint32 requestedCount,
+ nsIEventTarget *target)
+{
+ LOG(("III AsyncWait [this=%x]\n", this));
+
+ nsPipeEvents pipeEvents;
+ {
+ nsAutoMonitor mon(mPipe->mMonitor);
+
+ // replace a pending callback
+ mCallback = 0;
+ mCallbackFlags = 0;
+
+ nsCOMPtr<nsIInputStreamCallback> proxy;
+ if (target) {
+ nsresult rv = NS_NewInputStreamReadyEvent(getter_AddRefs(proxy),
+ callback, target);
+ if (NS_FAILED(rv)) return rv;
+ callback = proxy;
+ }
+
+ if (NS_FAILED(mPipe->mStatus) ||
+ (mAvailable && !(flags & WAIT_CLOSURE_ONLY))) {
+ // stream is already closed or readable; post event.
+ pipeEvents.NotifyInputReady(this, callback);
+ }
+ else {
+ // queue up callback object to be notified when data becomes available
+ mCallback = callback;
+ mCallbackFlags = flags;
+ }
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsPipeInputStream::Seek(PRInt32 whence, PRInt64 offset)
+{
+ NS_NOTREACHED("nsPipeInputStream::Seek");
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsPipeInputStream::Tell(PRInt64 *offset)
+{
+ *offset = mLogicalOffset;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsPipeInputStream::SetEOF()
+{
+ NS_NOTREACHED("nsPipeInputStream::SetEOF");
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+#define COMPARE(s1, s2, i) \
+ (ignoreCase \
+ ? nsCRT::strncasecmp((const char *)s1, (const char *)s2, (PRUint32)i) \
+ : nsCRT::strncmp((const char *)s1, (const char *)s2, (PRUint32)i))
+
+NS_IMETHODIMP
+nsPipeInputStream::Search(const char *forString,
+ PRBool ignoreCase,
+ PRBool *found,
+ PRUint32 *offsetSearchedTo)
+{
+ LOG(("III Search [for=%s ic=%u]\n", forString, ignoreCase));
+
+ nsAutoMonitor mon(mPipe->mMonitor);
+
+ char *cursor1, *limit1;
+ PRUint32 index = 0, offset = 0;
+ PRUint32 strLen = strlen(forString);
+
+ mPipe->PeekSegment(0, cursor1, limit1);
+ if (cursor1 == limit1) {
+ *found = PR_FALSE;
+ *offsetSearchedTo = 0;
+ LOG((" result [found=%u offset=%u]\n", *found, *offsetSearchedTo));
+ return NS_OK;
+ }
+
+ while (PR_TRUE) {
+ PRUint32 i, len1 = limit1 - cursor1;
+
+ // check if the string is in the buffer segment
+ for (i = 0; i < len1 - strLen + 1; i++) {
+ if (COMPARE(&cursor1[i], forString, strLen) == 0) {
+ *found = PR_TRUE;
+ *offsetSearchedTo = offset + i;
+ LOG((" result [found=%u offset=%u]\n", *found, *offsetSearchedTo));
+ return NS_OK;
+ }
+ }
+
+ // get the next segment
+ char *cursor2, *limit2;
+ PRUint32 len2;
+
+ index++;
+ offset += len1;
+
+ mPipe->PeekSegment(index, cursor2, limit2);
+ if (cursor2 == limit2) {
+ *found = PR_FALSE;
+ *offsetSearchedTo = offset - strLen + 1;
+ LOG((" result [found=%u offset=%u]\n", *found, *offsetSearchedTo));
+ return NS_OK;
+ }
+ len2 = limit2 - cursor2;
+
+ // check if the string is straddling the next buffer segment
+ PRUint32 lim = PR_MIN(strLen, len2 + 1);
+ for (i = 0; i < lim; ++i) {
+ PRUint32 strPart1Len = strLen - i - 1;
+ PRUint32 strPart2Len = strLen - strPart1Len;
+ const char* strPart2 = &forString[strLen - strPart2Len];
+ PRUint32 bufSeg1Offset = len1 - strPart1Len;
+ if (COMPARE(&cursor1[bufSeg1Offset], forString, strPart1Len) == 0 &&
+ COMPARE(cursor2, strPart2, strPart2Len) == 0) {
+ *found = PR_TRUE;
+ *offsetSearchedTo = offset - strPart1Len;
+ LOG((" result [found=%u offset=%u]\n", *found, *offsetSearchedTo));
+ return NS_OK;
+ }
+ }
+
+ // finally continue with the next buffer
+ cursor1 = cursor2;
+ limit1 = limit2;
+ }
+
+ NS_NOTREACHED("can't get here");
+ return NS_ERROR_UNEXPECTED; // keep compiler happy
+}
+
+//-----------------------------------------------------------------------------
+// nsPipeOutputStream methods:
+//-----------------------------------------------------------------------------
+
+nsresult
+nsPipeOutputStream::Wait()
+{
+ NS_ASSERTION(mBlocking, "wait on non-blocking pipe output stream");
+
+ nsAutoMonitor mon(mPipe->mMonitor);
+
+ if (NS_SUCCEEDED(mPipe->mStatus) && !mWritable) {
+ LOG(("OOO pipe output: waiting for space\n"));
+ mBlocked = PR_TRUE;
+ mon.Wait();
+ mBlocked = PR_FALSE;
+ LOG(("OOO pipe output: woke up [pipe-status=%x writable=%u]\n",
+ mPipe->mStatus, mWritable == PR_TRUE));
+ }
+
+ return mPipe->mStatus == NS_BASE_STREAM_CLOSED ? NS_OK : mPipe->mStatus;
+}
+
+PRBool
+nsPipeOutputStream::OnOutputWritable(nsPipeEvents &events)
+{
+ PRBool result = PR_FALSE;
+
+ mWritable = PR_TRUE;
+
+ if (mCallback && !(mCallbackFlags & WAIT_CLOSURE_ONLY)) {
+ events.NotifyOutputReady(this, mCallback);
+ mCallback = 0;
+ mCallbackFlags = 0;
+ }
+ else if (mBlocked)
+ result = PR_TRUE;
+
+ return result;
+}
+
+PRBool
+nsPipeOutputStream::OnOutputException(nsresult reason, nsPipeEvents &events)
+{
+ LOG(("nsPipeOutputStream::OnOutputException [this=%x reason=%x]\n",
+ this, reason));
+
+ nsresult result = PR_FALSE;
+
+ NS_ASSERTION(NS_FAILED(reason), "huh? successful exception");
+ mWritable = PR_FALSE;
+
+ if (mCallback) {
+ events.NotifyOutputReady(this, mCallback);
+ mCallback = 0;
+ mCallbackFlags = 0;
+ }
+ else if (mBlocked)
+ result = PR_TRUE;
+
+ return result;
+}
+
+
+NS_IMETHODIMP_(nsrefcnt)
+nsPipeOutputStream::AddRef()
+{
+ mWriterRefCnt++;
+ return mPipe->AddRef();
+}
+
+NS_IMETHODIMP_(nsrefcnt)
+nsPipeOutputStream::Release()
+{
+ if (--mWriterRefCnt == 0)
+ Close();
+ return mPipe->Release();
+}
+
+NS_IMPL_QUERY_INTERFACE2(nsPipeOutputStream,
+ nsIOutputStream,
+ nsIAsyncOutputStream)
+
+NS_IMETHODIMP
+nsPipeOutputStream::CloseWithStatus(nsresult reason)
+{
+ LOG(("OOO CloseWithStatus [this=%x reason=%x]\n", this, reason));
+
+ if (NS_SUCCEEDED(reason))
+ reason = NS_BASE_STREAM_CLOSED;
+
+ // input stream may remain open
+ mPipe->OnPipeException(reason, PR_TRUE);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsPipeOutputStream::Close()
+{
+ return CloseWithStatus(NS_BASE_STREAM_CLOSED);
+}
+
+NS_IMETHODIMP
+nsPipeOutputStream::WriteSegments(nsReadSegmentFun reader,
+ void* closure,
+ PRUint32 count,
+ PRUint32 *writeCount)
+{
+ LOG(("OOO WriteSegments [this=%x count=%u]\n", this, count));
+
+ nsresult rv = NS_OK;
+
+ char *segment;
+ PRUint32 segmentLen;
+
+ *writeCount = 0;
+ while (count) {
+ rv = mPipe->GetWriteSegment(segment, segmentLen);
+ if (NS_FAILED(rv)) {
+ if (rv == NS_BASE_STREAM_WOULD_BLOCK) {
+ // pipe is full
+ if (!mBlocking) {
+ // ignore this error if we've already written something
+ if (*writeCount > 0)
+ rv = NS_OK;
+ break;
+ }
+ // wait for the pipe to have an empty segment.
+ rv = Wait();
+ if (NS_SUCCEEDED(rv))
+ continue;
+ }
+ mPipe->OnPipeException(rv);
+ break;
+ }
+
+ // write no more than count
+ if (segmentLen > count)
+ segmentLen = count;
+
+ PRUint32 readCount, originalLen = segmentLen;
+ while (segmentLen) {
+ readCount = 0;
+
+ rv = reader(this, closure, segment, *writeCount, segmentLen, &readCount);
+
+ if (NS_FAILED(rv) || readCount == 0) {
+ count = 0;
+ // any errors returned from the reader end here: do not
+ // propogate to the caller of WriteSegments.
+ rv = NS_OK;
+ break;
+ }
+
+ NS_ASSERTION(readCount <= segmentLen, "read more than expected");
+ segment += readCount;
+ segmentLen -= readCount;
+ count -= readCount;
+ *writeCount += readCount;
+ mLogicalOffset += readCount;
+ }
+
+ if (segmentLen < originalLen)
+ mPipe->AdvanceWriteCursor(originalLen - segmentLen);
+ }
+
+ return rv;
+}
+
+static NS_METHOD
+nsReadFromRawBuffer(nsIOutputStream* outStr,
+ void* closure,
+ char* toRawSegment,
+ PRUint32 offset,
+ PRUint32 count,
+ PRUint32 *readCount)
+{
+ const char* fromBuf = (const char*)closure;
+ memcpy(toRawSegment, &fromBuf[offset], count);
+ *readCount = count;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsPipeOutputStream::Write(const char* fromBuf,
+ PRUint32 bufLen,
+ PRUint32 *writeCount)
+{
+ return WriteSegments(nsReadFromRawBuffer, (void*)fromBuf, bufLen, writeCount);
+}
+
+NS_IMETHODIMP
+nsPipeOutputStream::Flush(void)
+{
+ // nothing to do
+ return NS_OK;
+}
+
+static NS_METHOD
+nsReadFromInputStream(nsIOutputStream* outStr,
+ void* closure,
+ char* toRawSegment,
+ PRUint32 offset,
+ PRUint32 count,
+ PRUint32 *readCount)
+{
+ nsIInputStream* fromStream = (nsIInputStream*)closure;
+ return fromStream->Read(toRawSegment, count, readCount);
+}
+
+NS_IMETHODIMP
+nsPipeOutputStream::WriteFrom(nsIInputStream* fromStream,
+ PRUint32 count,
+ PRUint32 *writeCount)
+{
+ return WriteSegments(nsReadFromInputStream, fromStream, count, writeCount);
+}
+
+NS_IMETHODIMP
+nsPipeOutputStream::IsNonBlocking(PRBool *aNonBlocking)
+{
+ *aNonBlocking = !mBlocking;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsPipeOutputStream::AsyncWait(nsIOutputStreamCallback *callback,
+ PRUint32 flags,
+ PRUint32 requestedCount,
+ nsIEventTarget *target)
+{
+ LOG(("OOO AsyncWait [this=%x]\n", this));
+
+ nsPipeEvents pipeEvents;
+ {
+ nsAutoMonitor mon(mPipe->mMonitor);
+
+ // replace a pending callback
+ mCallback = 0;
+ mCallbackFlags = 0;
+
+ nsCOMPtr<nsIOutputStreamCallback> proxy;
+ if (target) {
+ nsresult rv = NS_NewOutputStreamReadyEvent(getter_AddRefs(proxy),
+ callback, target);
+ if (NS_FAILED(rv)) return rv;
+ callback = proxy;
+ }
+
+ if (NS_FAILED(mPipe->mStatus) ||
+ (mWritable && !(flags & WAIT_CLOSURE_ONLY))) {
+ // stream is already closed or writable; post event.
+ pipeEvents.NotifyOutputReady(this, callback);
+ }
+ else {
+ // queue up callback object to be notified when data becomes available
+ mCallback = callback;
+ mCallbackFlags = flags;
+ }
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsPipeOutputStream::Seek(PRInt32 whence, PRInt64 offset)
+{
+ NS_NOTREACHED("nsPipeOutputStream::Seek");
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsPipeOutputStream::Tell(PRInt64 *offset)
+{
+ *offset = mLogicalOffset;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsPipeOutputStream::SetEOF()
+{
+ NS_NOTREACHED("nsPipeOutputStream::SetEOF");
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+NS_COM nsresult
+NS_NewPipe2(nsIAsyncInputStream **pipeIn,
+ nsIAsyncOutputStream **pipeOut,
+ PRBool nonBlockingInput,
+ PRBool nonBlockingOutput,
+ PRUint32 segmentSize,
+ PRUint32 segmentCount,
+ nsIMemory *segmentAlloc)
+{
+ nsresult rv;
+
+#if defined(PR_LOGGING)
+ if (!gPipeLog)
+ gPipeLog = PR_NewLogModule("nsPipe");
+#endif
+
+ nsPipe *pipe = new nsPipe();
+ if (!pipe)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ rv = pipe->Init(nonBlockingInput,
+ nonBlockingOutput,
+ segmentSize,
+ segmentCount,
+ segmentAlloc);
+ if (NS_FAILED(rv)) {
+ NS_ADDREF(pipe);
+ NS_RELEASE(pipe);
+ return rv;
+ }
+
+ pipe->GetInputStream(pipeIn);
+ pipe->GetOutputStream(pipeOut);
+ return NS_OK;
+}
+
+////////////////////////////////////////////////////////////////////////////////
diff --git a/src/libs/xpcom18a4/xpcom/io/nsScriptableInputStream.cpp b/src/libs/xpcom18a4/xpcom/io/nsScriptableInputStream.cpp
new file mode 100644
index 00000000..5458b1b7
--- /dev/null
+++ b/src/libs/xpcom18a4/xpcom/io/nsScriptableInputStream.cpp
@@ -0,0 +1,102 @@
+/* -*- 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.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 ***** */
+
+#include "nsScriptableInputStream.h"
+#include "nsMemory.h"
+
+NS_IMPL_ISUPPORTS1(nsScriptableInputStream, nsIScriptableInputStream)
+
+// nsIBaseStream methods
+NS_IMETHODIMP
+nsScriptableInputStream::Close(void) {
+ if (!mInputStream) return NS_ERROR_NOT_INITIALIZED;
+ return mInputStream->Close();
+}
+
+// nsIScriptableInputStream methods
+NS_IMETHODIMP
+nsScriptableInputStream::Init(nsIInputStream *aInputStream) {
+ if (!aInputStream) return NS_ERROR_NULL_POINTER;
+ mInputStream = aInputStream;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsScriptableInputStream::Available(PRUint32 *_retval) {
+ if (!mInputStream) return NS_ERROR_NOT_INITIALIZED;
+ return mInputStream->Available(_retval);
+}
+
+NS_IMETHODIMP
+nsScriptableInputStream::Read(PRUint32 aCount, char **_retval) {
+ nsresult rv = NS_OK;
+ PRUint32 count = 0;
+ char *buffer = nsnull;
+
+ if (!mInputStream) return NS_ERROR_NOT_INITIALIZED;
+
+ rv = mInputStream->Available(&count);
+ if (NS_FAILED(rv)) return rv;
+
+ count = PR_MIN(count, aCount);
+ buffer = (char*)nsMemory::Alloc(count+1); // make room for '\0'
+ if (!buffer) return NS_ERROR_OUT_OF_MEMORY;
+
+ PRUint32 amtRead = 0;
+ rv = mInputStream->Read(buffer, count, &amtRead);
+ if (NS_FAILED(rv)) {
+ nsMemory::Free(buffer);
+ return rv;
+ }
+
+ buffer[amtRead] = '\0';
+ *_retval = buffer;
+ return NS_OK;
+}
+
+NS_METHOD
+nsScriptableInputStream::Create(nsISupports *aOuter, REFNSIID aIID, void **aResult) {
+ if (aOuter) return NS_ERROR_NO_AGGREGATION;
+
+ nsScriptableInputStream *sis = new nsScriptableInputStream();
+ if (!sis) return NS_ERROR_OUT_OF_MEMORY;
+
+ NS_ADDREF(sis);
+ nsresult rv = sis->QueryInterface(aIID, aResult);
+ NS_RELEASE(sis);
+ return rv;
+}
diff --git a/src/libs/xpcom18a4/xpcom/io/nsScriptableInputStream.h b/src/libs/xpcom18a4/xpcom/io/nsScriptableInputStream.h
new file mode 100644
index 00000000..b9976176
--- /dev/null
+++ b/src/libs/xpcom18a4/xpcom/io/nsScriptableInputStream.h
@@ -0,0 +1,71 @@
+/* -*- Mode: IDL; 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.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 ***** */
+
+#ifndef ___nsscriptableinputstream___h_
+#define ___nsscriptableinputstream___h_
+
+#include "nsIScriptableInputStream.h"
+#include "nsIInputStream.h"
+#include "nsCOMPtr.h"
+
+#define NS_SCRIPTABLEINPUTSTREAM_CID \
+{ 0x7225c040, 0xa9bf, 0x11d3, { 0xa1, 0x97, 0x0, 0x50, 0x4, 0x1c, 0xaf, 0x44 } }
+
+#define NS_SCRIPTABLEINPUTSTREAM_CONTRACTID "@mozilla.org/scriptableinputstream;1"
+#define NS_SCRIPTABLEINPUTSTREAM_CLASSNAME "Scriptable Input Stream"
+
+class nsScriptableInputStream : public nsIScriptableInputStream {
+public:
+ // nsISupports methods
+ NS_DECL_ISUPPORTS
+
+ // nsIScriptableInputStream methods
+ NS_DECL_NSISCRIPTABLEINPUTSTREAM
+
+ // nsScriptableInputStream methods
+ nsScriptableInputStream() {};
+
+ static NS_METHOD
+ Create(nsISupports *aOuter, REFNSIID aIID, void **aResult);
+
+private:
+ ~nsScriptableInputStream() {};
+
+ nsCOMPtr<nsIInputStream> mInputStream;
+};
+
+#endif // ___nsscriptableinputstream___h_
diff --git a/src/libs/xpcom18a4/xpcom/io/nsSegmentedBuffer.cpp b/src/libs/xpcom18a4/xpcom/io/nsSegmentedBuffer.cpp
new file mode 100644
index 00000000..f6a1c15d
--- /dev/null
+++ b/src/libs/xpcom18a4/xpcom/io/nsSegmentedBuffer.cpp
@@ -0,0 +1,207 @@
+/* -*- Mode: C++; tab-width: 2; 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.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 ***** */
+
+#include "nsSegmentedBuffer.h"
+#include "nsCRT.h"
+
+nsresult
+nsSegmentedBuffer::Init(PRUint32 segmentSize, PRUint32 maxSize,
+ nsIMemory* allocator)
+{
+ if (mSegmentArrayCount != 0)
+ return NS_ERROR_FAILURE; // initialized more than once
+ mSegmentSize = segmentSize;
+ mMaxSize = maxSize;
+ mSegAllocator = allocator;
+ if (mSegAllocator == nsnull) {
+ mSegAllocator = nsMemory::GetGlobalMemoryService();
+ }
+ else {
+ NS_ADDREF(mSegAllocator);
+ }
+#if 0 // testing...
+ mSegmentArrayCount = 2;
+#else
+ mSegmentArrayCount = NS_SEGMENTARRAY_INITIAL_COUNT;
+#endif
+ return NS_OK;
+}
+
+char*
+nsSegmentedBuffer::AppendNewSegment()
+{
+ if (GetSize() >= mMaxSize)
+ return nsnull;
+
+ if (mSegmentArray == nsnull) {
+ PRUint32 bytes = mSegmentArrayCount * sizeof(char*);
+ mSegmentArray = (char**)nsMemory::Alloc(bytes);
+ if (mSegmentArray == nsnull)
+ return nsnull;
+ memset(mSegmentArray, 0, bytes);
+ }
+
+ if (IsFull()) {
+ PRUint32 newArraySize = mSegmentArrayCount * 2;
+ PRUint32 bytes = newArraySize * sizeof(char*);
+ char** newSegArray = (char**)nsMemory::Realloc(mSegmentArray, bytes);
+ if (newSegArray == nsnull)
+ return nsnull;
+ mSegmentArray = newSegArray;
+ // copy wrapped content to new extension
+ if (mFirstSegmentIndex > mLastSegmentIndex) {
+ // deal with wrap around case
+ memcpy(&mSegmentArray[mSegmentArrayCount],
+ mSegmentArray,
+ mLastSegmentIndex * sizeof(char*));
+ memset(mSegmentArray, 0, mLastSegmentIndex * sizeof(char*));
+ mLastSegmentIndex += mSegmentArrayCount;
+ memset(&mSegmentArray[mLastSegmentIndex], 0,
+ (newArraySize - mLastSegmentIndex) * sizeof(char*));
+ }
+ else {
+ memset(&mSegmentArray[mLastSegmentIndex], 0,
+ (newArraySize - mLastSegmentIndex) * sizeof(char*));
+ }
+ mSegmentArrayCount = newArraySize;
+ }
+
+ char* seg = (char*)mSegAllocator->Alloc(mSegmentSize);
+ if (seg == nsnull) {
+ return nsnull;
+ }
+ mSegmentArray[mLastSegmentIndex] = seg;
+ mLastSegmentIndex = ModSegArraySize(mLastSegmentIndex + 1);
+ return seg;
+}
+
+PRBool
+nsSegmentedBuffer::DeleteFirstSegment()
+{
+ NS_ASSERTION(mSegmentArray[mFirstSegmentIndex] != nsnull, "deleting bad segment");
+ (void)mSegAllocator->Free(mSegmentArray[mFirstSegmentIndex]);
+ mSegmentArray[mFirstSegmentIndex] = nsnull;
+ PRInt32 last = ModSegArraySize(mLastSegmentIndex - 1);
+ if (mFirstSegmentIndex == last) {
+ mLastSegmentIndex = last;
+ return PR_TRUE;
+ }
+ else {
+ mFirstSegmentIndex = ModSegArraySize(mFirstSegmentIndex + 1);
+ return PR_FALSE;
+ }
+}
+
+PRBool
+nsSegmentedBuffer::DeleteLastSegment()
+{
+ PRInt32 last = ModSegArraySize(mLastSegmentIndex - 1);
+ NS_ASSERTION(mSegmentArray[last] != nsnull, "deleting bad segment");
+ (void)mSegAllocator->Free(mSegmentArray[last]);
+ mSegmentArray[last] = nsnull;
+ mLastSegmentIndex = last;
+ return (PRBool)(mLastSegmentIndex == mFirstSegmentIndex);
+}
+
+PRBool
+nsSegmentedBuffer::ReallocLastSegment(size_t newSize)
+{
+ PRInt32 last = ModSegArraySize(mLastSegmentIndex - 1);
+ NS_ASSERTION(mSegmentArray[last] != nsnull, "realloc'ing bad segment");
+ char *newSegment =
+ (char*)mSegAllocator->Realloc(mSegmentArray[last], newSize);
+ if (newSegment) {
+ mSegmentArray[last] = newSegment;
+ return PR_TRUE;
+ } else {
+ return PR_FALSE;
+ }
+}
+
+void
+nsSegmentedBuffer::Empty()
+{
+ if (mSegmentArray) {
+ for (PRUint32 i = 0; i < mSegmentArrayCount; i++) {
+ if (mSegmentArray[i])
+ mSegAllocator->Free(mSegmentArray[i]);
+ }
+ nsMemory::Free(mSegmentArray);
+ mSegmentArray = nsnull;
+ }
+ mSegmentArrayCount = NS_SEGMENTARRAY_INITIAL_COUNT;
+ mFirstSegmentIndex = mLastSegmentIndex = 0;
+}
+
+#ifdef DEBUG
+NS_COM void
+TestSegmentedBuffer()
+{
+ nsSegmentedBuffer* buf = new nsSegmentedBuffer();
+ NS_ASSERTION(buf, "out of memory");
+ buf->Init(4, 16);
+ char* seg;
+ PRBool empty;
+ seg = buf->AppendNewSegment();
+ NS_ASSERTION(seg, "AppendNewSegment failed");
+ seg = buf->AppendNewSegment();
+ NS_ASSERTION(seg, "AppendNewSegment failed");
+ seg = buf->AppendNewSegment();
+ NS_ASSERTION(seg, "AppendNewSegment failed");
+ empty = buf->DeleteFirstSegment();
+ NS_ASSERTION(!empty, "DeleteFirstSegment failed");
+ empty = buf->DeleteFirstSegment();
+ NS_ASSERTION(!empty, "DeleteFirstSegment failed");
+ seg = buf->AppendNewSegment();
+ NS_ASSERTION(seg, "AppendNewSegment failed");
+ seg = buf->AppendNewSegment();
+ NS_ASSERTION(seg, "AppendNewSegment failed");
+ seg = buf->AppendNewSegment();
+ NS_ASSERTION(seg, "AppendNewSegment failed");
+ empty = buf->DeleteFirstSegment();
+ NS_ASSERTION(!empty, "DeleteFirstSegment failed");
+ empty = buf->DeleteFirstSegment();
+ NS_ASSERTION(!empty, "DeleteFirstSegment failed");
+ empty = buf->DeleteFirstSegment();
+ NS_ASSERTION(!empty, "DeleteFirstSegment failed");
+ empty = buf->DeleteFirstSegment();
+ NS_ASSERTION(empty, "DeleteFirstSegment failed");
+ delete buf;
+}
+#endif
+
+////////////////////////////////////////////////////////////////////////////////
diff --git a/src/libs/xpcom18a4/xpcom/io/nsSegmentedBuffer.h b/src/libs/xpcom18a4/xpcom/io/nsSegmentedBuffer.h
new file mode 100644
index 00000000..43620632
--- /dev/null
+++ b/src/libs/xpcom18a4/xpcom/io/nsSegmentedBuffer.h
@@ -0,0 +1,125 @@
+/* -*- Mode: C++; tab-width: 2; 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.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 ***** */
+
+#ifndef nsSegmentedBuffer_h__
+#define nsSegmentedBuffer_h__
+
+#include "nsMemory.h"
+#include "prclist.h"
+
+class nsSegmentedBuffer
+{
+public:
+ nsSegmentedBuffer()
+ : mSegmentSize(0), mMaxSize(0),
+ mSegAllocator(nsnull), mSegmentArray(nsnull),
+ mSegmentArrayCount(0),
+ mFirstSegmentIndex(0), mLastSegmentIndex(0) {}
+
+ ~nsSegmentedBuffer() {
+ Empty();
+ NS_IF_RELEASE(mSegAllocator);
+ }
+
+
+ NS_COM nsresult Init(PRUint32 segmentSize, PRUint32 maxSize,
+ nsIMemory* allocator = nsnull);
+
+ NS_COM char* AppendNewSegment(); // pushes at end
+
+ // returns true if no more segments remain:
+ PRBool DeleteFirstSegment(); // pops from beginning
+
+ // returns true if no more segments remain:
+ PRBool DeleteLastSegment(); // pops from beginning
+
+ // Call Realloc() on last segment. This is used to reduce memory
+ // consumption when data is not an exact multiple of segment size.
+ PRBool ReallocLastSegment(size_t newSize);
+
+ NS_COM void Empty(); // frees all segments
+
+ inline PRUint32 GetSegmentCount() {
+ if (mFirstSegmentIndex <= mLastSegmentIndex)
+ return mLastSegmentIndex - mFirstSegmentIndex;
+ else
+ return mSegmentArrayCount + mLastSegmentIndex - mFirstSegmentIndex;
+ }
+
+ inline PRUint32 GetSegmentSize() { return mSegmentSize; }
+ inline PRUint32 GetMaxSize() { return mMaxSize; }
+ inline PRUint32 GetSize() { return GetSegmentCount() * mSegmentSize; }
+
+ inline char* GetSegment(PRUint32 indx) {
+ NS_ASSERTION(indx < GetSegmentCount(), "index out of bounds");
+ PRInt32 i = ModSegArraySize(mFirstSegmentIndex + (PRInt32)indx);
+ return mSegmentArray[i];
+ }
+
+protected:
+ inline PRInt32 ModSegArraySize(PRInt32 n) {
+ PRUint32 result = n & (mSegmentArrayCount - 1);
+ NS_ASSERTION(result == n % mSegmentArrayCount,
+ "non-power-of-2 mSegmentArrayCount");
+ return result;
+ }
+
+ inline PRBool IsFull() {
+ return ModSegArraySize(mLastSegmentIndex + 1) == mFirstSegmentIndex;
+ }
+
+protected:
+ PRUint32 mSegmentSize;
+ PRUint32 mMaxSize;
+ nsIMemory* mSegAllocator;
+ char** mSegmentArray;
+ PRUint32 mSegmentArrayCount;
+ PRInt32 mFirstSegmentIndex;
+ PRInt32 mLastSegmentIndex;
+};
+
+// NS_SEGMENTARRAY_INITIAL_SIZE: This number needs to start out as a
+// power of 2 given how it gets used. We double the segment array
+// when we overflow it, and use that fact that it's a power of 2
+// to compute a fast modulus operation in IsFull.
+//
+// 32 segment array entries can accommodate 128k of data if segments
+// are 4k in size. That seems like a reasonable amount that will avoid
+// needing to grow the segment array.
+#define NS_SEGMENTARRAY_INITIAL_COUNT 32
+
+#endif // nsSegmentedBuffer_h__
diff --git a/src/libs/xpcom18a4/xpcom/io/nsStorageStream.cpp b/src/libs/xpcom18a4/xpcom/io/nsStorageStream.cpp
new file mode 100644
index 00000000..2a6b0166
--- /dev/null
+++ b/src/libs/xpcom18a4/xpcom/io/nsStorageStream.cpp
@@ -0,0 +1,563 @@
+/* -*- 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, released
+ * March 31, 1998.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1998-1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Pierre Phaneuf <pp@ludusdesign.com>
+ *
+ * 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 ***** */
+
+/*
+ * The storage stream provides an internal buffer that can be filled by a
+ * client using a single output stream. One or more independent input streams
+ * can be created to read the data out non-destructively. The implementation
+ * uses a segmented buffer internally to avoid realloc'ing of large buffers,
+ * with the attendant performance loss and heap fragmentation.
+ */
+
+#include "nsStorageStream.h"
+#include "nsSegmentedBuffer.h"
+#include "nsCOMPtr.h"
+#include "prbit.h"
+#include "nsIInputStream.h"
+#include "nsISeekableStream.h"
+#include "prlog.h"
+#include "nsInt64.h"
+#if defined(PR_LOGGING)
+//
+// Log module for StorageStream logging...
+//
+// To enable logging (see prlog.h for full details):
+//
+// set NSPR_LOG_MODULES=StorageStreamLog:5
+// set NSPR_LOG_FILE=nspr.log
+//
+// this enables PR_LOG_DEBUG level information and places all output in
+// the file nspr.log
+//
+PRLogModuleInfo* StorageStreamLog = nsnull;
+
+#endif /* PR_LOGGING */
+
+nsStorageStream::nsStorageStream()
+ : mSegmentedBuffer(0), mSegmentSize(0), mWriteInProgress(PR_FALSE),
+ mLastSegmentNum(-1), mWriteCursor(0), mSegmentEnd(0), mLogicalLength(0)
+{
+#if defined(PR_LOGGING)
+ //
+ // Initialize the global PRLogModule for socket transport logging
+ // if necessary...
+ //
+ if (nsnull == StorageStreamLog) {
+ StorageStreamLog = PR_NewLogModule("StorageStreamLog");
+ }
+#endif /* PR_LOGGING */
+
+ PR_LOG(StorageStreamLog, PR_LOG_DEBUG,
+ ("Creating nsStorageStream [%x].\n", this));
+}
+
+nsStorageStream::~nsStorageStream()
+{
+ if (mSegmentedBuffer)
+ delete mSegmentedBuffer;
+}
+
+NS_IMPL_THREADSAFE_ISUPPORTS2(nsStorageStream,
+ nsIStorageStream,
+ nsIOutputStream)
+
+NS_IMETHODIMP
+nsStorageStream::Init(PRUint32 segmentSize, PRUint32 maxSize,
+ nsIMemory *segmentAllocator)
+{
+ mSegmentedBuffer = new nsSegmentedBuffer();
+ if (!mSegmentedBuffer)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ mSegmentSize = segmentSize;
+ mSegmentSizeLog2 = PR_FloorLog2(segmentSize);
+
+ // Segment size must be a power of two
+ if (mSegmentSize != ((PRUint32)1 << mSegmentSizeLog2))
+ return NS_ERROR_INVALID_ARG;
+
+ return mSegmentedBuffer->Init(segmentSize, maxSize, segmentAllocator);
+}
+
+NS_IMETHODIMP
+nsStorageStream::GetOutputStream(PRInt32 aStartingOffset,
+ nsIOutputStream * *aOutputStream)
+{
+ NS_ENSURE_ARG(aOutputStream);
+ if (mWriteInProgress)
+ return NS_ERROR_NOT_AVAILABLE;
+
+ nsresult rv = Seek(aStartingOffset);
+ if (NS_FAILED(rv)) return rv;
+
+ // Enlarge the last segment in the buffer so that it is the same size as
+ // all the other segments in the buffer. (It may have been realloc'ed
+ // smaller in the Close() method.)
+ if (mLastSegmentNum >= 0)
+ mSegmentedBuffer->ReallocLastSegment(mSegmentSize);
+
+ // Need to re-Seek, since realloc might have changed segment base pointer
+ rv = Seek(aStartingOffset);
+ if (NS_FAILED(rv)) return rv;
+
+ NS_ADDREF(this);
+ *aOutputStream = NS_STATIC_CAST(nsIOutputStream*, this);
+ mWriteInProgress = PR_TRUE;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsStorageStream::Close()
+{
+ mWriteInProgress = PR_FALSE;
+
+ PRInt32 segmentOffset = SegOffset(mLogicalLength);
+
+ // Shrink the final segment in the segmented buffer to the minimum size
+ // needed to contain the data, so as to conserve memory.
+ if (segmentOffset)
+ mSegmentedBuffer->ReallocLastSegment(segmentOffset);
+
+ mWriteCursor = 0;
+ mSegmentEnd = 0;
+
+ PR_LOG(StorageStreamLog, PR_LOG_DEBUG,
+ ("nsStorageStream [%x] Close mWriteCursor=%x mSegmentEnd=%x\n",
+ this, mWriteCursor, mSegmentEnd));
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsStorageStream::Flush()
+{
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsStorageStream::Write(const char *aBuffer, PRUint32 aCount, PRUint32 *aNumWritten)
+{
+ const char* readCursor;
+ PRUint32 count, availableInSegment, remaining;
+ nsresult rv = NS_OK;
+
+ NS_ENSURE_ARG_POINTER(aNumWritten);
+ NS_ENSURE_ARG(aBuffer);
+
+ PR_LOG(StorageStreamLog, PR_LOG_DEBUG,
+ ("nsStorageStream [%x] Write mWriteCursor=%x mSegmentEnd=%x aCount=%d\n",
+ this, mWriteCursor, mSegmentEnd, aCount));
+
+ remaining = aCount;
+ readCursor = aBuffer;
+ while (remaining) {
+ availableInSegment = mSegmentEnd - mWriteCursor;
+ if (!availableInSegment) {
+ mWriteCursor = mSegmentedBuffer->AppendNewSegment();
+ if (!mWriteCursor) {
+ mSegmentEnd = 0;
+ rv = NS_ERROR_OUT_OF_MEMORY;
+ goto out;
+ }
+ mLastSegmentNum++;
+ mSegmentEnd = mWriteCursor + mSegmentSize;
+ availableInSegment = mSegmentEnd - mWriteCursor;
+ PR_LOG(StorageStreamLog, PR_LOG_DEBUG,
+ ("nsStorageStream [%x] Write (new seg) mWriteCursor=%x mSegmentEnd=%x\n",
+ this, mWriteCursor, mSegmentEnd));
+ }
+
+ count = PR_MIN(availableInSegment, remaining);
+ memcpy(mWriteCursor, readCursor, count);
+ remaining -= count;
+ readCursor += count;
+ mWriteCursor += count;
+ PR_LOG(StorageStreamLog, PR_LOG_DEBUG,
+ ("nsStorageStream [%x] Writing mWriteCursor=%x mSegmentEnd=%x count=%d\n",
+ this, mWriteCursor, mSegmentEnd, count));
+ };
+
+ out:
+ *aNumWritten = aCount - remaining;
+ mLogicalLength += *aNumWritten;
+
+ PR_LOG(StorageStreamLog, PR_LOG_DEBUG,
+ ("nsStorageStream [%x] Wrote mWriteCursor=%x mSegmentEnd=%x numWritten=%d\n",
+ this, mWriteCursor, mSegmentEnd, *aNumWritten));
+ return rv;
+}
+
+NS_IMETHODIMP
+nsStorageStream::WriteFrom(nsIInputStream *inStr, PRUint32 count, PRUint32 *_retval)
+{
+ NS_NOTREACHED("WriteFrom");
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsStorageStream::WriteSegments(nsReadSegmentFun reader, void * closure, PRUint32 count, PRUint32 *_retval)
+{
+ NS_NOTREACHED("WriteSegments");
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsStorageStream::IsNonBlocking(PRBool *aNonBlocking)
+{
+ *aNonBlocking = PR_TRUE;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsStorageStream::GetLength(PRUint32 *aLength)
+{
+ NS_ENSURE_ARG(aLength);
+ *aLength = mLogicalLength;
+ return NS_OK;
+}
+
+// Truncate the buffer by deleting the end segments
+NS_IMETHODIMP
+nsStorageStream::SetLength(PRUint32 aLength)
+{
+ if (mWriteInProgress)
+ return NS_ERROR_NOT_AVAILABLE;
+
+ if (aLength > mLogicalLength)
+ return NS_ERROR_INVALID_ARG;
+
+ PRInt32 newLastSegmentNum = SegNum(aLength);
+ PRInt32 segmentOffset = SegOffset(aLength);
+ if (segmentOffset == 0)
+ newLastSegmentNum--;
+
+ while (newLastSegmentNum < mLastSegmentNum) {
+ mSegmentedBuffer->DeleteLastSegment();
+ mLastSegmentNum--;
+ }
+
+ mLogicalLength = aLength;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsStorageStream::GetWriteInProgress(PRBool *aWriteInProgress)
+{
+ NS_ENSURE_ARG(aWriteInProgress);
+
+ *aWriteInProgress = mWriteInProgress;
+ return NS_OK;
+}
+
+NS_METHOD
+nsStorageStream::Seek(PRInt32 aPosition)
+{
+ // An argument of -1 means "seek to end of stream"
+ if (aPosition == -1)
+ aPosition = mLogicalLength;
+
+ // Seeking beyond the buffer end is illegal
+ if ((PRUint32)aPosition > mLogicalLength)
+ return NS_ERROR_INVALID_ARG;
+
+ // Seeking backwards in the write stream results in truncation
+ SetLength(aPosition);
+
+ // Special handling for seek to start-of-buffer
+ if (aPosition == 0) {
+ mWriteCursor = 0;
+ mSegmentEnd = 0;
+ PR_LOG(StorageStreamLog, PR_LOG_DEBUG,
+ ("nsStorageStream [%x] Seek mWriteCursor=%x mSegmentEnd=%x\n",
+ this, mWriteCursor, mSegmentEnd));
+ return NS_OK;
+ }
+
+ // Segment may have changed, so reset pointers
+ mWriteCursor = mSegmentedBuffer->GetSegment(mLastSegmentNum);
+ NS_ASSERTION(mWriteCursor, "null mWriteCursor");
+ mSegmentEnd = mWriteCursor + mSegmentSize;
+ PRInt32 segmentOffset = SegOffset(aPosition);
+ mWriteCursor += segmentOffset;
+
+ PR_LOG(StorageStreamLog, PR_LOG_DEBUG,
+ ("nsStorageStream [%x] Seek mWriteCursor=%x mSegmentEnd=%x\n",
+ this, mWriteCursor, mSegmentEnd));
+ return NS_OK;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+// There can be many nsStorageInputStreams for a single nsStorageStream
+class nsStorageInputStream : public nsIInputStream
+ , public nsISeekableStream
+{
+public:
+ nsStorageInputStream(nsStorageStream *aStorageStream, PRUint32 aSegmentSize)
+ : mStorageStream(aStorageStream), mReadCursor(0),
+ mSegmentEnd(0), mSegmentNum(0),
+ mSegmentSize(aSegmentSize), mLogicalCursor(0)
+ {
+ NS_ADDREF(mStorageStream);
+ }
+
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIINPUTSTREAM
+ NS_DECL_NSISEEKABLESTREAM
+
+private:
+ ~nsStorageInputStream()
+ {
+ NS_IF_RELEASE(mStorageStream);
+ }
+
+protected:
+ NS_METHOD Seek(PRUint32 aPosition);
+
+ friend class nsStorageStream;
+
+private:
+ nsStorageStream* mStorageStream;
+ const char* mReadCursor; // Next memory location to read byte, or NULL
+ const char* mSegmentEnd; // One byte past end of current buffer segment
+ PRUint32 mSegmentNum; // Segment number containing read cursor
+ PRUint32 mSegmentSize; // All segments, except the last, are of this size
+ PRUint32 mLogicalCursor; // Logical offset into stream
+
+ PRUint32 SegNum(PRUint32 aPosition) {return aPosition >> mStorageStream->mSegmentSizeLog2;}
+ PRUint32 SegOffset(PRUint32 aPosition) {return aPosition & (mSegmentSize - 1);}
+};
+
+NS_IMPL_THREADSAFE_ISUPPORTS2(nsStorageInputStream,
+ nsIInputStream,
+ nsISeekableStream)
+
+NS_IMETHODIMP
+nsStorageStream::NewInputStream(PRInt32 aStartingOffset, nsIInputStream* *aInputStream)
+{
+ nsStorageInputStream *inputStream = new nsStorageInputStream(this, mSegmentSize);
+ if (!inputStream)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ NS_ADDREF(inputStream);
+
+ nsresult rv = inputStream->Seek(aStartingOffset);
+ if (NS_FAILED(rv)) {
+ NS_RELEASE(inputStream);
+ return rv;
+ }
+
+ *aInputStream = inputStream;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsStorageInputStream::Close()
+{
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsStorageInputStream::Available(PRUint32 *aAvailable)
+{
+ *aAvailable = mStorageStream->mLogicalLength - mLogicalCursor;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsStorageInputStream::Read(char* aBuffer, PRUint32 aCount, PRUint32 *aNumRead)
+{
+ char* writeCursor;
+ PRUint32 count, availableInSegment, remainingCapacity;
+
+ remainingCapacity = aCount;
+ writeCursor = aBuffer;
+ while (remainingCapacity) {
+ availableInSegment = mSegmentEnd - mReadCursor;
+ if (!availableInSegment) {
+ PRUint32 available = mStorageStream->mLogicalLength - mLogicalCursor;
+ if (!available)
+ goto out;
+
+ mReadCursor = mStorageStream->mSegmentedBuffer->GetSegment(++mSegmentNum);
+ mSegmentEnd = mReadCursor + PR_MIN(mSegmentSize, available);
+ }
+
+ count = PR_MIN(availableInSegment, remainingCapacity);
+ memcpy(writeCursor, mReadCursor, count);
+ remainingCapacity -= count;
+ mReadCursor += count;
+ writeCursor += count;
+ mLogicalCursor += count;
+ };
+
+ out:
+ *aNumRead = aCount - remainingCapacity;
+
+ PRBool isWriteInProgress = PR_FALSE;
+ if (NS_FAILED(mStorageStream->GetWriteInProgress(&isWriteInProgress)))
+ isWriteInProgress = PR_FALSE;
+
+ if (*aNumRead == 0 && isWriteInProgress)
+ return NS_BASE_STREAM_WOULD_BLOCK;
+ else
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsStorageInputStream::ReadSegments(nsWriteSegmentFun writer, void * closure, PRUint32 aCount, PRUint32 *aNumRead)
+{
+ PRUint32 count, availableInSegment, remainingCapacity, bytesConsumed;
+ nsresult rv;
+
+ remainingCapacity = aCount;
+ while (remainingCapacity) {
+ availableInSegment = mSegmentEnd - mReadCursor;
+ if (!availableInSegment) {
+ PRUint32 available = mStorageStream->mLogicalLength - mLogicalCursor;
+ if (!available)
+ goto out;
+
+ mReadCursor = mStorageStream->mSegmentedBuffer->GetSegment(++mSegmentNum);
+ mSegmentEnd = mReadCursor + PR_MIN(mSegmentSize, available);
+ availableInSegment = mSegmentEnd - mReadCursor;
+ }
+
+ count = PR_MIN(availableInSegment, remainingCapacity);
+ rv = writer(this, closure, mReadCursor, mLogicalCursor, count, &bytesConsumed);
+ if (NS_FAILED(rv) || (bytesConsumed == 0))
+ break;
+ remainingCapacity -= bytesConsumed;
+ mReadCursor += bytesConsumed;
+ mLogicalCursor += bytesConsumed;
+ };
+
+ out:
+ *aNumRead = aCount - remainingCapacity;
+
+ PRBool isWriteInProgress = PR_FALSE;
+ if (NS_FAILED(mStorageStream->GetWriteInProgress(&isWriteInProgress)))
+ isWriteInProgress = PR_FALSE;
+
+ if (*aNumRead == 0 && isWriteInProgress)
+ return NS_BASE_STREAM_WOULD_BLOCK;
+ else
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsStorageInputStream::IsNonBlocking(PRBool *aNonBlocking)
+{
+ *aNonBlocking = PR_TRUE;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsStorageInputStream::Seek(PRInt32 aWhence, PRInt64 aOffset)
+{
+ nsInt64 pos = aOffset;
+
+ switch (aWhence) {
+ case NS_SEEK_SET:
+ break;
+ case NS_SEEK_CUR:
+ pos += mLogicalCursor;
+ break;
+ case NS_SEEK_END:
+ pos += mStorageStream->mLogicalLength;
+ break;
+ default:
+ NS_NOTREACHED("unexpected whence value");
+ return NS_ERROR_UNEXPECTED;
+ }
+ nsInt64 logicalCursor(mLogicalCursor);
+ if (pos == logicalCursor)
+ return NS_OK;
+
+ return Seek(pos);
+}
+
+NS_IMETHODIMP
+nsStorageInputStream::Tell(PRInt64 *aResult)
+{
+ LL_UI2L(*aResult, mLogicalCursor);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsStorageInputStream::SetEOF()
+{
+ NS_NOTREACHED("nsStorageInputStream::SetEOF");
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_METHOD
+nsStorageInputStream::Seek(PRUint32 aPosition)
+{
+ PRUint32 length = mStorageStream->mLogicalLength;
+ if (aPosition >= length)
+ return NS_ERROR_INVALID_ARG;
+
+ mSegmentNum = SegNum(aPosition);
+ PRUint32 segmentOffset = SegOffset(aPosition);
+ mReadCursor = mStorageStream->mSegmentedBuffer->GetSegment(mSegmentNum) +
+ segmentOffset;
+ PRUint32 available = length - aPosition;
+ mSegmentEnd = mReadCursor + PR_MIN(mSegmentSize - segmentOffset, available);
+ mLogicalCursor = aPosition;
+ return NS_OK;
+}
+
+NS_COM nsresult
+NS_NewStorageStream(PRUint32 segmentSize, PRUint32 maxSize, nsIStorageStream **result)
+{
+ NS_ENSURE_ARG(result);
+
+ nsStorageStream* storageStream = new nsStorageStream();
+ if (!storageStream) return NS_ERROR_OUT_OF_MEMORY;
+
+ NS_ADDREF(storageStream);
+ nsresult rv = storageStream->Init(segmentSize, maxSize, nsnull);
+ if (NS_FAILED(rv)) {
+ NS_RELEASE(storageStream);
+ return rv;
+ }
+ *result = storageStream;
+ return NS_OK;
+}
diff --git a/src/libs/xpcom18a4/xpcom/io/nsStorageStream.h b/src/libs/xpcom18a4/xpcom/io/nsStorageStream.h
new file mode 100644
index 00000000..0729c01c
--- /dev/null
+++ b/src/libs/xpcom18a4/xpcom/io/nsStorageStream.h
@@ -0,0 +1,98 @@
+/* -*- 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, released
+ * March 31, 1998.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1998-1999
+ * 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 ***** */
+
+/*
+ * The storage stream provides an internal buffer that can be filled by a
+ * client using a single output stream. One or more independent input streams
+ * can be created to read the data out non-destructively. The implementation
+ * uses a segmented buffer internally to avoid realloc'ing of large buffers,
+ * with the attendant performance loss and heap fragmentation.
+ */
+
+#ifndef _nsStorageStream_h_
+#define _nsStorageStream_h_
+
+#include "nsIStorageStream.h"
+#include "nsIOutputStream.h"
+#include "nsMemory.h"
+
+#define NS_STORAGESTREAM_CID \
+{ /* 669a9795-6ff7-4ed4-9150-c34ce2971b63 */ \
+ 0x669a9795, \
+ 0x6ff7, \
+ 0x4ed4, \
+ {0x91, 0x50, 0xc3, 0x4c, 0xe2, 0x97, 0x1b, 0x63} \
+}
+
+#define NS_STORAGESTREAM_CONTRACTID "@mozilla.org/storagestream;1"
+#define NS_STORAGESTREAM_CLASSNAME "Storage Stream"
+
+class nsSegmentedBuffer;
+
+class nsStorageStream : public nsIStorageStream,
+ public nsIOutputStream
+{
+public:
+ nsStorageStream();
+
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSISTORAGESTREAM
+ NS_DECL_NSIOUTPUTSTREAM
+
+ friend class nsStorageInputStream;
+
+private:
+ ~nsStorageStream();
+
+ nsSegmentedBuffer* mSegmentedBuffer;
+ PRUint32 mSegmentSize; // All segments, except possibly the last, are of this size
+ // Must be power-of-2
+ PRUint32 mSegmentSizeLog2; // log2(mSegmentSize)
+ PRBool mWriteInProgress; // true, if an un-Close'ed output stream exists
+ PRInt32 mLastSegmentNum; // Last segment # in use, -1 initially
+ char* mWriteCursor; // Pointer to next byte to be written
+ char* mSegmentEnd; // Pointer to one byte after end of segment
+ // containing the write cursor
+ PRUint32 mLogicalLength; // Number of bytes written to stream
+
+ NS_METHOD Seek(PRInt32 aPosition);
+ PRUint32 SegNum(PRUint32 aPosition) {return aPosition >> mSegmentSizeLog2;}
+ PRUint32 SegOffset(PRUint32 aPosition) {return aPosition & (mSegmentSize - 1);}
+};
+
+#endif // _nsStorageStream_h_
diff --git a/src/libs/xpcom18a4/xpcom/io/nsStreamUtils.cpp b/src/libs/xpcom18a4/xpcom/io/nsStreamUtils.cpp
new file mode 100644
index 00000000..aeb52b2a
--- /dev/null
+++ b/src/libs/xpcom18a4/xpcom/io/nsStreamUtils.cpp
@@ -0,0 +1,583 @@
+/* ***** 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.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2002
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Darin Fisher <darin@netscape.com>
+ *
+ * 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 "nsStreamUtils.h"
+#include "nsCOMPtr.h"
+#include "nsIPipe.h"
+#include "nsIEventTarget.h"
+#include "nsAutoLock.h"
+
+//-----------------------------------------------------------------------------
+
+class nsInputStreamReadyEvent : public PLEvent
+ , public nsIInputStreamCallback
+{
+public:
+ NS_DECL_ISUPPORTS
+
+ nsInputStreamReadyEvent(nsIInputStreamCallback *callback,
+ nsIEventTarget *target)
+ : mCallback(callback)
+ , mTarget(target)
+ {
+ }
+
+private:
+ ~nsInputStreamReadyEvent()
+ {
+ if (mCallback) {
+ nsresult rv;
+ //
+ // whoa!! looks like we never posted this event. take care to
+ // release mCallback on the correct thread. if mTarget lives on the
+ // calling thread, then we are ok. otherwise, we have to try to
+ // proxy the Release over the right thread. if that thread is dead,
+ // then there's nothing we can do... better to leak than crash.
+ //
+ PRBool val;
+ rv = mTarget->IsOnCurrentThread(&val);
+ if (NS_FAILED(rv) || !val) {
+ nsCOMPtr<nsIInputStreamCallback> event;
+ NS_NewInputStreamReadyEvent(getter_AddRefs(event), mCallback, mTarget);
+ mCallback = 0;
+ if (event) {
+ rv = event->OnInputStreamReady(nsnull);
+ if (NS_FAILED(rv)) {
+ NS_NOTREACHED("leaking stream event");
+ nsISupports *sup = event;
+ NS_ADDREF(sup);
+ }
+ }
+ }
+ }
+ }
+
+public:
+ NS_IMETHOD OnInputStreamReady(nsIAsyncInputStream *stream)
+ {
+ mStream = stream;
+
+ // will be released when event is handled
+ NS_ADDREF_THIS();
+
+ PL_InitEvent(this, nsnull, EventHandler, EventCleanup);
+
+ if (NS_FAILED(mTarget->PostEvent(this))) {
+ NS_WARNING("PostEvent failed");
+ NS_RELEASE_THIS();
+ return NS_ERROR_FAILURE;
+ }
+
+ return NS_OK;
+ }
+
+private:
+ nsCOMPtr<nsIAsyncInputStream> mStream;
+ nsCOMPtr<nsIInputStreamCallback> mCallback;
+ nsCOMPtr<nsIEventTarget> mTarget;
+
+ PR_STATIC_CALLBACK(void *) EventHandler(PLEvent *plevent)
+ {
+ nsInputStreamReadyEvent *ev = (nsInputStreamReadyEvent *) plevent;
+ // bypass event delivery if this is a cleanup event...
+ if (ev->mCallback)
+ ev->mCallback->OnInputStreamReady(ev->mStream);
+ ev->mCallback = 0;
+ return NULL;
+ }
+
+ PR_STATIC_CALLBACK(void) EventCleanup(PLEvent *plevent)
+ {
+ nsInputStreamReadyEvent *ev = (nsInputStreamReadyEvent *) plevent;
+ NS_RELEASE(ev);
+ }
+};
+
+NS_IMPL_THREADSAFE_ISUPPORTS1(nsInputStreamReadyEvent,
+ nsIInputStreamCallback)
+
+//-----------------------------------------------------------------------------
+
+class nsOutputStreamReadyEvent : public PLEvent
+ , public nsIOutputStreamCallback
+{
+public:
+ NS_DECL_ISUPPORTS
+
+ nsOutputStreamReadyEvent(nsIOutputStreamCallback *callback,
+ nsIEventTarget *target)
+ : mCallback(callback)
+ , mTarget(target)
+ {
+ }
+
+private:
+ ~nsOutputStreamReadyEvent()
+ {
+ if (mCallback) {
+ nsresult rv;
+ //
+ // whoa!! looks like we never posted this event. take care to
+ // release mCallback on the correct thread. if mTarget lives on the
+ // calling thread, then we are ok. otherwise, we have to try to
+ // proxy the Release over the right thread. if that thread is dead,
+ // then there's nothing we can do... better to leak than crash.
+ //
+ PRBool val;
+ rv = mTarget->IsOnCurrentThread(&val);
+ if (NS_FAILED(rv) || !val) {
+ nsCOMPtr<nsIOutputStreamCallback> event;
+ NS_NewOutputStreamReadyEvent(getter_AddRefs(event), mCallback, mTarget);
+ mCallback = 0;
+ if (event) {
+ rv = event->OnOutputStreamReady(nsnull);
+ if (NS_FAILED(rv)) {
+ NS_NOTREACHED("leaking stream event");
+ nsISupports *sup = event;
+ NS_ADDREF(sup);
+ }
+ }
+ }
+ }
+ }
+
+public:
+ void Init(nsIOutputStreamCallback *callback, nsIEventTarget *target)
+ {
+ mCallback = callback;
+ mTarget = target;
+
+ PL_InitEvent(this, nsnull, EventHandler, EventCleanup);
+ }
+
+ NS_IMETHOD OnOutputStreamReady(nsIAsyncOutputStream *stream)
+ {
+ mStream = stream;
+
+ // this will be released when the event is handled
+ NS_ADDREF_THIS();
+
+ PL_InitEvent(this, nsnull, EventHandler, EventCleanup);
+
+ if (NS_FAILED(mTarget->PostEvent(this))) {
+ NS_WARNING("PostEvent failed");
+ NS_RELEASE_THIS();
+ return NS_ERROR_FAILURE;
+ }
+
+ return NS_OK;
+ }
+
+private:
+ nsCOMPtr<nsIAsyncOutputStream> mStream;
+ nsCOMPtr<nsIOutputStreamCallback> mCallback;
+ nsCOMPtr<nsIEventTarget> mTarget;
+
+ PR_STATIC_CALLBACK(void *) EventHandler(PLEvent *plevent)
+ {
+ nsOutputStreamReadyEvent *ev = (nsOutputStreamReadyEvent *) plevent;
+ if (ev->mCallback)
+ ev->mCallback->OnOutputStreamReady(ev->mStream);
+ ev->mCallback = 0;
+ return NULL;
+ }
+
+ PR_STATIC_CALLBACK(void) EventCleanup(PLEvent *ev)
+ {
+ nsOutputStreamReadyEvent *event = (nsOutputStreamReadyEvent *) ev;
+ NS_RELEASE(event);
+ }
+};
+
+NS_IMPL_THREADSAFE_ISUPPORTS1(nsOutputStreamReadyEvent,
+ nsIOutputStreamCallback)
+
+//-----------------------------------------------------------------------------
+
+NS_COM nsresult
+NS_NewInputStreamReadyEvent(nsIInputStreamCallback **event,
+ nsIInputStreamCallback *callback,
+ nsIEventTarget *target)
+{
+ nsInputStreamReadyEvent *ev = new nsInputStreamReadyEvent(callback, target);
+ if (!ev)
+ return NS_ERROR_OUT_OF_MEMORY;
+ NS_ADDREF(*event = ev);
+ return NS_OK;
+}
+
+NS_COM nsresult
+NS_NewOutputStreamReadyEvent(nsIOutputStreamCallback **event,
+ nsIOutputStreamCallback *callback,
+ nsIEventTarget *target)
+{
+ nsOutputStreamReadyEvent *ev = new nsOutputStreamReadyEvent(callback, target);
+ if (!ev)
+ return NS_ERROR_OUT_OF_MEMORY;
+ NS_ADDREF(*event = ev);
+ return NS_OK;
+}
+
+//-----------------------------------------------------------------------------
+// NS_AsyncCopy implementation
+
+// abstract stream copier...
+class nsAStreamCopier : public nsIInputStreamCallback
+ , public nsIOutputStreamCallback
+{
+public:
+ NS_DECL_ISUPPORTS
+
+ nsAStreamCopier()
+ : mLock(nsnull)
+ , mCallback(nsnull)
+ , mClosure(nsnull)
+ , mChunkSize(0)
+ , mEventInProcess(PR_FALSE)
+ , mEventIsPending(PR_FALSE)
+ {
+ }
+
+ // virtual since subclasses call superclass Release()
+ virtual ~nsAStreamCopier()
+ {
+ if (mLock)
+ PR_DestroyLock(mLock);
+ }
+
+ // kick off the async copy...
+ nsresult Start(nsIInputStream *source,
+ nsIOutputStream *sink,
+ nsIEventTarget *target,
+ nsAsyncCopyCallbackFun callback,
+ void *closure,
+ PRUint32 chunksize)
+ {
+ mSource = source;
+ mSink = sink;
+ mTarget = target;
+ mCallback = callback;
+ mClosure = closure;
+ mChunkSize = chunksize;
+
+ mLock = PR_NewLock();
+ if (!mLock)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ mAsyncSource = do_QueryInterface(mSource);
+ mAsyncSink = do_QueryInterface(mSink);
+
+ return PostContinuationEvent();
+ }
+
+ // implemented by subclasses, returns number of bytes copied and
+ // sets source and sink condition before returning.
+ virtual PRUint32 DoCopy(nsresult *sourceCondition, nsresult *sinkCondition) = 0;
+
+ void Process()
+ {
+ if (!mSource || !mSink)
+ return;
+
+ nsresult sourceCondition, sinkCondition;
+
+ // ok, copy data from source to sink.
+ for (;;) {
+ PRUint32 n = DoCopy(&sourceCondition, &sinkCondition);
+ if (NS_FAILED(sourceCondition) || NS_FAILED(sinkCondition) || n == 0) {
+ if (sourceCondition == NS_BASE_STREAM_WOULD_BLOCK && mAsyncSource) {
+ // need to wait for more data from source. while waiting for
+ // more source data, be sure to observe failures on output end.
+ mAsyncSource->AsyncWait(this, 0, 0, nsnull);
+
+ if (mAsyncSink)
+ mAsyncSink->AsyncWait(this,
+ nsIAsyncOutputStream::WAIT_CLOSURE_ONLY,
+ 0, nsnull);
+ }
+ else if (sinkCondition == NS_BASE_STREAM_WOULD_BLOCK && mAsyncSink) {
+ // need to wait for more room in the sink. while waiting for
+ // more room in the sink, be sure to observer failures on the
+ // input end.
+ mAsyncSink->AsyncWait(this, 0, 0, nsnull);
+
+ if (mAsyncSource)
+ mAsyncSource->AsyncWait(this,
+ nsIAsyncInputStream::WAIT_CLOSURE_ONLY,
+ 0, nsnull);
+ }
+ else {
+ // close source
+ if (mAsyncSource)
+ mAsyncSource->CloseWithStatus(sinkCondition);
+ else
+ mSource->Close();
+ mAsyncSource = nsnull;
+ mSource = nsnull;
+
+ // close sink
+ if (mAsyncSink)
+ mAsyncSink->CloseWithStatus(sourceCondition);
+ else
+ mSink->Close();
+ mAsyncSink = nsnull;
+ mSink = nsnull;
+
+ // notify state complete...
+ if (mCallback) {
+ nsresult status = sourceCondition;
+ if (NS_SUCCEEDED(status))
+ status = sinkCondition;
+ if (status == NS_BASE_STREAM_CLOSED)
+ status = NS_OK;
+ mCallback(mClosure, status);
+ }
+ }
+ break;
+ }
+ }
+ }
+
+ NS_IMETHOD OnInputStreamReady(nsIAsyncInputStream *source)
+ {
+ PostContinuationEvent();
+ return NS_OK;
+ }
+
+ NS_IMETHOD OnOutputStreamReady(nsIAsyncOutputStream *sink)
+ {
+ PostContinuationEvent();
+ return NS_OK;
+ }
+
+ PR_STATIC_CALLBACK(void*) HandleContinuationEvent(PLEvent *event)
+ {
+ nsAStreamCopier *self = (nsAStreamCopier *) event->owner;
+ self->Process();
+
+ // clear "in process" flag and post any pending continuation event
+ nsAutoLock lock(self->mLock);
+ self->mEventInProcess = PR_FALSE;
+ if (self->mEventIsPending) {
+ self->mEventIsPending = PR_FALSE;
+ self->PostContinuationEvent_Locked();
+ }
+ return nsnull;
+ }
+
+ PR_STATIC_CALLBACK(void) DestroyContinuationEvent(PLEvent *event)
+ {
+ nsAStreamCopier *self = (nsAStreamCopier *) event->owner;
+ NS_RELEASE(self);
+ delete event;
+ }
+
+ nsresult PostContinuationEvent()
+ {
+ // we cannot post a continuation event if there is currently
+ // an event in process. doing so could result in Process being
+ // run simultaneously on multiple threads, so we mark the event
+ // as pending, and if an event is already in process then we
+ // just let that existing event take care of posting the real
+ // continuation event.
+
+ nsAutoLock lock(mLock);
+ return PostContinuationEvent_Locked();
+ }
+
+ nsresult PostContinuationEvent_Locked()
+ {
+ nsresult rv = NS_OK;
+ if (mEventInProcess)
+ mEventIsPending = PR_TRUE;
+ else {
+ PLEvent *event = new PLEvent;
+ if (!event)
+ rv = NS_ERROR_OUT_OF_MEMORY;
+ else {
+ NS_ADDREF_THIS();
+ PL_InitEvent(event, this,
+ HandleContinuationEvent,
+ DestroyContinuationEvent);
+
+ rv = mTarget->PostEvent(event);
+ if (NS_SUCCEEDED(rv))
+ mEventInProcess = PR_TRUE;
+ else {
+ NS_ERROR("unable to post continuation event");
+ PL_DestroyEvent(event);
+ }
+ }
+ }
+ return rv;
+ }
+
+protected:
+ nsCOMPtr<nsIInputStream> mSource;
+ nsCOMPtr<nsIOutputStream> mSink;
+ nsCOMPtr<nsIAsyncInputStream> mAsyncSource;
+ nsCOMPtr<nsIAsyncOutputStream> mAsyncSink;
+ nsCOMPtr<nsIEventTarget> mTarget;
+ PRLock *mLock;
+ nsAsyncCopyCallbackFun mCallback;
+ void *mClosure;
+ PRUint32 mChunkSize;
+ PRPackedBool mEventInProcess;
+ PRPackedBool mEventIsPending;
+};
+
+NS_IMPL_THREADSAFE_ISUPPORTS2(nsAStreamCopier,
+ nsIInputStreamCallback,
+ nsIOutputStreamCallback)
+
+class nsStreamCopierIB : public nsAStreamCopier
+{
+public:
+ nsStreamCopierIB() : nsAStreamCopier() {}
+ virtual ~nsStreamCopierIB() {}
+
+ struct ReadSegmentsState {
+ nsIOutputStream *mSink;
+ nsresult mSinkCondition;
+ };
+
+ static NS_METHOD ConsumeInputBuffer(nsIInputStream *inStr,
+ void *closure,
+ const char *buffer,
+ PRUint32 offset,
+ PRUint32 count,
+ PRUint32 *countWritten)
+ {
+ ReadSegmentsState *state = (ReadSegmentsState *) closure;
+
+ nsresult rv = state->mSink->Write(buffer, count, countWritten);
+ if (NS_FAILED(rv))
+ state->mSinkCondition = rv;
+ else if (*countWritten == 0)
+ state->mSinkCondition = NS_BASE_STREAM_CLOSED;
+
+ return state->mSinkCondition;
+ }
+
+ PRUint32 DoCopy(nsresult *sourceCondition, nsresult *sinkCondition)
+ {
+ ReadSegmentsState state;
+ state.mSink = mSink;
+ state.mSinkCondition = NS_OK;
+
+ PRUint32 n;
+ *sourceCondition =
+ mSource->ReadSegments(ConsumeInputBuffer, &state, mChunkSize, &n);
+ *sinkCondition = state.mSinkCondition;
+ return n;
+ }
+};
+
+class nsStreamCopierOB : public nsAStreamCopier
+{
+public:
+ nsStreamCopierOB() : nsAStreamCopier() {}
+ virtual ~nsStreamCopierOB() {}
+
+ struct WriteSegmentsState {
+ nsIInputStream *mSource;
+ nsresult mSourceCondition;
+ };
+
+ static NS_METHOD FillOutputBuffer(nsIOutputStream *outStr,
+ void *closure,
+ char *buffer,
+ PRUint32 offset,
+ PRUint32 count,
+ PRUint32 *countRead)
+ {
+ WriteSegmentsState *state = (WriteSegmentsState *) closure;
+
+ nsresult rv = state->mSource->Read(buffer, count, countRead);
+ if (NS_FAILED(rv))
+ state->mSourceCondition = rv;
+ else if (*countRead == 0)
+ state->mSourceCondition = NS_BASE_STREAM_CLOSED;
+
+ return state->mSourceCondition;
+ }
+
+ PRUint32 DoCopy(nsresult *sourceCondition, nsresult *sinkCondition)
+ {
+ WriteSegmentsState state;
+ state.mSource = mSource;
+ state.mSourceCondition = NS_OK;
+
+ PRUint32 n;
+ *sinkCondition =
+ mSink->WriteSegments(FillOutputBuffer, &state, mChunkSize, &n);
+ *sourceCondition = state.mSourceCondition;
+ return n;
+ }
+};
+
+//-----------------------------------------------------------------------------
+
+NS_COM nsresult
+NS_AsyncCopy(nsIInputStream *source,
+ nsIOutputStream *sink,
+ nsIEventTarget *target,
+ nsAsyncCopyMode mode,
+ PRUint32 chunkSize,
+ nsAsyncCopyCallbackFun callback,
+ void *closure)
+{
+ NS_ASSERTION(target, "non-null target required");
+
+ nsresult rv;
+ nsAStreamCopier *copier;
+
+ if (mode == NS_ASYNCCOPY_VIA_READSEGMENTS)
+ copier = new nsStreamCopierIB();
+ else
+ copier = new nsStreamCopierOB();
+
+ if (!copier)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ // Start() takes an owning ref to the copier...
+ NS_ADDREF(copier);
+ rv = copier->Start(source, sink, target, callback, closure, chunkSize);
+ NS_RELEASE(copier);
+
+ return rv;
+}
diff --git a/src/libs/xpcom18a4/xpcom/io/nsStreamUtils.h b/src/libs/xpcom18a4/xpcom/io/nsStreamUtils.h
new file mode 100644
index 00000000..3d33f1c1
--- /dev/null
+++ b/src/libs/xpcom18a4/xpcom/io/nsStreamUtils.h
@@ -0,0 +1,111 @@
+/* ***** 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.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2002
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Darin Fisher <darin@netscape.com>
+ *
+ * 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 nsStreamUtils_h__
+#define nsStreamUtils_h__
+
+#include "nscore.h"
+
+class nsIInputStream;
+class nsIOutputStream;
+class nsIInputStreamCallback;
+class nsIOutputStreamCallback;
+class nsIEventTarget;
+
+/**
+ * A "one-shot" proxy of the OnInputStreamReady callback. The resulting
+ * proxy object's OnInputStreamReady function may only be called once! The
+ * proxy object ensures that the real notify object will be free'd on the
+ * thread corresponding to the given event target regardless of what thread
+ * the proxy object is destroyed on.
+ *
+ * This function is designed to be used to implement AsyncWait when the
+ * aEventTarget parameter is non-null.
+ */
+extern NS_COM nsresult
+NS_NewInputStreamReadyEvent(nsIInputStreamCallback **aEvent,
+ nsIInputStreamCallback *aNotify,
+ nsIEventTarget *aEventTarget);
+
+/**
+ * A "one-shot" proxy of the OnOutputStreamReady callback. The resulting
+ * proxy object's OnOutputStreamReady function may only be called once! The
+ * proxy object ensures that the real notify object will be free'd on the
+ * thread corresponding to the given event target regardless of what thread
+ * the proxy object is destroyed on.
+ *
+ * This function is designed to be used to implement AsyncWait when the
+ * aEventTarget parameter is non-null.
+ */
+extern NS_COM nsresult
+NS_NewOutputStreamReadyEvent(nsIOutputStreamCallback **aEvent,
+ nsIOutputStreamCallback *aNotify,
+ nsIEventTarget *aEventTarget);
+
+/* ------------------------------------------------------------------------- */
+
+enum nsAsyncCopyMode {
+ NS_ASYNCCOPY_VIA_READSEGMENTS,
+ NS_ASYNCCOPY_VIA_WRITESEGMENTS
+};
+
+/**
+ * This function is called when the async copy process completes. The reported
+ * status is NS_OK on success and some error code on failure.
+ */
+typedef void (* nsAsyncCopyCallbackFun)(void *closure, nsresult status);
+
+/**
+ * This function asynchronously copies data from the source to the sink. All
+ * data transfer occurs on the thread corresponding to the given event target.
+ * A null event target is not permitted.
+ *
+ * The copier handles blocking or non-blocking streams transparently. If a
+ * stream operation returns NS_BASE_STREAM_WOULD_BLOCK, then the stream will
+ * be QI'd to nsIAsync{In,Out}putStream and its AsyncWait method will be used
+ * to determine when to resume copying.
+ */
+extern NS_COM nsresult
+NS_AsyncCopy(nsIInputStream *aSource,
+ nsIOutputStream *aSink,
+ nsIEventTarget *aEventTarget,
+ nsAsyncCopyMode aMode = NS_ASYNCCOPY_VIA_READSEGMENTS,
+ PRUint32 aChunkSize = 4096,
+ nsAsyncCopyCallbackFun aCallbackFun = nsnull,
+ void *aCallbackClosure = nsnull);
+
+#endif // !nsStreamUtils_h__
diff --git a/src/libs/xpcom18a4/xpcom/io/nsStringIO.h b/src/libs/xpcom18a4/xpcom/io/nsStringIO.h
new file mode 100644
index 00000000..beca1d19
--- /dev/null
+++ b/src/libs/xpcom18a4/xpcom/io/nsStringIO.h
@@ -0,0 +1,86 @@
+/* -*- Mode: C++; tab-width: 2; 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):
+ * Scott Collins <scc@mozilla.org>
+ *
+ * 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 ***** */
+
+#ifndef nsStringIO_h___
+#define nsStringIO_h___
+
+#include "nsAString.h"
+#include <stdio.h>
+
+
+template <class CharT>
+class nsFileCharSink
+ {
+ public:
+ typedef CharT value_type;
+
+ public:
+ nsFileCharSink( FILE* aOutputFile ) : mOutputFile(aOutputFile) { }
+
+ PRUint32
+ write( const value_type* s, PRUint32 n )
+ {
+ return fwrite(s, sizeof(CharT), n, mOutputFile);
+ }
+
+ private:
+ FILE* mOutputFile;
+ };
+
+
+template <class CharT>
+inline
+void
+fprint_string( FILE* aFile, const basic_nsAString<CharT>& aString )
+ {
+ nsReadingIterator<CharT> fromBegin, fromEnd;
+ nsFileCharSink<CharT> toBegin(aFile);
+ copy_string(aString.BeginReading(fromBegin), aString.EndReading(fromEnd), toBegin);
+ }
+
+
+template <class CharT>
+inline
+void
+print_string( const basic_nsAString<CharT>& aString )
+ {
+ fprint_string(stdout, aString);
+ }
+
+
+#endif // !defined(nsStringIO_h___)
diff --git a/src/libs/xpcom18a4/xpcom/io/nsStringStream.cpp b/src/libs/xpcom18a4/xpcom/io/nsStringStream.cpp
new file mode 100644
index 00000000..536eab11
--- /dev/null
+++ b/src/libs/xpcom18a4/xpcom/io/nsStringStream.cpp
@@ -0,0 +1,457 @@
+/* -*- 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.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):
+ * mcmullen@netscape.com (original author)
+ * warren@netscape.com
+ * alecf@netscape.com
+ * scc@mozilla.org
+ * david.gardiner@unisa.edu.au
+ * fur@netscape.com
+ * norris@netscape.com
+ * pinkerton@netscape.com
+ * davidm@netscape.com
+ * sfraser@netscape.com
+ * darin@netscape.com
+ * bzbarsky@mit.edu
+ *
+ * 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 ***** */
+
+/**
+ * Based on original code from nsIStringStream.cpp
+ */
+
+#include "nsStringStream.h"
+
+#include "prerror.h"
+#include "plstr.h"
+#include "nsReadableUtils.h"
+#include "nsCRT.h"
+#include "nsISeekableStream.h"
+#include "nsInt64.h"
+
+#define NS_FILE_RESULT(x) ns_file_convert_result((PRInt32)x)
+#define NS_FILE_FAILURE NS_FILE_RESULT(-1)
+
+static nsresult ns_file_convert_result(PRInt32 nativeErr)
+{
+ return nativeErr ?
+ NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_FILES,((nativeErr)&0xFFFF))
+ : NS_OK;
+}
+
+//-----------------------------------------------------------------------------
+// nsIStringInputStream implementation
+//-----------------------------------------------------------------------------
+
+class nsStringInputStream : public nsIStringInputStream
+ , public nsIRandomAccessStore
+
+{
+public:
+ nsStringInputStream()
+ : mOffset(0)
+ , mLastResult(NS_OK)
+ , mEOF(PR_FALSE)
+ , mOwned(PR_FALSE)
+ , mConstString(nsnull)
+ , mLength(0)
+ {}
+
+private:
+ ~nsStringInputStream()
+ {
+ if (mOwned)
+ nsMemory::Free((char*)mConstString);
+ }
+
+public:
+ NS_DECL_ISUPPORTS
+
+ NS_DECL_NSISTRINGINPUTSTREAM
+ NS_DECL_NSIINPUTSTREAM
+
+ // nsIRandomAccessStore interface
+ NS_IMETHOD GetAtEOF(PRBool* outAtEOF);
+ NS_IMETHOD SetAtEOF(PRBool inAtEOF);
+
+ NS_DECL_NSISEEKABLESTREAM
+
+protected:
+ PRInt32 LengthRemaining() const
+ {
+ return mLength - mOffset;
+ }
+
+ void Clear()
+ {
+ NS_ASSERTION(mConstString || !mOwned,
+ "Can't have mOwned set and have a null string!");
+ if (mOwned)
+ nsMemory::Free((char*)mConstString);
+
+ // We're about to get a new string; clear the members that
+ // would no longer have valid values.
+ mOffset = 0;
+ mLastResult = NS_OK;
+ mEOF = PR_FALSE;
+ }
+
+ PRInt32 mOffset;
+ nsresult mLastResult;
+ PRPackedBool mEOF;
+ PRPackedBool mOwned;
+ const char* mConstString;
+ PRInt32 mLength;
+};
+
+NS_IMPL_THREADSAFE_ISUPPORTS4(nsStringInputStream,
+ nsIStringInputStream,
+ nsIInputStream,
+ nsIRandomAccessStore,
+ nsISeekableStream)
+
+/////////
+// nsIStringInputStream implementation
+/////////
+NS_IMETHODIMP
+nsStringInputStream::SetData(const char *data, PRInt32 dataLen)
+{
+ if (dataLen < 0)
+ dataLen = strlen(data);
+
+ return AdoptData(nsCRT::strndup(data, dataLen), dataLen);
+}
+
+NS_IMETHODIMP
+nsStringInputStream::AdoptData(char *data, PRInt32 dataLen)
+{
+ NS_ENSURE_ARG_POINTER(data);
+
+ if (dataLen < 0)
+ dataLen = strlen(data);
+
+ Clear();
+
+ mConstString = (const char *) data;
+ mLength = dataLen;
+ mOwned = PR_TRUE;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsStringInputStream::ShareData(const char *data, PRInt32 dataLen)
+{
+ NS_ENSURE_ARG_POINTER(data);
+
+ if (dataLen < 0)
+ dataLen = strlen(data);
+
+ Clear();
+
+ mConstString = data;
+ mLength = dataLen;
+ mOwned = PR_FALSE;
+ return NS_OK;
+}
+
+/////////
+// nsIInputStream implementation
+/////////
+NS_IMETHODIMP nsStringInputStream::Close()
+{
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsStringInputStream::Available(PRUint32 *aLength)
+{
+ NS_PRECONDITION(aLength != nsnull, "null ptr");
+ if (!aLength)
+ return NS_ERROR_NULL_POINTER;
+ *aLength = LengthRemaining();
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsStringInputStream::Read(char* aBuf, PRUint32 aCount,
+ PRUint32 *aReadCount)
+{
+ NS_PRECONDITION(aBuf != nsnull, "null ptr");
+ if (!aBuf)
+ return NS_ERROR_NULL_POINTER;
+ NS_PRECONDITION(aReadCount != nsnull, "null ptr");
+ if (!aReadCount)
+ return NS_ERROR_NULL_POINTER;
+ if (NS_FAILED(mLastResult))
+ return mLastResult;
+
+ PRInt32 bytesRead;
+ PRInt32 maxCount = mLength - mOffset;
+ if ((PRInt32)aCount > maxCount)
+ bytesRead = maxCount;
+ else
+ bytesRead = aCount;
+
+ memcpy(aBuf, mConstString + mOffset, bytesRead);
+ mOffset += bytesRead;
+
+ *aReadCount = bytesRead;
+ if (bytesRead < (PRInt32)aCount)
+ SetAtEOF(PR_TRUE);
+ return NS_OK;
+}
+
+
+NS_IMETHODIMP
+nsStringInputStream::ReadSegments(nsWriteSegmentFun writer, void * closure,
+ PRUint32 aCount, PRUint32 * result)
+{
+ nsresult rv;
+ PRInt32 maxCount = mLength - mOffset;
+ if (maxCount == 0) {
+ *result = 0;
+ return NS_OK;
+ }
+ if ((PRInt32)aCount > maxCount)
+ aCount = maxCount;
+ rv = writer(this, closure, mConstString + mOffset,
+ 0, aCount, result);
+ if (NS_SUCCEEDED(rv))
+ mOffset += *result;
+ // errors returned from the writer end here!
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsStringInputStream::IsNonBlocking(PRBool *aNonBlocking)
+{
+ *aNonBlocking = PR_TRUE;
+ return NS_OK;
+}
+
+
+/////////
+// nsISeekableStream implementation
+/////////
+NS_IMETHODIMP nsStringInputStream::Seek(PRInt32 whence, PRInt64 offset)
+{
+ mLastResult = NS_OK; // reset on a seek.
+ const nsInt64 maxUint32 = PR_UINT32_MAX;
+ nsInt64 offset64(offset);
+ PRInt32 offset32;
+ LL_L2I(offset32, offset);
+
+ NS_ASSERTION(maxUint32 > offset64, "string streams only support 32 bit offsets");
+ mEOF = PR_FALSE; // reset on a seek.
+ PRInt32 fileSize = LengthRemaining();
+ PRInt32 newPosition=-1;
+ switch (whence)
+ {
+ case NS_SEEK_CUR: newPosition = mOffset + offset32; break;
+ case NS_SEEK_SET: newPosition = offset32; break;
+ case NS_SEEK_END: newPosition = fileSize + offset32; break;
+ }
+ if (newPosition < 0)
+ {
+ newPosition = 0;
+ mLastResult = NS_FILE_RESULT(PR_FILE_SEEK_ERROR);
+ }
+ if (newPosition >= fileSize)
+ {
+ newPosition = fileSize;
+ mEOF = PR_TRUE;
+ }
+ mOffset = newPosition;
+ return NS_OK;
+}
+
+
+NS_IMETHODIMP nsStringInputStream::Tell(PRInt64* outWhere)
+{
+ *outWhere = mOffset;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsStringInputStream::SetEOF()
+{
+ NS_NOTYETIMPLEMENTED("nsStringInputStream::SetEOF");
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+/////////
+// nsIRandomAccessStore implementation
+/////////
+NS_IMETHODIMP nsStringInputStream::GetAtEOF(PRBool* outAtEOF)
+{
+ *outAtEOF = mEOF;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsStringInputStream::SetAtEOF(PRBool inAtEOF)
+{
+ mEOF = inAtEOF;
+ return NS_OK;
+}
+
+// Factory method to get an nsInputStream from an nsAString. Result will
+// implement nsIStringInputStream and nsIRandomAccessStore
+extern "C" NS_COM nsresult
+NS_NewStringInputStream(nsIInputStream** aStreamResult,
+ const nsAString& aStringToRead)
+{
+ NS_PRECONDITION(aStreamResult, "null out ptr");
+
+ char* data = ToNewCString(aStringToRead);
+ if (!data)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ nsStringInputStream* stream = new nsStringInputStream();
+ if (! stream) {
+ nsMemory::Free(data);
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ NS_ADDREF(stream);
+
+ nsresult rv = stream->AdoptData(data, aStringToRead.Length());
+ if (NS_FAILED(rv)) {
+ nsMemory::Free(data);
+ NS_RELEASE(stream);
+ return rv;
+ }
+
+ *aStreamResult = stream;
+ return NS_OK;
+}
+
+// Factory method to get an nsInputStream from an nsACString. Result will
+// implement nsIStringInputStream and nsIRandomAccessStore
+extern "C" NS_COM nsresult
+NS_NewCStringInputStream(nsIInputStream** aStreamResult,
+ const nsACString& aStringToRead)
+{
+ NS_PRECONDITION(aStreamResult, "null out ptr");
+
+ char* data = ToNewCString(aStringToRead);
+ if (!data)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ nsStringInputStream* stream = new nsStringInputStream();
+ if (! stream) {
+ nsMemory::Free(data);
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ NS_ADDREF(stream);
+
+ nsresult rv = stream->AdoptData(data, aStringToRead.Length());
+ if (NS_FAILED(rv)) {
+ nsMemory::Free(data);
+ NS_RELEASE(stream);
+ return rv;
+ }
+
+ *aStreamResult = stream;
+ return NS_OK;
+}
+
+// Factory method to get an nsInputStream from a C string. Result will
+// implement nsIStringInputStream and nsIRandomAccessStore
+extern "C" NS_COM nsresult
+NS_NewCharInputStream(nsIInputStream** aStreamResult,
+ const char* aStringToRead)
+{
+ NS_PRECONDITION(aStreamResult, "null out ptr");
+
+ nsStringInputStream* stream = new nsStringInputStream();
+ if (! stream)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ NS_ADDREF(stream);
+
+ nsresult rv = stream->ShareData(aStringToRead, -1);
+
+ if (NS_FAILED(rv)) {
+ NS_RELEASE(stream);
+ return rv;
+ }
+
+ *aStreamResult = stream;
+ return NS_OK;
+}
+
+// Factory method to get an nsInputStream from a byte array. Result will
+// implement nsIStringInputStream and nsIRandomAccessStore
+extern "C" NS_COM nsresult
+NS_NewByteInputStream(nsIInputStream** aStreamResult,
+ const char* aStringToRead,
+ PRInt32 aLength)
+{
+ NS_PRECONDITION(aStreamResult, "null out ptr");
+
+ nsStringInputStream* stream = new nsStringInputStream();
+ if (! stream)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ NS_ADDREF(stream);
+
+ nsresult rv = stream->ShareData(aStringToRead, aLength);
+
+ if (NS_FAILED(rv)) {
+ NS_RELEASE(stream);
+ return rv;
+ }
+
+ *aStreamResult = stream;
+ return NS_OK;
+}
+
+// factory method for constructing a nsStringInputStream object
+NS_METHOD
+nsStringInputStreamConstructor(nsISupports *outer, REFNSIID iid, void **result)
+{
+ *result = nsnull;
+
+ if (outer)
+ return NS_ERROR_NO_AGGREGATION;
+
+ nsStringInputStream *inst;
+ NS_NEWXPCOM(inst, nsStringInputStream);
+ if (!inst)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ NS_ADDREF(inst);
+ nsresult rv = inst->QueryInterface(iid, result);
+ NS_RELEASE(inst);
+
+ return rv;
+}
diff --git a/src/libs/xpcom18a4/xpcom/io/nsStringStream.h b/src/libs/xpcom18a4/xpcom/io/nsStringStream.h
new file mode 100644
index 00000000..6f2f7f20
--- /dev/null
+++ b/src/libs/xpcom18a4/xpcom/io/nsStringStream.h
@@ -0,0 +1,84 @@
+/* -*- Mode: C++; tab-width: 2; 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):
+ * darin@netscape.com (original author)
+ *
+ * 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 ***** */
+
+#ifndef nsStringStream_h__
+#define nsStringStream_h__
+
+#include "nsISeekableStream.h"
+
+/* a6cf90e8-15b3-11d2-932e-00805f8add32 */
+#define NS_IRANDOMACCESS_IID \
+{ 0xa6cf90eb, 0x15b3, 0x11d2, \
+ {0x93, 0x2e, 0x00, 0x80, 0x5f, 0x8a, 0xdd, 0x32} }
+
+//========================================================================================
+class nsIRandomAccessStore
+// Supports Seek, Tell etc.
+//========================================================================================
+: public nsISeekableStream
+{
+public:
+ NS_DEFINE_STATIC_IID_ACCESSOR(NS_IRANDOMACCESS_IID)
+
+/* "PROTECTED" */
+ NS_IMETHOD GetAtEOF(PRBool* outAtEOF) = 0;
+ NS_IMETHOD SetAtEOF(PRBool inAtEOF) = 0;
+}; // class nsIRandomAccessStore
+
+#include "nsIStringStream.h"
+
+/**
+ * nsStringInputStream : nsIStringInputStream
+ * , nsIInputStream
+ * , nsISeekableStream
+ * , nsIRandomAccessStore
+ */
+#define NS_STRINGINPUTSTREAM_CLASSNAME "nsStringInputStream"
+#define NS_STRINGINPUTSTREAM_CONTRACTID "@mozilla.org/io/string-input-stream;1"
+#define NS_STRINGINPUTSTREAM_CID \
+{ /* 0abb0835-5000-4790-af28-61b3ba17c295 */ \
+ 0x0abb0835, \
+ 0x5000, \
+ 0x4790, \
+ {0xaf, 0x28, 0x61, 0xb3, 0xba, 0x17, 0xc2, 0x95} \
+}
+extern NS_METHOD nsStringInputStreamConstructor(nsISupports *, REFNSIID, void **);
+
+
+
+#endif // nsStringStream_h__
diff --git a/src/libs/xpcom18a4/xpcom/io/nsUnicharInputStream.cpp b/src/libs/xpcom18a4/xpcom/io/nsUnicharInputStream.cpp
new file mode 100644
index 00000000..99e9364a
--- /dev/null
+++ b/src/libs/xpcom18a4/xpcom/io/nsUnicharInputStream.cpp
@@ -0,0 +1,415 @@
+/* -*- Mode: C++; tab-width: 2; 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 ***** */
+
+
+#include "nsIUnicharInputStream.h"
+#include "nsIByteBuffer.h"
+#include "nsIUnicharBuffer.h"
+#include "nsIServiceManager.h"
+#include "nsString.h"
+#include "nsCRT.h"
+#include "nsUTF8Utils.h"
+#include <fcntl.h>
+#if defined(NS_WIN32)
+#include <io.h>
+#else
+#include <unistd.h>
+#endif
+
+class StringUnicharInputStream : public nsIUnicharInputStream {
+public:
+ StringUnicharInputStream(nsString* aString);
+
+ NS_DECL_ISUPPORTS
+
+ NS_IMETHOD Read(PRUnichar* aBuf,
+ PRUint32 aCount,
+ PRUint32 *aReadCount);
+ NS_IMETHOD ReadSegments(nsWriteUnicharSegmentFun aWriter,
+ void* aClosure,
+ PRUint32 aCount, PRUint32* aReadCount);
+ NS_IMETHOD Close();
+
+ nsString* mString;
+ PRUint32 mPos;
+ PRUint32 mLen;
+
+private:
+ ~StringUnicharInputStream();
+};
+
+StringUnicharInputStream::StringUnicharInputStream(nsString* aString)
+{
+ mString = aString;
+ mPos = 0;
+ mLen = aString->Length();
+}
+
+StringUnicharInputStream::~StringUnicharInputStream()
+{
+ if (nsnull != mString) {
+ delete mString;
+ }
+}
+
+NS_IMETHODIMP
+StringUnicharInputStream::Read(PRUnichar* aBuf,
+ PRUint32 aCount,
+ PRUint32 *aReadCount)
+{
+ if (mPos >= mLen) {
+ *aReadCount = 0;
+ return NS_OK;
+ }
+ const PRUnichar* us = mString->get();
+ NS_ASSERTION(mLen >= mPos, "unsigned madness");
+ PRUint32 amount = mLen - mPos;
+ if (amount > aCount) {
+ amount = aCount;
+ }
+ memcpy(aBuf, us + mPos, sizeof(PRUnichar) * amount);
+ mPos += amount;
+ *aReadCount = amount;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+StringUnicharInputStream::ReadSegments(nsWriteUnicharSegmentFun aWriter,
+ void* aClosure,
+ PRUint32 aCount, PRUint32 *aReadCount)
+{
+ PRUint32 bytesWritten;
+ PRUint32 totalBytesWritten = 0;
+
+ nsresult rv;
+ aCount = PR_MIN(mString->Length() - mPos, aCount);
+
+ while (aCount) {
+ rv = aWriter(this, aClosure, mString->get() + mPos,
+ totalBytesWritten, aCount, &bytesWritten);
+
+ if (NS_FAILED(rv)) {
+ // don't propagate errors to the caller
+ break;
+ }
+
+ aCount -= bytesWritten;
+ totalBytesWritten += bytesWritten;
+ mPos += bytesWritten;
+ }
+
+ *aReadCount = totalBytesWritten;
+
+ return NS_OK;
+}
+
+nsresult StringUnicharInputStream::Close()
+{
+ mPos = mLen;
+ if (nsnull != mString) {
+ delete mString;
+ mString = 0;
+ }
+ return NS_OK;
+}
+
+NS_IMPL_ISUPPORTS1(StringUnicharInputStream, nsIUnicharInputStream)
+
+NS_COM nsresult
+NS_NewStringUnicharInputStream(nsIUnicharInputStream** aInstancePtrResult,
+ nsString* aString)
+{
+ NS_PRECONDITION(nsnull != aString, "null ptr");
+ NS_PRECONDITION(nsnull != aInstancePtrResult, "null ptr");
+ if ((nsnull == aString) || (nsnull == aInstancePtrResult)) {
+ return NS_ERROR_NULL_POINTER;
+ }
+
+ StringUnicharInputStream* it = new StringUnicharInputStream(aString);
+ if (nsnull == it) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ return it->QueryInterface(NS_GET_IID(nsIUnicharInputStream),
+ (void**) aInstancePtrResult);
+}
+
+//----------------------------------------------------------------------
+
+class UTF8InputStream : public nsIUnicharInputStream {
+public:
+ UTF8InputStream();
+ nsresult Init(nsIInputStream* aStream, PRUint32 aBufSize);
+
+ NS_DECL_ISUPPORTS
+ NS_IMETHOD Read(PRUnichar* aBuf,
+ PRUint32 aCount,
+ PRUint32 *aReadCount);
+ NS_IMETHOD ReadSegments(nsWriteUnicharSegmentFun aWriter,
+ void* aClosure,
+ PRUint32 aCount,
+ PRUint32 *aReadCount);
+ NS_IMETHOD Close();
+
+private:
+ ~UTF8InputStream();
+
+protected:
+ PRInt32 Fill(nsresult * aErrorCode);
+
+ static void CountValidUTF8Bytes(const char *aBuf, PRUint32 aMaxBytes, PRUint32& aValidUTF8bytes, PRUint32& aValidUCS2bytes);
+
+ nsCOMPtr<nsIInputStream> mInput;
+ nsCOMPtr<nsIByteBuffer> mByteData;
+ nsCOMPtr<nsIUnicharBuffer> mUnicharData;
+
+ PRUint32 mByteDataOffset;
+ PRUint32 mUnicharDataOffset;
+ PRUint32 mUnicharDataLength;
+};
+
+UTF8InputStream::UTF8InputStream() :
+ mByteDataOffset(0),
+ mUnicharDataOffset(0),
+ mUnicharDataLength(0)
+{
+}
+
+nsresult
+UTF8InputStream::Init(nsIInputStream* aStream, PRUint32 aBufferSize)
+{
+ if (aBufferSize == 0) {
+ aBufferSize = 8192;
+ }
+
+ nsresult rv = NS_NewByteBuffer(getter_AddRefs(mByteData), nsnull, aBufferSize);
+ if (NS_FAILED(rv)) return rv;
+ rv = NS_NewUnicharBuffer(getter_AddRefs(mUnicharData), nsnull, aBufferSize);
+ if (NS_FAILED(rv)) return rv;
+
+ mInput = aStream;
+
+ return NS_OK;
+}
+
+NS_IMPL_ISUPPORTS1(UTF8InputStream,nsIUnicharInputStream)
+
+UTF8InputStream::~UTF8InputStream()
+{
+ Close();
+}
+
+nsresult UTF8InputStream::Close()
+{
+ mInput = nsnull;
+ mByteData = nsnull;
+ mUnicharData = nsnull;
+
+ return NS_OK;
+}
+
+nsresult UTF8InputStream::Read(PRUnichar* aBuf,
+ PRUint32 aCount,
+ PRUint32 *aReadCount)
+{
+ NS_ASSERTION(mUnicharDataLength >= mUnicharDataOffset, "unsigned madness");
+ PRUint32 rv = mUnicharDataLength - mUnicharDataOffset;
+ nsresult errorCode;
+ if (0 == rv) {
+ // Fill the unichar buffer
+ rv = Fill(&errorCode);
+ if (rv <= 0) {
+ *aReadCount = 0;
+ return errorCode;
+ }
+ }
+ if (rv > aCount) {
+ rv = aCount;
+ }
+ memcpy(aBuf, mUnicharData->GetBuffer() + mUnicharDataOffset,
+ rv * sizeof(PRUnichar));
+ mUnicharDataOffset += rv;
+ *aReadCount = rv;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+UTF8InputStream::ReadSegments(nsWriteUnicharSegmentFun aWriter,
+ void* aClosure,
+ PRUint32 aCount, PRUint32 *aReadCount)
+{
+ NS_ASSERTION(mUnicharDataLength >= mUnicharDataOffset, "unsigned madness");
+ PRUint32 bytesToWrite = mUnicharDataLength - mUnicharDataOffset;
+ nsresult rv = NS_OK;
+ if (0 == bytesToWrite) {
+ // Fill the unichar buffer
+ bytesToWrite = Fill(&rv);
+ if (bytesToWrite <= 0) {
+ *aReadCount = 0;
+ return rv;
+ }
+ }
+
+ if (bytesToWrite > aCount)
+ bytesToWrite = aCount;
+
+ PRUint32 bytesWritten;
+ PRUint32 totalBytesWritten = 0;
+
+ while (bytesToWrite) {
+ rv = aWriter(this, aClosure,
+ mUnicharData->GetBuffer() + mUnicharDataOffset,
+ totalBytesWritten, bytesToWrite, &bytesWritten);
+
+ if (NS_FAILED(rv)) {
+ // don't propagate errors to the caller
+ break;
+ }
+
+ bytesToWrite -= bytesWritten;
+ totalBytesWritten += bytesWritten;
+ mUnicharDataOffset += bytesWritten;
+ }
+
+ *aReadCount = totalBytesWritten;
+
+ return NS_OK;
+}
+
+PRInt32 UTF8InputStream::Fill(nsresult * aErrorCode)
+{
+ if (nsnull == mInput) {
+ // We already closed the stream!
+ *aErrorCode = NS_BASE_STREAM_CLOSED;
+ return -1;
+ }
+
+ NS_ASSERTION(mByteData->GetLength() >= mByteDataOffset, "unsigned madness");
+ PRUint32 remainder = mByteData->GetLength() - mByteDataOffset;
+ mByteDataOffset = remainder;
+ PRInt32 nb = mByteData->Fill(aErrorCode, mInput, remainder);
+ if (nb <= 0) {
+ // Because we assume a many to one conversion, the lingering data
+ // in the byte buffer must be a partial conversion
+ // fragment. Because we know that we have recieved no more new
+ // data to add to it, we can't convert it. Therefore, we discard
+ // it.
+ return nb;
+ }
+ NS_ASSERTION(remainder + nb == mByteData->GetLength(), "bad nb");
+
+ // Now convert as much of the byte buffer to unicode as possible
+ PRUint32 srcLen, dstLen;
+ CountValidUTF8Bytes(mByteData->GetBuffer(),remainder + nb, srcLen, dstLen);
+
+ // the number of UCS2 characters should always be <= the number of
+ // UTF8 chars
+ NS_ASSERTION( (remainder+nb >= srcLen), "cannot be longer than out buffer");
+ NS_ASSERTION(PRInt32(dstLen) <= mUnicharData->GetBufferSize(),
+ "Ouch. I would overflow my buffer if I wasn't so careful.");
+ if (PRInt32(dstLen) > mUnicharData->GetBufferSize()) return 0;
+
+ ConvertUTF8toUTF16 converter(mUnicharData->GetBuffer());
+
+ nsASingleFragmentCString::const_char_iterator start = mByteData->GetBuffer();
+ nsASingleFragmentCString::const_char_iterator end = mByteData->GetBuffer() + srcLen;
+
+ copy_string(start, end, converter);
+ NS_ASSERTION(converter.Length() == dstLen, "length mismatch");
+
+ mUnicharDataOffset = 0;
+ mUnicharDataLength = dstLen;
+ mByteDataOffset = srcLen;
+
+ return dstLen;
+}
+
+void
+UTF8InputStream::CountValidUTF8Bytes(const char* aBuffer, PRUint32 aMaxBytes, PRUint32& aValidUTF8bytes, PRUint32& aValidUCS2chars)
+{
+ const char *c = aBuffer;
+ const char *end = aBuffer + aMaxBytes;
+ const char *lastchar = c; // pre-initialize in case of 0-length buffer
+ PRUint32 ucs2bytes = 0;
+ while (c < end && *c) {
+ lastchar = c;
+ ucs2bytes++;
+
+ if (UTF8traits::isASCII(*c))
+ c++;
+ else if (UTF8traits::is2byte(*c))
+ c += 2;
+ else if (UTF8traits::is3byte(*c))
+ c += 3;
+ else if (UTF8traits::is4byte(*c))
+ c += 4;
+ else if (UTF8traits::is5byte(*c))
+ c += 5;
+ else if (UTF8traits::is6byte(*c))
+ c += 6;
+ else {
+ NS_WARNING("Unrecognized UTF8 string in UTF8InputStream::CountValidUTF8Bytes()");
+ break; // Otherwise we go into an infinite loop. But what happens now?
+ }
+ }
+ if (c > end) {
+ c = lastchar;
+ ucs2bytes--;
+ }
+
+ aValidUTF8bytes = c - aBuffer;
+ aValidUCS2chars = ucs2bytes;
+}
+
+NS_COM nsresult
+NS_NewUTF8ConverterStream(nsIUnicharInputStream** aInstancePtrResult,
+ nsIInputStream* aStreamToWrap,
+ PRInt32 aBufferSize)
+{
+ // Create converter input stream
+ UTF8InputStream* it = new UTF8InputStream();
+ if (nsnull == it) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ nsresult rv = it->Init(aStreamToWrap, aBufferSize);
+ if (NS_FAILED(rv))
+ return rv;
+
+ return it->QueryInterface(NS_GET_IID(nsIUnicharInputStream),
+ (void **) aInstancePtrResult);
+}