diff options
Diffstat (limited to 'src/libs/xpcom18a4/xpcom/io')
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..eb144c55 --- /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; + long 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; + long 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(¤tSegmentOffset); + 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(¤tMtime); + 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(¤tSegmentOffset); + 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); +} |