зеркало из https://github.com/mozilla/gecko-dev.git
560 строки
18 KiB
C
560 строки
18 KiB
C
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
/* ***** BEGIN LICENSE BLOCK *****
|
|
* Version: NPL 1.1/GPL 2.0/LGPL 2.1
|
|
*
|
|
* The contents of this file are subject to the Netscape 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/NPL/
|
|
*
|
|
* 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 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 NPL, 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 NPL, the GPL or the LGPL.
|
|
*
|
|
* ***** END LICENSE BLOCK ***** */
|
|
|
|
|
|
#ifndef _XP_File_
|
|
#define _XP_File_
|
|
|
|
#include "xp_core.h"
|
|
#include "xp_path.h"
|
|
|
|
#define XP_FILE_NATIVE_PATH char *
|
|
#define XP_FILE_URL_PATH char*
|
|
/*-----------------------------------------------------------------------------
|
|
XP_File.h
|
|
Cross-Platform File API
|
|
|
|
XP_File INTERFACE IS A COMMONLY MISUNDERSTOOD API.PLEASE READ THESE
|
|
DOCS BEFORE USING THE API. The API is not self-documenting in any way,
|
|
and broken in many.
|
|
|
|
XP_File mimics the stdio file interface (fopen, fread & friends...).
|
|
The main difference is that to specify a file location, XP_File
|
|
uses a name in combination with an enum XP_FileType instead of full path.
|
|
|
|
For example :
|
|
stdio: fopen(filename, permissions)
|
|
maps to xp_file: XP_FileOpen (name, XP_FileType type, XP_FilePerm permissions);
|
|
|
|
Meaning of the name argument depends on the XP_FileType it is used with.
|
|
|
|
For example:
|
|
A) XP_FileOpen(name, xpURL)
|
|
name is the URL path to the file (/Drive/file.html)
|
|
B) XP_FileOpen(name, xpCache)
|
|
name is the partial path from cache directory (expands to cache-directory/name)
|
|
C) XP_FileOpen(name, xpTemporary)
|
|
name is the partial path from temporary directory (expands to cache-directory/name)
|
|
|
|
Corollary: YOU CANNOT MIX AND MATCH NAMES AND TYPES
|
|
For example:
|
|
newTempFile = XP_TempName(name, xpTemporary);
|
|
file = XP_FileOpen(newTempFile, xpURL, "r+w"); // BAD!, you've just mixed xpURL and xpTemporary
|
|
This is a very common error. It might work on some platforms, but it'll break others.
|
|
|
|
FILE NAME MADNESS
|
|
|
|
There are 3 basic ways to specify a file in the XP_File interface:
|
|
|
|
TheXP_FileSpec
|
|
|
|
char* - XP_FileType pair: used in most XP_File calls. the meaning of the name
|
|
depends on the enum. Common ones are:
|
|
XP_FileType / name means:
|
|
|
|
xpTemporary partial path relative to temporary directory
|
|
xpURL full path in the standard URL format (file://)
|
|
xpGlobalHistory name is ignored.
|
|
|
|
XP_FILE_NATIVE_PATH
|
|
|
|
char * used in platform-specific code, platform-specific full path
|
|
Windows: "C:\Windows\regedit.exe"
|
|
X: "/h/users/atotic/home.html"
|
|
Mac: "Macintosh HD:System Folder:System"
|
|
|
|
XP_FILE_URL_PATH used in cross-platform code, when you need to manipulate the full path.
|
|
It is a full path in the standard URL format: (file://<path>, but just the <path> part)
|
|
|
|
XP_FILE_NATIVE_PATH and XP_FILE_URL_PATH are often confused
|
|
|
|
To convert between these types:
|
|
|
|
Conversion: TheXP_FileSpec -> XP_FILE_NATIVE_PATH
|
|
Call: WH_FileName(name, type)
|
|
Example: WH_FileName("myName", xpTemporary) -> "/u/tmp/myName" on Unix
|
|
-> "C:\tmp\myName" on Windows
|
|
-> "Mac HD:Temporary folder:myName" on Mac
|
|
|
|
Conversion: XP_FILE_NATIVE_PATH -> XP_FILE_URL_PATH
|
|
Call: XP_PlatformFileToURL(name)
|
|
Example: Windows: XP_PlatformFileToURL("C:\tmp\myName") -> "file:///C|/tmp/myName"
|
|
Unix: XP_PlatformFileToURL(/u/tmp/myName") -> "file:///u/tmp/myName"
|
|
Mac: XP_PlatformFileToURL("Mac HD:Temporary folder:myName") -> "file:///Mac%20HD/Temporary%20folder/myName"
|
|
|
|
You cannot convert anything into arbitrary TheXP_FileSpec, but you can use the XP_FILE_URL_PATH
|
|
in combination with xpURL enum.
|
|
Example: XP_FileOpen("C|/tmp/myName", xpURL) works
|
|
|
|
COMMONLY USED CALLS NOT IN STDIO:
|
|
|
|
WH_FileName(name, XP_FileType) - maps TheXP_FileSpec pair to XP_FILE_NATIVE_PATH.
|
|
Use it when you need access to full paths in platform-specific code. For example:
|
|
For example:
|
|
cacheName = XP_FileName("", xpCacheFAT);
|
|
fopen(cacheName);
|
|
|
|
|
|
EXTENSIONS TO STDIO API:
|
|
|
|
- XP_FileRename also moves files accross file systems (copy + delete)
|
|
|
|
|
|
MISC API NOTES:
|
|
|
|
The names of most of the calls are derived by prepending stdio call name
|
|
with XP_File. (open -> XP_FileOpen, opendir -> XP_FileOpenDir). But warren
|
|
was fixing up some bugs near the end of 3.0, and in his infinite wisdom renamed
|
|
a few of them to WH_ instead of XP_. So, XP_FileName became WH_FileName. WH_ stands
|
|
for Warren Harris. Whatever...
|
|
|
|
Calls are not documented well. Most of the docs are in this header file. You
|
|
can usually get a hint about what the call should do by looking up the man
|
|
pages of the equivalent stdio call.
|
|
|
|
OLD DOCS (May mislead you, read at your peril!):
|
|
Macintosh file system is not organized in the same hierarchy as UNIX and PC
|
|
':' is a separator character instead of '/', '~', '/../' constructs do not
|
|
work. Basically, you cannot easily open a file using a pathname.
|
|
Because different types of files need to be stored in different
|
|
directories, the XP_FileOpen API takes a file type and file name as an
|
|
argument. Semantics of how file is opened vary with type.
|
|
|
|
"Personal" files are stored in the user's "home" directory (~ on unix,
|
|
launch point or system folder on Mac, dunno about Windows). File names
|
|
may be ignored since there's no reason for multiple personal files.
|
|
"Temporary" files are stored in /tmp or the equivalent
|
|
"URL" is the only type that allows access to anywhere on the disk
|
|
|
|
File Type File name Semantics
|
|
xpURL url part after file:/// Platform specific
|
|
without #aklsdf
|
|
xpGlobalHistory - Opens personal global history
|
|
xpKeyChain - Opens personal key chain
|
|
xpUserPrefs - Opens personal prefs file
|
|
xpJSMailFilters - Opens personal js mail filters
|
|
xpJSHTMLFilters - Opens personal js HTML filters
|
|
xpCache name without path Opens a cache file
|
|
xpExtCache Fully qualified path Opens an external cache file
|
|
xpTemporary local filename Opens a temporary file
|
|
xpNewsrc - Opens a .newsrc file
|
|
xpSignature - Opens a .signature file
|
|
xpCertDB - Opens cert DB
|
|
xpCertDBNameIDX - Opens cert name index
|
|
xpKeyDB - Opens key DB
|
|
xpSignedAppletDB - Signed applets DB filename
|
|
xpJSCookieFilters - Opens personal js cookie filters
|
|
|
|
-----------------------------------------------------------------------------*/
|
|
|
|
#if defined(XP_UNIX) || defined(XP_BEOS)
|
|
# include <dirent.h>
|
|
# include <sys/stat.h>
|
|
#endif /* XP_UNIX */
|
|
|
|
#ifdef XP_MAC
|
|
# ifndef _UNIX
|
|
# include <unix.h>
|
|
# endif
|
|
# include <Types.h>
|
|
# include <Files.h>
|
|
#endif /* XP_MAC */
|
|
|
|
#ifdef XP_WIN32
|
|
# include <windows.h>
|
|
# include <sys/stat.h>
|
|
/*
|
|
* typedef taken from dbm/include/winfile.h
|
|
* removing dbm dependency for bug 100966
|
|
*/
|
|
typedef struct DIR_Struct {
|
|
void * directoryPtr;
|
|
WIN32_FIND_DATA data;
|
|
} DIR;
|
|
|
|
#endif /* XP_WIN32 */
|
|
|
|
#ifdef XP_OS2
|
|
# include "dirent.h"
|
|
# include "sys/stat.h"
|
|
#endif /* XP_OS2 */
|
|
|
|
/*-----------------------------------------------------------------------------
|
|
Types - NOT prototypes
|
|
Protoypes go further down.
|
|
-----------------------------------------------------------------------------*/
|
|
|
|
typedef enum XP_FileType {
|
|
xpAddrBookNew,
|
|
xpAddrBook,
|
|
xpUserPrefs,
|
|
xpKeyChain,
|
|
xpGlobalHistory,
|
|
xpGlobalHistoryList,
|
|
xpHotlist,
|
|
xpTemporary,
|
|
xpURL,
|
|
xpCache,
|
|
xpSARCache, /* larubbio location independent cache */
|
|
xpMimeTypes,
|
|
xpCacheFAT,
|
|
xpSARCacheIndex, /* larubbio location independent cache */
|
|
xpNewsRC,
|
|
xpSNewsRC,
|
|
xpTemporaryNewsRC,
|
|
xpNewsgroups,
|
|
xpSNewsgroups,
|
|
xpNewsHostDatabase,
|
|
xpHTTPCookie,
|
|
xpProxyConfig,
|
|
xpSocksConfig,
|
|
xpSignature,
|
|
xpNewsrcFileMap,
|
|
xpFileToPost,
|
|
xpMailFolder,
|
|
xpMailFolderSummary,
|
|
xpJSMailFilters,
|
|
xpJSHTMLFilters,
|
|
xpMailSort, /* File that specifies which mail folders
|
|
to file which messages. */
|
|
xpNewsSort,
|
|
xpMailFilterLog, /* log files for mail/news filters */
|
|
xpNewsFilterLog,
|
|
xpMailPopState,
|
|
xpMailSubdirectory,
|
|
xpBookmarks,
|
|
xpCertDB,
|
|
xpCertDBNameIDX,
|
|
xpKeyDB,
|
|
xpSecModuleDB,
|
|
xpExtCache,
|
|
xpExtCacheIndex, /* index of external indexes */
|
|
xpRegistry,
|
|
|
|
xpXoverCache, /* Cache of XOVER files. This
|
|
filename always takes the form
|
|
"hostname/groupname" (except for calls
|
|
to XP_MakeDirectory, where it is just
|
|
"hostname").*/
|
|
xpEditColorScheme, /* ifdef EDITOR Families of "good" color combinations */
|
|
xpJPEGFile, /* used for pictures in LDAP directory */
|
|
xpVCardFile, /* used for versit vCards */
|
|
xpLDIFFile, /* used for LDIF (LDAP Interchange format files */
|
|
xpImapRootDirectory,
|
|
xpImapServerDirectory,
|
|
xpHTMLAddressBook, /* old (HTML) address book */
|
|
xpSignedAppletDB, /* filename of signed applets DB */
|
|
xpCryptoPolicy, /* Export Policy control file */
|
|
xpFolderCache, /* for caching mail/news folder info */
|
|
xpPKCS12File, /* used for PKCS 12 certificate transport */
|
|
xpJSCookieFilters, /* Opens personal js cookie filters */
|
|
#if defined(CookieManagement)
|
|
xpHTTPCookiePermission,
|
|
#endif
|
|
#if defined(SingleSignon)
|
|
xpHTTPSingleSignon,
|
|
#endif
|
|
xpLIClientDB,
|
|
xpLIPrefs,
|
|
xpJSConfig, /* Javascript 'jsc' config cache file */
|
|
xpSecurityModule /* security loadable module */
|
|
} XP_FileType;
|
|
|
|
|
|
#define XP_FILE_READ "r"
|
|
#define XP_FILE_READ_BIN "rb"
|
|
#define XP_FILE_WRITE "w"
|
|
#define XP_FILE_WRITE_BIN "wb"
|
|
#define XP_FILE_APPEND "a"
|
|
#define XP_FILE_APPEND_BIN "ab"
|
|
#define XP_FILE_UPDATE "r+"
|
|
#define XP_FILE_TRUNCATE "w+"
|
|
#ifdef SUNOS4
|
|
/* XXX SunOS4 hack -- make this universal by using r+b and w+b */
|
|
#define XP_FILE_UPDATE_BIN "r+"
|
|
#define XP_FILE_TRUNCATE_BIN "w+"
|
|
#else
|
|
#define XP_FILE_UPDATE_BIN "rb+"
|
|
#define XP_FILE_TRUNCATE_BIN "wb+"
|
|
#endif
|
|
|
|
#ifdef __BORLANDC__
|
|
#define _stat stat
|
|
#endif
|
|
#ifdef XP_MAC
|
|
#define XP_STAT_READONLY( statinfo ) (0)
|
|
#else
|
|
#define XP_STAT_READONLY( statinfo ) ((statinfo.st_mode & S_IWRITE) == 0)
|
|
#endif
|
|
|
|
|
|
typedef FILE * XP_File;
|
|
typedef char * XP_FilePerm;
|
|
|
|
#ifdef XP_WIN
|
|
typedef struct _stat XP_StatStruct;
|
|
#endif
|
|
|
|
#if defined(XP_OS2)
|
|
typedef struct stat XP_StatStruct;
|
|
#endif
|
|
|
|
#if defined(XP_UNIX) || defined(XP_BEOS)
|
|
typedef struct stat XP_StatStruct;
|
|
#endif
|
|
|
|
#if defined (XP_MAC)
|
|
typedef struct stat XP_StatStruct;
|
|
#endif
|
|
|
|
#if defined(XP_UNIX) || defined(XP_WIN) || defined(XP_OS2) || defined(XP_BEOS)
|
|
typedef DIR * XP_Dir;
|
|
typedef struct dirent XP_DirEntryStruct;
|
|
#endif
|
|
|
|
#ifdef XP_MAC
|
|
/* Mac has non-stdio definitions of XP_Dir and XP_DirEntryStruct */
|
|
|
|
typedef struct macdstat {
|
|
char d_name[300];
|
|
} XP_DirEntryStruct;
|
|
|
|
typedef struct dirstruct {
|
|
XP_DirEntryStruct dirent;
|
|
FSSpec dirSpecs;
|
|
short index; /* Index for looping in XP_OpenDir */
|
|
XP_FileType type;
|
|
} *XP_Dir;
|
|
|
|
#endif /* XP_MAC */
|
|
|
|
|
|
/*-----------------------------------------------------------------------------
|
|
Prototypes
|
|
-----------------------------------------------------------------------------*/
|
|
|
|
XP_BEGIN_PROTOS
|
|
|
|
/*
|
|
* Given TheXP_FileSpec returns XP_FILE_NATIVE_PATH.
|
|
* See docs on top for more info
|
|
*/
|
|
PUBLIC XP_FILE_NATIVE_PATH WH_FileName (const char *name, XP_FileType type);
|
|
|
|
/*
|
|
* Given XP_FILE_URL_PATH, returns XP_FILE_NATIVE_PATH
|
|
* See docs on top for more info
|
|
*/
|
|
PUBLIC XP_FILE_NATIVE_PATH WH_FilePlatformName(const char * name);
|
|
|
|
/*
|
|
* Given XP_FILE_NATIVE_PATH, returns XP_FILE_URL_PATH
|
|
* See docs on top for more info
|
|
*/
|
|
PUBLIC XP_FILE_URL_PATH XP_PlatformFileToURL (const XP_FILE_NATIVE_PATH platformName);
|
|
|
|
/* takes a portion of a local path and returns an allocated portion
|
|
* of an XP path. This function was initially created to translate
|
|
* imap server folder names to xp names.
|
|
*/
|
|
PUBLIC char *XP_PlatformPartialPathToXPPartialPath (const char *platformPath);
|
|
|
|
/* Returns a pathname of a file suitable for use as temporary storage
|
|
* Warning: you can only use the returned pathname in other XP_File calls
|
|
* with the same enum.
|
|
*/
|
|
extern char * WH_TempName(XP_FileType type, const char * prefix);
|
|
|
|
/* Returns the path to the temp directory. */
|
|
extern char* XP_TempDirName(void);
|
|
|
|
/* Various other wrappers for stdio.h
|
|
*/
|
|
extern XP_File XP_FileOpen (const char* name, XP_FileType type,
|
|
const XP_FilePerm permissions);
|
|
|
|
extern int XP_Stat(const char * name, XP_StatStruct * outStat,
|
|
XP_FileType type);
|
|
|
|
extern XP_Dir XP_OpenDir(const char * name, XP_FileType type);
|
|
|
|
extern void XP_CloseDir(XP_Dir dir);
|
|
|
|
extern XP_DirEntryStruct *XP_ReadDir(XP_Dir dir);
|
|
|
|
extern int XP_FileRename(const char * from, XP_FileType fromtype,
|
|
const char * to, XP_FileType totype);
|
|
|
|
extern int XP_FileRemove(const char * name, XP_FileType type);
|
|
|
|
extern int XP_MakeDirectory(const char* name, XP_FileType type);
|
|
|
|
/* XP_MakeDirectoryR recursively creates all the directories needed */
|
|
extern int XP_MakeDirectoryR(const char* name, XP_FileType type);
|
|
|
|
extern int XP_RemoveDirectory(const char *name, XP_FileType type);
|
|
|
|
extern int XP_RemoveDirectoryRecursive(const char *name, XP_FileType type);
|
|
|
|
/* Truncate a file to be a given length. It is important (especially on the
|
|
Mac) to make sure not to ever call this if you have the file opened. */
|
|
extern int XP_FileTruncate(const char* name, XP_FileType type, int32 length);
|
|
|
|
extern int XP_FileWrite (const void* source, int32 count, XP_File file);
|
|
|
|
extern char * XP_FileReadLine(char * dest, int32 bufferSize, XP_File file);
|
|
|
|
extern long XP_FileTell( XP_File file );
|
|
|
|
extern int XP_FileFlush( XP_File file );
|
|
|
|
extern int XP_FileClose(XP_File file);
|
|
|
|
/* Defines */
|
|
|
|
#define XP_Fileno fileno
|
|
#define XP_FileSeek(file,offset,whence) fseek ((file), (offset), (whence))
|
|
#define XP_FileRead(dest,count,file) fread ((dest), 1, (count), (file))
|
|
#define XP_FileTell(file) ftell(file)
|
|
#define XP_FileFlush(file) fflush(file)
|
|
/* varargs make it impossible to do any other way */
|
|
#define XP_FilePrintf fprintf
|
|
|
|
/* XP_GetNewsRCFiles returns a null terminated array of pointers to malloc'd
|
|
* filename's. Each filename represents a different newsrc file.
|
|
*
|
|
* return only the filename since the path is not needed or wanted.
|
|
*
|
|
* Netlib is expecting a string of the form:
|
|
* [s]newsrc-host.name.domain[:port]
|
|
*
|
|
* examples:
|
|
* newsrc-news.mcom.com
|
|
* snewsrc-flop.mcom.com
|
|
* newsrc-news.mcom.com:118
|
|
* snewsrc-flop.mcom.com:1191
|
|
*
|
|
* the port number is optional and should only be
|
|
* there when different from the default.
|
|
* the "s" in front of newsrc signifies that
|
|
* security is to be used.
|
|
*
|
|
* return NULL on critical error or no files
|
|
*/
|
|
extern char ** XP_GetNewsRCFiles(void);
|
|
|
|
|
|
/* #ifdef EDITOR */
|
|
/* If pszLocalName is not NULL, we return the full pathname
|
|
* in local platform syntax. Caller must free this string.
|
|
* Returns TRUE if file already exists
|
|
* Windows version implemented in winfe\fegui.cpp
|
|
*/
|
|
extern Bool XP_ConvertUrlToLocalFile(const char * szURL, char **pszLocalName);
|
|
|
|
/* Construct a temporary filename in same directory as supplied "file:///" type URL
|
|
* Used as write destination for saving edited document
|
|
* User must free string.
|
|
* Windows version implemented in winfe\fegui.cpp
|
|
*/
|
|
extern char * XP_BackupFileName( const char * szURL );
|
|
|
|
extern XP_Bool XP_FileIsFullPath(const char * name);
|
|
|
|
extern XP_Bool XP_FileNameContainsBadChars(const char *name);
|
|
|
|
#ifdef XP_MAC
|
|
|
|
/* XP_FileNumberOfFilesInDirectory returns the number of files in the specified
|
|
* directory
|
|
*/
|
|
extern int XP_FileNumberOfFilesInDirectory(const char * dir_name, XP_FileType type);
|
|
|
|
#endif /* XP_MAC */
|
|
|
|
XP_END_PROTOS
|
|
|
|
|
|
|
|
#if defined(XP_UNIX) || defined(XP_WIN) || defined(XP_OS2) || defined(XP_BEOS)
|
|
|
|
/* Unix and Windows preferences communicate with the netlib through these
|
|
global variables. Netlib does "something sensible" if they are NULL.
|
|
*/
|
|
extern char *FE_SARCacheDir;
|
|
extern char *FE_UserNewsHost;
|
|
extern char *FE_UserNewsRC;
|
|
extern char *FE_TempDir;
|
|
extern char *FE_CacheDir;
|
|
extern char *FE_DownloadDir;
|
|
extern char *FE_GlobalHist;
|
|
|
|
#endif /* XP_UNIX || XP_WIN */
|
|
|
|
|
|
/* Each of the three patforms seem to have subtly different opinions
|
|
about which functions should be aliases for their stdio counterparts,
|
|
and which should be real functions.
|
|
|
|
Note that on the platform in question, these #defines will override
|
|
the prototypes above.
|
|
*/
|
|
|
|
#if defined(XP_UNIX) || defined(XP_WIN) || defined(XP_OS2) || defined(XP_BEOS)
|
|
# define XP_FileReadLine(destBuffer, bufferSize, file) \
|
|
fgets(destBuffer, bufferSize, file)
|
|
#endif /* XP_UNIX || XP_WIN */
|
|
|
|
#if defined(XP_UNIX) || defined(XP_OS2) || defined(XP_BEOS)
|
|
# define XP_ReadDir(DirPtr) readdir((DirPtr))
|
|
# define XP_CloseDir(DirPtr) closedir((DirPtr))
|
|
#endif /* XP_UNIX */
|
|
|
|
#if defined(XP_MAC) || defined(XP_WIN) || defined(XP_OS2)
|
|
/* #### Note! This defn is dangerous because `count' is evaluated twice! */
|
|
# define XP_FileWrite(source, count, file) \
|
|
fwrite((void*)(source), 1, \
|
|
(size_t) ((count) == -1 ? strlen((char*)source) : (count)), \
|
|
(file))
|
|
|
|
# define XP_FileClose(file) fclose ((file))
|
|
|
|
#endif /* XP_MAC || XP_WIN */
|
|
|
|
#endif /* _XP_File_ */
|