Milán Major

netscape-revival

1 branch
Code

lib/xp/xp_file.c

/* -*- Mode: C; tab-width: 4 -*- */
#include "xp.h"
#include "prmon.h"

#ifdef XP_UNIX
# include <sys/fcntl.h>
#endif

#if defined(XP_MAC)

#ifndef powerc
	// To avoid inclusion of the fp.h header, which is incompatible with math.h
	// during 68k compilation
	#define __FP__	
#endif


#include "mcom_db.h"		// For the stupid 
int mkstemp(const char *path)
{
	return -1;
}
#include "xp_file.h"
#pragma cplusplus on
#include "macdlg.h"
	// macfe
#include "resgui.h"
#include "ufilemgr.h"
#include "uprefd.h"
	// Netscape
#include "net.h"
#include "xp.h"
	// utilities
#include "PascalString.h"
#include <LString.h>

#ifdef PROFILE
#pragma profile on
#endif

XP_BEGIN_PROTOS

/**************************************************************************************
 * New stdio architecture on the Mac
 * - All files will be referenced by standard path names.
 * - Standard routines convert between the file name and file spec
 * - WH_FileName with a type always returns a full path.
 **************************************************************************************/
   
const char* CacheFilePrefix = "cache";

// For the Mac, get the Mac specs first
extern long	_fcreator, _ftype;/* Creator and Type for files created by the open function */
OSErr XP_FileSpec(const char *name, XP_FileType type, FSSpec* spec)
{
	CStr255 tempName(name);
	OSErr err = noErr;
	_fcreator = emSignature;
	switch (type)	{
/* MAIN FOLDER */
		case xpUserPrefs:	// Ignored by MacFE
			err = fnfErr;
			break;
		case xpGlobalHistory:	// Simple
			_ftype = emGlobalHistory;
			*spec = CPrefs::GetFilePrototype( CPrefs::MainFolder );
			GetIndString(tempName, 300, globHistoryName);
			LString::CopyPStr(tempName, spec->name, 63);
			break;
		case xpBookmarks:
			{
			_ftype = emTextType;
			char * newName = NULL;
			newName = name ? XP_STRDUP(name) : NULL;
			if (newName == NULL)
				err = bdNamErr;
			else
			{
				NET_UnEscape(newName);
				err = CFileMgr::FSSpecFromLocalURLPath( newName, spec);
				XP_FREE(newName);
			}
			}
			break;
/* MISC */
		case xpTemporary:
			_ftype = emTextType;
			*spec = CPrefs::GetTemporaryFolder();
			LString::CopyPStr(tempName, spec->name, 63);
			
			break;
		case xpFileToPost:
			{
			_ftype = emTextType;
			char * newName = NULL;
			newName = name ? XP_STRDUP(name) : NULL;
			if (newName == NULL)
				err = bdNamErr;
			else
			{
				NET_UnEscape(newName);
				err = CFileMgr::FSSpecFromLocalURLPath( newName, spec);
				XP_FREE(newName);
			}
			}
			break;
		case xpURL:
		case xpExtCache:
			_ftype = emTextType;
			err = CFileMgr::FSSpecFromLocalURLPath( name, spec);
			break;
		case xpMimeTypes:
			*spec = CPrefs::GetFilePrototype( CPrefs::MainFolder );
			GetIndString(tempName, 300, mimeTypes);
			LString::CopyPStr(tempName, spec->name, 63);
			break;
		case xpHTTPCookie:
			_ftype = emMagicCookie;
			*spec = CPrefs::GetFilePrototype( CPrefs::MainFolder );
			GetIndString(tempName, 300, magicCookie);
			LString::CopyPStr(tempName, spec->name, 63);
			break;
		case xpProxyConfig:
			_ftype = emTextType;
			*spec = CPrefs::GetFilePrototype( CPrefs::MainFolder );
			GetIndString(tempName, 300, proxyConfig);
			LString::CopyPStr(tempName, spec->name, 63);
			break;
		case xpSocksConfig:
			_ftype = emTextType;
			*spec = CPrefs::GetFilePrototype( CPrefs::MainFolder );
			GetIndString(tempName, 300, socksConfig);
			LString::CopyPStr(tempName, spec->name, 63);	
			break;		
		case xpSignature:
			_ftype = emTextType;
			*spec = CPrefs::GetFolderSpec( CPrefs::SignatureFile );
			break;
/* SECURITY */
		case xpKeyChain:	// Should probably be the default name instead
			_ftype = emKeyChain;
			*spec = CPrefs::GetFilePrototype( CPrefs::SecurityFolder );
			LString::CopyPStr(tempName, spec->name, 63);
			break;
		case xpCertDB:
			_ftype = emCertificates;
			*spec = CPrefs::GetFilePrototype( CPrefs::SecurityFolder );
			GetIndString(tempName, 300, certDB);
			if (name != NULL)
				tempName += name;
			LString::CopyPStr(tempName, spec->name, 63);	
			break;
		case xpCertDBNameIDX:
			_ftype = emCertIndex;
			*spec = CPrefs::GetFilePrototype( CPrefs::SecurityFolder );
			GetIndString(tempName, 300, certDBNameIDX);
			break;	
		case xpKeyDB:
			_ftype = emTextType;
			*spec = CPrefs::GetFilePrototype( CPrefs::SecurityFolder );
			GetIndString(tempName, 300, keyDb);
			LString::CopyPStr(tempName, spec->name, 63);	
			break;	
/* CACHE */
		case xpCache:
			_ftype = emTextType;
			*spec = CPrefs::GetFilePrototype( CPrefs::DiskCacheFolder );
			LString::CopyPStr(tempName, spec->name, 63);
			break;
		case xpCacheFAT:
			_ftype = emCacheFat;
			*spec = CPrefs::GetFilePrototype( CPrefs::DiskCacheFolder );
			GetIndString(tempName, 300, cacheLog);
			LString::CopyPStr(tempName, spec->name, 63);	
			break;
		case xpExtCacheIndex:
			_ftype = emExtCache;			
			*spec = CPrefs::GetFilePrototype( CPrefs::MainFolder );
			GetIndString(tempName, 300, extCacheFile);
			LString::CopyPStr(tempName, spec->name, 63);
			break;	
/* NEWS */
		case xpNewsRC:
		case xpSNewsRC:
			_ftype = emNewsrcFile;
			*spec = CPrefs::GetFilePrototype( CPrefs::NewsFolder );
			if ( (name == NULL) || (*name == 0))
			{
				GetIndString( tempName, 300, newsrc );
				/* NET_RegisterNewsrcFile( tempName, name, type == xpSNewsRC, FALSE ); */
			}
			else
			{
				tempName = NET_MapNewsrcHostToFilename( name, type == xpSNewsRC, FALSE );
				if ( tempName.IsEmpty() )
				{
					tempName = name;
					CFileMgr::UniqueFileSpec( *spec, CStr31( tempName ), *spec );
					tempName = spec->name;
					NET_RegisterNewsrcFile( tempName, name, type == xpSNewsRC, FALSE );
				}
			}
			LString::CopyPStr( tempName, spec->name, 63 );
			break;
		case xpNewsgroups:
			_ftype = emTextType;
			*spec = CPrefs::GetFilePrototype( CPrefs::NewsFolder );
			tempName = NET_MapNewsrcHostToFilename( name, FALSE, TRUE );
			if ( tempName.IsEmpty() )
			{
				CStr63 newName( "groups-" );
				newName += name;
				tempName = newName;
				CFileMgr::UniqueFileSpec( *spec, CStr31( tempName ), *spec );
				tempName = spec->name;
				NET_RegisterNewsrcFile( tempName, name, FALSE, TRUE );
			}
			LString::CopyPStr( tempName, spec->name, 63 );
			break;
		case xpSNewsgroups:
			_ftype = emTextType;
			*spec = CPrefs::GetFilePrototype( CPrefs::NewsFolder );
			tempName = NET_MapNewsrcHostToFilename( name, TRUE, TRUE );
			if ( tempName.IsEmpty() )
			{
				CStr63 newName( "sgroups-" );
				newName += name;
				tempName = newName;
				CFileMgr::UniqueFileSpec( *spec, CStr31( tempName ), *spec );
				tempName = spec->name;
				NET_RegisterNewsrcFile( tempName, name, TRUE, TRUE );
			}
			LString::CopyPStr( tempName, spec->name, 63 );
			break;
		case xpTemporaryNewsRC:
			_ftype = emNewsrcFile;
			*spec = CPrefs::GetFilePrototype( CPrefs::NewsFolder );
			tempName = "temp newsrc";
			LString::CopyPStr( tempName, spec->name, 63 );
			break;
		case xpNewsrcFileMap:
			_ftype = emNewsrcDatabase;
			*spec = CPrefs::GetFilePrototype( CPrefs::NewsFolder );
			GetIndString(tempName, 300, newsFileMap);
			LString::CopyPStr(tempName, spec->name, 63);	
			break;
		case xpXoverCache:
			_ftype = emNewsXoverCache;
			*spec = CPrefs::GetFilePrototype( CPrefs::NewsFolder );
			GetIndString(tempName, 300, xoverCache);
			LString::CopyPStr(tempName, spec->name, 63);	
			break;			
/* MAIL */
		case xpMailSort:
			_ftype = emMailSort;
			*spec = CPrefs::GetFilePrototype( CPrefs::MailFolder );
			GetIndString(tempName, 300, mailSortName);
			LString::CopyPStr(tempName, spec->name, 63);	
			break;	
		case xpMailFolder:
			{
			_ftype = emTextType;
			char * newName = name ? XP_STRDUP(name) : NULL;
			if (newName == NULL)
				err = bdNamErr;
			else
			{
				NET_UnEscape(newName);
				err = CFileMgr::FSSpecFromLocalURLPath( newName, spec);
				XP_FREE(newName);
			}
			}
			break;
		case xpMailFolderSummary:
			_ftype = emMailFolderSummary;
			{
				char * newName = NULL;
				CStr255 extension;
	
				newName = name ? XP_STRDUP(name) : NULL;
				if (newName == NULL)
				{
					err = bdNamErr;
					break;
				}
				NET_UnEscape(newName);
				err = CFileMgr::FSSpecFromLocalURLPath( newName, spec);
				XP_FREE(newName);
				GetIndString( extension, 300, mailBoxExt);
				tempName = spec->name;
				tempName = tempName + extension;
				LString::CopyPStr( tempName, spec->name, 63 );
			}
			break;
		case xpMailPopState:
			_ftype = emTextType;
			*spec = CPrefs::GetFilePrototype( CPrefs::MainFolder );
			GetIndString( tempName, 300, mailPopState);
			LString::CopyPStr( tempName, spec->name, 63 );
			break;
		default:
			err = bdNamErr;
			break;
	}
	if (err == fnfErr)
		err = noErr;
	return err;
}

char* xp_FileName( const char* name, XP_FileType type, char* buf, char* configBuf )
{
	FSSpec		macSpec;
	OSErr		err = noErr;
	
	if ( ( type == xpCache ) && name)
	{
		char * cachePath = CPrefs::GetCachePath();
		if (cachePath == NULL)
			err = fnfErr;
		else
		{
			XP_STRCPY(buf, cachePath);
			XP_STRCAT(buf, name);
		}
	}
	else
	{
		char*		path = NULL;
	
		err = XP_FileSpec( name, type, &macSpec );

		if (err != noErr)
			return NULL;
			
		path = CFileMgr::PathNameFromFSSpec( macSpec, TRUE );
		
		if ((strlen(path) > 1000) )
		{
			XP_ASSERT(FALSE);
			return NULL;
		}

		if ( !path )
			return NULL;

		XP_STRCPY( buf, path );
		XP_FREE( path );
	}
	
	if ( err != noErr )
		return NULL;
	
	return buf;
}

char*
XP_TempDirName(void)
{
	char* path;
	FSSpec spec = CPrefs::GetTemporaryFolder();
	path = CFileMgr::PathNameFromFSSpec(spec, TRUE);
	if (strlen(path) > 1000) {
		XP_ASSERT(FALSE);
		return NULL;
	}
	return path;
}

char * xp_FilePlatformName(const char * name, char* path)
{
	FSSpec spec;
	OSErr err;
	char * fullPath;

	if (name == NULL)
		return NULL;

	err = CFileMgr::FSSpecFromLocalURLPath(name, &spec);
	if (err != noErr)
		return NULL;
	fullPath = CFileMgr::PathNameFromFSSpec( spec, TRUE );
	if (fullPath && (XP_STRLEN(fullPath) < 300))
		XP_STRCPY(path, fullPath);
	else
		return NULL;
	if (fullPath)
		XP_FREE(fullPath);
	return path;
}

// Needs to deal with both CR, CRLF, and LF end-of-line
extern char * XP_FileReadLine(char * dest, int32 bufferSize,
							  XP_File file)
{
// Hack-o-rama alert
// We are using the internal stdio structures
// Rationale: most of the time, we get CR terminated lines
// this should be fast. We do a lookup for LF by looking into internal
// file buffer, if available.
// In best case, our overhead is only looking for LF in the stream
	char * retBuf = fgets(dest, bufferSize, file);
	if (retBuf == NULL)	// EOF
		return NULL;
	char * LFoccurence = (char *)strchr(retBuf, LF);
	if (LFoccurence != NULL)	// We have hit LF before CR, 
	{
		fseek(file, -(strlen(retBuf) - (LFoccurence  - retBuf))+1, SEEK_CUR);
		LFoccurence[1] = 0;
	}
	else	// We have CR, check if the next character is LF
	{
		Boolean didGetc = FALSE;
		int c;
		// Following code is based on getc from stdio.h. Might break with next release
// stdio code:
//		inline int getc(FILE *_Str)  {return ((_Str->_Next
//	< _Str->_Rend ? *_Str->_Next++ : fgetc(_Str))); }
		if (file->_Next < file->_Rend)
			c = *file->_Next;
		else
		{
			c = fgetc(file);
			didGetc = TRUE;
		}
		if (c == EOF)
			;	// Do nothing, end of file
		else if (c == LF)	// Swallow CR/LF
		{
			if (didGetc)
				;	// Do nothing, pointer was already moved
			else
				fgetc(file);	// For side-effect of moving the mark
			int len = strlen(retBuf);
			if (len < bufferSize)	// Append LF to our buffer if we can
			{
				retBuf[len++] = LF;
				retBuf[len] = 0;
			}
		}
		else	// No LF, just clean up the seek
		{
			if (didGetc)
				fseek(file, -1, SEEK_CUR);
			else
				;
		}
	}
	return retBuf;
/* This one is legal (no hacks). It works,  but is slow. 
// Algorithm
// Read in up to CR
// 	if next character is LF we are done
//  else
//     if there was a LF before CR, return the string including LF
//
	char * retBuf = fgets(dest, bufferSize, file);
	if (retBuf != NULL)	
	{
		// First, deal with CR/LF case
		int c = fgetc(file);
		if (c != LF)
		{
			// Not a linefeed, retract reading of the last character
			if (c != EOF) 
			{
				fseek(file, -1, SEEK_CUR);
				// We have read up to CR, or EOF
				size_t len = strlen(retBuf);
				char * LFoccurence = (char *)memchr(retBuf, LF, len);
				if (LFoccurence != NULL && (LFoccurence[1] != 0))	// We have hit LF before CR
				{
					fseek(file, -(len - (LFoccurence  - retBuf))+1, SEEK_CUR);
					LFoccurence[1] = 0;
					return retBuf;
				}
			}
		}
		else	// Append the linefeed
		{
			size_t len = strlen(retBuf);	// Do not append if we would overwrite memory
			if ((len + 2) < bufferSize)
			{
				retBuf[len++] = LF;
				retBuf[len] = 0;	// Terminate
			}
		}
	}
	return retBuf;
*/
}

char * xp_TempName(XP_FileType type, const char * prefix, char* buf, char* buf2, unsigned int *count)
{
	FSSpec tempSpec;
	CStr255 defaultName(prefix);
	OSErr err = noErr;

	switch (type)	{
		case xpTemporaryNewsRC:
			defaultName = "temp newsrc";
			break;
			break;
		case xpCache:
			defaultName = CacheFilePrefix;
			CStr255 dateString;
			::NumToString (::TickCount(), dateString);
			defaultName += dateString;
			break;
		case xpBookmarks:
			defaultName += ".bak";
			break;
		case xpFileToPost:	// Temporary files to post return the full path
			
		case xpTemporary:
		default:
			if (defaultName.IsEmpty())
				defaultName = "temp";
			break;
	}
	
	
	if (type == xpFileToPost)	
		// Temporary post files still go to Temporary, and not post directory
		err = XP_FileSpec(defaultName, xpTemporary, &tempSpec);
	else
		err = XP_FileSpec(defaultName, type, &tempSpec);

	if (err && err != fnfErr)
		return NULL;

	FSSpec finalSpec;
	err = CFileMgr::UniqueFileSpec(tempSpec, tempSpec.name, finalSpec);
	if (err != noErr)
	{
		XP_ASSERT(FALSE);
		return NULL;
	}
	if ((type == xpFileToPost) || (type == xpBookmarks) || (type == xpMailFolder) )	// Post file needs full pathname
	{
		char * tempPath = CFileMgr::PathNameFromFSSpec( finalSpec, TRUE );
		tempPath = CFileMgr::EncodeMacPath( tempPath );
		if (tempPath == NULL)
			return NULL;
		else if (XP_STRLEN(tempPath) > 500)	// Buffer overflow check
		{
			XP_FREE(tempPath);
			return NULL;
		}
			XP_STRCPY(buf, tempPath);
		XP_FREE(tempPath);
	}
	else
		XP_STRCPY(buf, CStr63(finalSpec.name));
	return buf;
}



// XP_OpenDir is not handling different directories yet. 
XP_Dir XP_OpenDir( const char* name, XP_FileType type )
{
	CInfoPBRec		pb;
	OSErr			err;
	DirInfo*		dipb;
	struct dirstruct *		dir = XP_NEW(struct dirstruct);

	if (dir == NULL)
		return NULL;

	XP_TRACE(( "opening file: %s", name ));
	dir->type = type;
	
	err = XP_FileSpec( name, type, &dir->dirSpecs );
	if ( err != noErr )
	{
		XP_DELETE(dir);
		return NULL;
	}

	dipb = (DirInfo*)&pb;

	pb.hFileInfo.ioNamePtr = dir->dirSpecs.name;
	pb.hFileInfo.ioVRefNum = dir->dirSpecs.vRefNum;
	pb.hFileInfo.ioDirID = dir->dirSpecs.parID;
	pb.hFileInfo.ioFDirIndex = 0;	/* use ioNamePtr and ioDirID */

	err = PBGetCatInfoSync( &pb );

	/* test that we have gotten a directory back, not a file */
	if ( (err != noErr ) || !( dipb->ioFlAttrib & 0x0010 ) )
	{
		XP_DELETE( dir );
		return NULL;
	}
	dir->dirSpecs.parID = pb.hFileInfo.ioDirID;
	dir->index = pb.dirInfo.ioDrNmFls;

	return (XP_Dir)dir;
}

void XP_CloseDir( XP_Dir dir )
{
	if ( dir )
		XP_DELETE(dir);
}

int XP_FileNumberOfFilesInDirectory(const char * dir_name, XP_FileType type)
{
	FSSpec spec;
	OSErr err = XP_FileSpec(dir_name, type, &spec);
	if ((err != noErr) && (err != fnfErr))
		goto loser;
	CInfoPBRec pb;
	pb.hFileInfo.ioNamePtr = spec.name;
	pb.hFileInfo.ioVRefNum = spec.vRefNum;
	pb.hFileInfo.ioDirID = spec.parID;
	pb.hFileInfo.ioFDirIndex = 0;	/* use ioNamePtr and ioDirID */
	err = PBGetCatInfoSync(&pb);
	if (err != noErr)
		goto loser;
	return pb.dirInfo.ioDrNmFls;
loser:
	return 10000;	/* ???? */
}

XP_DirEntryStruct * XP_ReadDir(XP_Dir dir)
{
tryagain:
	if (dir->index <= 0)
		return NULL;
	CInfoPBRec cipb;
	DirInfo	*dipb=(DirInfo *)&cipb;
	dipb->ioCompletion = NULL;
	dipb->ioFDirIndex = dir->index;
	dipb->ioVRefNum = dir->dirSpecs.vRefNum; // Might need to use vRefNum, not sure
	dipb->ioDrDirID = dir->dirSpecs.parID;
	dipb->ioNamePtr = (StringPtr)&dir->dirent.d_name;
	OSErr err = PBGetCatInfoSync (&cipb);
	/* We are traversing the directory backwards */
	if (err != noErr)
	{
		if (dir->index > 1)
		{
			dir->index--;
			goto tryagain;
		}
		else
			return NULL;
	}
	p2cstr((StringPtr)&dir->dirent.d_name);

	/* Mail folders are treated in a special way */

	if ((dir->type == xpMailFolder) ||
		(dir->type == xpMailFolderSummary))
	{
		char * newName = XP_STRDUP(dir->dirent.d_name);
		newName = CFileMgr::EncodeMacPath(newName);
		if (newName)
		{
			XP_STRCPY(dir->dirent.d_name, newName);
			XP_FREE(newName);
		}
	}

	dir->index--;
	return &dir->dirent;
}

int XP_MakeDirectory(const char* name, XP_FileType type)
{
	FSSpec spec;
	OSErr err = XP_FileSpec(name, type, &spec);
	if ((err != noErr) && (err != fnfErr))	// Should not happen
		return -1;
	short refNum;
	long dirID;
	err = CFileMgr::CreateFolderInFolder(
				spec.vRefNum, spec.parID, spec.name,	// Name of the new folder
				&refNum, &dirID);
	if (err == noErr)
		return 0;
	else
		return -1;
}

int XP_FileRename( const char* from, XP_FileType fromtype,
				   const char* to, XP_FileType totype )
{
	FSSpec		spec;
	OSErr		err;
	CStr255		cto;
	
	char* fromName = WH_FileName( from, fromtype );
	char* toName = WH_FileName( to, totype );
	
	if ( fromName && toName )
	{
		err = CFileMgr::FSSpecFromPathname( toName, &spec );
		if ( err == noErr )
			::FSpDelete( &spec );
		cto = (CStr255)spec.name;
			
		err = CFileMgr::FSSpecFromPathname( fromName, &spec );
		if ( err == noErr )
			err = ::FSpRename( &spec, cto );
	}
	else
		err = -1;
	if ( fromName )
		XP_FREE( fromName );
	if ( toName )
		XP_FREE( toName );

	if ( err == noErr )
		return 0;
	return -1;
}

int XP_FileRemove( const char* name, XP_FileType type )
{
	FSSpec		spec;
	OSErr		err;

	err = XP_FileSpec( name, type, &spec);

	if ( err == noErr )
	{
		err = ::FSpDelete( &spec );
		if ( type == xpNewsRC || type == xpNewsgroups )
	        NET_UnregisterNewsHost( name, FALSE );
		else if ( type == xpSNewsRC || type == xpSNewsgroups)
	        NET_UnregisterNewsHost( name, TRUE );
	}
	
	if ( err == noErr )
		return 0;
	return -1;
}

int XP_FileTruncate( const char* name, XP_FileType type, int32 len )
{
	FSSpec		spec;
	OSErr		err, err2;
	short refNum;

	if (len < 0)
		return EINVAL;

	err = XP_FileSpec( name, type, &spec);
	if (err != noErr)
		return EINVAL;

	err = FSpOpenDF(&spec, fsRdWrPerm, &refNum);
	if (err != noErr)
		return EACCES;
	
	err = SetEOF(refNum, len);
	
	err2 = FSClose(refNum);
	if ((err != noErr) || (err2 != noErr))
		return EIO;
	return 0;
}

XP_END_PROTOS

#ifdef PROFILE
#pragma profile off
#endif

#endif	/* XP_MAC */

XP_BEGIN_PROTOS


#if defined (XP_MAC) || defined(XP_UNIX)
/* Now that Mac is using stdio, we can share some of this.  Yay.
   Presumably Windows can share this too, but they keep these
   functions in the FE, so I don't know.
 */


int XP_Stat( const char* name, XP_StatStruct * outStat,  XP_FileType type )
{
	int		result = 0;
	
	char* newName = WH_FileName( name, type );
	if (!newName) return -1;
	result = stat( newName, outStat );
	XP_FREE(newName);
	return result;
}

XP_File XP_FileOpen( const char* name, XP_FileType type,
					 const XP_FilePerm permissions )
{
  char* newName = WH_FileName(name, type);
  XP_File result;
#ifdef XP_UNIX
  XP_StatStruct st;
  XP_Bool make_private_p = FALSE;
#endif

  /* One should never open newsrc for output directly. */
  XP_ASSERT (type != xpNewsRC || type != xpSNewsRC ||
			 !strchr (permissions, 'w'));

  if (newName == NULL)
	return NULL;

#ifdef XP_UNIX
  switch (type)
	{
	  /* These files are always private: if the user chmods them, we
		 make them -rw------ again the next time we use them, because
		 it's really important.
	   */
	case xpHTTPCookie:
	case xpKeyChain:
	case xpCertDB:
	case xpCertDBNameIDX:
	case xpKeyDB:
	case xpSecModuleDB:
	  /* Always make tmp files private, because of their short lifetime. */
	case xpTemporary:
	case xpTemporaryNewsRC:
	case xpFileToPost:
	case xpPKCS12File:
	  if (strchr (permissions, 'w'))     /* opening for output */
		make_private_p = TRUE;
	  break;

	  /* These files are created private, but if the user then goes and
		 chmods them, we let their changes stick, because it's reasonable
		 for a user to decide to give away read permission on these
		 (though by default, it makes sense for them to be more private
		 than the user's other files.)
	   */
	case xpCacheFAT:
	case xpCache:
	case xpGlobalHistory:
	case xpHotlist:
	case xpBookmarks:
	case xpMailFolder:
	  if (strchr (permissions, 'w') &&   /* opening for output */
		  XP_Stat (newName, &st, type))  /* and it doesn't exist yet */
		make_private_p = TRUE;
	  break;


	case xpProxyConfig:
	case xpSocksConfig:
	case xpNewsRC:
	case xpSNewsRC:
	case xpNewsgroups:
	case xpSNewsgroups:
	case xpURL:
	case xpMimeTypes:
	case xpSignature:
	case xpMailFolderSummary:
	case xpMailSort:
	case xpMailPopState:
	case xpExtCache:
	case xpExtCacheIndex:
	case xpXoverCache:
	  /* These files need not be more private than any other, so we
		 always create them with the default umask. */
	  break;
	default:
	  XP_ASSERT(0);
	}
#endif /* XP_UNIX */

#ifndef MCC_PROXY
  /* At this point, we'd better have an absolute path, because passing a
	 relative path to fopen() is undefined.  (For the client, at least -
	 I gather the proxy is different?) */
#endif/* !MCC_PROXY */

  result = fopen(newName, permissions);
  
#ifdef XP_MAC
 	if (result != 0)
 	{
 		int err;
 		
 		err = setvbuf(	result, 		/* file to buffer */
 						NULL,			/* allocate the buffer for us */
 						_IOFBF, 		/* fully buffer */
 						8 * 1024);		/* 8k buffer */
 						
		XP_ASSERT(err == 0); 						
 	}
#endif  

#ifdef XP_UNIX
  if (make_private_p && result)
#ifdef SCO_SV
	chmod (newName, S_IRUSR | S_IWUSR); /* rw only by owner */
#else
	fchmod (fileno (result), S_IRUSR | S_IWUSR); /* rw only by owner */
#endif
#endif /* XP_UNIX */

  XP_FREE(newName);
  return result; 
}

#endif /* XP_MAC || XP_UNIX */

#ifdef XP_UNIX

/* This is just like fclose(), except that it does fflush() and fsync() first,
   to ensure that the bits have made it to the disk before we return.
 */
int XP_FileClose(XP_File file)
{
  int filedes;

  /* This busniess with `status' and `err' is an attempt to preserve the
	 very first non-0 errno we get, while still proceeding to close the file
	 if the fflush/fsync failed for some (weird?) reason. */
  int status, err = 0;
  XP_ASSERT(file);
  if (!file) return -1;
  status = fflush(file);              /* Push the stdio buffer to the disk. */
  if (status != 0) err = errno;

  filedes = XP_Fileno (file);
  /*
   * Only fsync when the file is not read-only, otherwise
   * HPUX and IRIX return an error, and Linux syncs the disk anyway.
   */
  if (fcntl(filedes, F_GETFL) != O_RDONLY) {
    status = fsync(filedes);   /* Wait for disk buffers to go out. */
    if (status != 0 && err == 0) err = errno;
  }

  status = fclose(file);              /* Now close it. */
  if (status != 0 && err == 0) err = errno;

  errno = err;
  return status ? status : err ? -1 : 0;
}

#endif /* XP_UNIX */


XP_END_PROTOS

#ifdef XP_UNIX

#include <unistd.h>		/* for getpid */

#ifdef TRACE
#define Debug 1
#define DO_TRACE(x) if (xp_file_debug) XP_TRACE(x)


int xp_file_debug = 0;
#else
#define DO_TRACE(x)
#endif

int XP_FileRemove(const char * name, XP_FileType type)
{
	char * newName = WH_FileName(name, type);
	int result;
	if (!newName) return -1;
	result = remove(newName);
	XP_FREE(newName);
	if (result != 0)
	{
	  /* #### Uh, what is this doing?  Of course errno is set! */
		int err = errno;
		if (err != 0)
			return -1;
	}
	
	return result;
}


int XP_FileRename(const char * from, XP_FileType fromtype,
				  const char * to, XP_FileType totype)
{
	char * fromName = WH_FileName(from, fromtype);
	char * toName = WH_FileName(to, totype);
	int res = 0;
	if (fromName && toName)
		res = rename(fromName, toName);
	else
		res = -1;
	if (fromName)
		XP_FREE(fromName);
	if (toName)
		XP_FREE(toName);
	return res;
}


/* Create a new directory */

int XP_MakeDirectory(const char* name, XP_FileType type)
{
  int result;
  static XP_Bool madeXoverDir = FALSE;
  mode_t mode;
  switch (type) {
  case xpMailFolder:
	mode = 0700;
	break;
  default:
	mode = 0755;
	break;
  }
  if (type == xpXoverCache) {
	/* Once per session, we'll check that the xovercache directory is
	   created before trying to make any subdirectories of it.  Sigh. ###*/
	if (!madeXoverDir) {
	  madeXoverDir = TRUE;
	  XP_MakeDirectory("", type);
	}
  }
#ifdef __linux
  {
	/* Linux is a steaming pile.  It chokes if the parent of the
	   named directory is a symbolic link.  Dumbdumbdumb. */
	char rp[MAXPATHLEN];
	char *s, *s0 = WH_FileName (name, type);
	if (!s0) return -1;

	/* realpath is advertised to return a char* (rp) on success, and on
	   failure, return 0, set errno, and leave a partial path in rp.
	   But on Linux 1.2.3, it doesn't -- it returns 0, leaves the result
	   in rp, and doesn't set errno.  So, if errno is unset, assume all
	   is well.
	 */
	rp[0] = 0;
	errno = 0;
	s = realpath (s0, rp);
	XP_FREE(s0);
	/* WTF??? if (errno) return -1; */
	if (!s) s = rp;
	if (!*s) return -1;
	result = mkdir (s, mode);
  }
#elif defined(SUNOS4)
  {
	char rp[MAXPATHLEN];
	char *s = WH_FileName (name, type);
	char *tmp;
	if (!s) return -1;

	/* Take off all trailing slashes, because some systems (SunOS 4.1.2)
	   don't think mkdir("/tmp/x/") means mkdir("/tmp/x"), sigh... */
	PR_snprintf (rp, MAXPATHLEN-1, "%s", s);
	XP_FREE(s);
	while ((tmp = XP_STRRCHR(rp, '/')) && tmp[1] == '\0')
	  *tmp = '\0';

	result = mkdir (rp, mode);
  }
#else /* !__linux && !SUNOS4 */
  DO_TRACE(("XP_MakeDirectory called: creating: %s", name));
  {
	  char* filename = WH_FileName(name, type);
	  if (!filename) return -1;
	  result = mkdir(filename, mode);
	  XP_FREE(filename);
  }
#endif
  return result;
}


int XP_FileTruncate(const char* name, XP_FileType type, int32 length)
{
  char* filename = WH_FileName(name, type);
  int result = 0;
  if (!result) return -1;
  result = truncate(filename, length);
  XP_FREE(filename);
  return result;
}



/* Writes to a file
 */
int XP_FileWrite (const void* source, int32 count, XP_File file)
{
    int32		realCount;

    if ( count < 0 )
        realCount = XP_STRLEN( source );
    else
        realCount = count;

	return( fwrite( source, 1, realCount, file ) );
}

/* The user can set these on the preferences dialogs; the front end
   informs us of them by setting these variables.  We do "something
   sensible" if 0...
   */
PUBLIC char *FE_UserNewsHost = 0;	/* clone of NET_NewsHost, mostly... */
PUBLIC char *FE_UserNewsRC = 0;
PUBLIC char *FE_TempDir = 0;
PUBLIC char *FE_CacheDir = 0;
PUBLIC char *FE_GlobalHist = 0;


static const char *
xp_unix_config_directory(char* buf)
{
  static XP_Bool initted = FALSE;
  const char *dir = ".netscape";
  char *home;
  if (initted)
	return buf;

  home = getenv ("HOME");
  if (!home)
	home = "";

#ifdef OLD_UNIX_FILES

  sprintf (buf, "%.900s", home);
  if (buf[strlen(buf)-1] == '/')
	buf[strlen(buf)-1] = 0;

#else  /* !OLD_UNIX_FILES */

  if (*home && home[strlen(home)-1] == '/')
	sprintf (buf, "%.900s%s", home, dir);
  else
	sprintf (buf, "%.900s/%s", home, dir);

#endif /* !OLD_UNIX_FILES */

  return buf;
}


static int
xp_unix_sprintf_stat( char * buf,
                      const char * dirName,
                      const char * lang,
                      const char * fileName)
{
    int               result;
    int               len;
    XP_StatStruct     outStat;

    if (dirName == NULL || 0 == (len = strlen(dirName)) || len > 900)
	return -1;

    while (--len > 0 && dirName[len] == '/')
	/* do nothing */;		/* strip trailing slashes */
    strncpy(buf, dirName, ++len);
    buf[len++] = '/';
    buf[len]   =  0;
    if (lang != NULL && *lang == 0)
	lang  = NULL;
    if (lang != NULL) 
	sprintf(buf+len, "%.100s/%s", lang, fileName);
    else
	strcpy(buf+len, fileName);
    result = stat( buf, &outStat );
    if (result >= 0 && !S_ISREG(outStat.st_mode))
	result = -1;
    if (result < 0 && lang != NULL)
	result = xp_unix_sprintf_stat(buf, dirName, NULL, fileName);
    return result;
}


/* returns a unmalloced static
 * string that is only available for
 * temporary use.
 */
PUBLIC char *
xp_FileName (const char *name, XP_FileType type, char* buf, char* configBuf)
{
  const char *conf_dir = xp_unix_config_directory(configBuf);

  switch (type)
	{
	case xpCacheFAT:
	  {
	  	const char *cache_dir = FE_CacheDir;
		if (!cache_dir || !*cache_dir)
		  cache_dir = conf_dir;
		if (cache_dir [strlen (cache_dir) - 1] == '/')
		  sprintf (buf, "%.900sindex.db", cache_dir);
		else
		  sprintf (buf, "%.900s/index.db", cache_dir);

		name = buf;
		break;
	  }
	case xpCache:
	  {
		/* WH_TempName() returns relative pathnames for the cache files,
		   so that relative paths get written into the cacheFAT database.
		   WH_FileName() converts them to absolute if they aren't already
		   so that the logic of XP_FileOpen() and XP_FileRename() and who
		   knows what else is simpler.
		 */
		if (*name != '/')
		  {
			char *tmp = FE_CacheDir;
			if (!tmp || !*tmp) tmp = "/tmp";
			if (tmp [strlen (tmp) - 1] == '/')
			  sprintf (buf, "%.500s%.500s", tmp, name);
			else
			  sprintf (buf, "%.500s/%.500s", tmp, name);
			name = buf;
		  }
		break;
	  }

	case xpHTTPCookie:
	  {
#ifndef OLD_UNIX_FILES
		sprintf (buf, "%.900s/cookies", conf_dir);
#else  /* OLD_UNIX_FILES */
		sprintf (buf, "%.900s/.netscape-cookies", conf_dir);
#endif /* OLD_UNIX_FILES */
		name = buf;
		break;
	  }
	case xpProxyConfig:
	  {
		sprintf(buf, "%.900s/proxyconf", conf_dir);
		name = buf;
		break;
	  }
	case xpTemporary:
	  {
		if (*name != '/')
		  {
			char *tmp = FE_TempDir;
			if (!tmp || !*tmp) tmp = "/tmp";
			if (tmp [strlen (tmp) - 1] == '/')
			  sprintf (buf, "%.500s%.500s", tmp, name);
			else
			  sprintf (buf, "%.500s/%.500s", tmp, name);
			name = buf;
		  }
		break;
	  }
	case xpNewsRC:
	case xpSNewsRC:
	case xpTemporaryNewsRC:
	  {
		/* In this case, `name' is "" or "host" or "host:port". */

		char *home = getenv ("HOME");
		const char *newsrc_dir = ((FE_UserNewsRC && *FE_UserNewsRC)
								  ? FE_UserNewsRC
								  : (home ? home : ""));
		const char *basename = (type == xpSNewsRC ? ".snewsrc" : ".newsrc");
		const char *suffix = (type == xpTemporaryNewsRC ? ".tmp" : "");
		if (*name)
		  sprintf (buf, "%.800s%.1s%.8s-%.128s%.4s",
				   newsrc_dir,
				   (newsrc_dir[XP_STRLEN(newsrc_dir)-1] == '/' ? "" : "/"),
				   basename, name, suffix);
		else
		  sprintf (buf, "%.800s%.1s%.128s%.4s",
				   newsrc_dir,
				   (newsrc_dir[XP_STRLEN(newsrc_dir)-1] == '/' ? "" : "/"),
				   basename, suffix);

		name = buf;
		break;
	  }
	case xpNewsgroups:
	case xpSNewsgroups:
	  {
#ifndef OLD_UNIX_FILES
		sprintf (buf, "%.800s/%snewsgroups-%.128s", 
				 conf_dir, 
				 type == xpSNewsgroups ? "s" : "", 
				 name);
#else  /* OLD_UNIX_FILES */
		sprintf (buf, "%.800s/.netscape-%snewsgroups-%.128s", 
				 conf_dir, 
				 type == xpSNewsgroups ? "s" : "", 
				 name);
#endif /* OLD_UNIX_FILES */
		name = buf;
		break;
	  }

	case xpExtCacheIndex:
#ifndef OLD_UNIX_FILES
		sprintf (buf, "%.900s/cachelist", conf_dir);
#else  /* OLD_UNIX_FILES */
		sprintf (buf, "%.900s/.netscape-cache-list", conf_dir);
#endif /* OLD_UNIX_FILES */
		name = buf;
		break;

	case xpGlobalHistory:
		name = FE_GlobalHist;
		break;

	case xpCertDB:
#ifndef OLD_UNIX_FILES
		if ( name ) {
			sprintf (buf, "%.900s/cert%s.db", conf_dir, name);
		} else {
			sprintf (buf, "%.900s/cert.db", conf_dir);
		}
#else  /* OLD_UNIX_FILES */
		sprintf (buf, "%.900s/.netscape-certdb", conf_dir);
#endif /* OLD_UNIX_FILES */
		name = buf;
		break;
	case xpCertDBNameIDX:
#ifndef OLD_UNIX_FILES
		sprintf (buf, "%.900s/cert-nameidx.db", conf_dir);
#else  /* OLD_UNIX_FILES */
		sprintf (buf, "%.900s/.netscape-certdb-nameidx", conf_dir);
#endif /* OLD_UNIX_FILES */
		name = buf;
		break;
	case xpKeyDB:
#ifndef OLD_UNIX_FILES
		if ( name ) {
			sprintf (buf, "%.900s/key%s.db", conf_dir, name);
		} else {
			sprintf (buf, "%.900s/key.db", conf_dir);
		}
#else  /* OLD_UNIX_FILES */
		sprintf (buf, "%.900s/.netscape-keydb", conf_dir);
#endif /* OLD_UNIX_FILES */
		name = buf;
		break;
	case xpSecModuleDB:
		sprintf (buf, "%.900s/secmodule.db", conf_dir);
		name = buf;
		break;

	case xpFileToPost:
	case xpSignature:
	  	/* These are always absolute pathnames. 
		 * BUT, the user can type in whatever so
		 * we can't assert if it doesn't begin
		 * with a slash
		 */
		break;

	case xpExtCache:
	case xpKeyChain:
	case xpURL:
	case xpHotlist:
	case xpBookmarks:
	case xpMimeTypes:
	case xpSocksConfig:
	case xpMailFolder:
#ifdef BSDI
	    /* In bsdi, mkdir fails if the directory name is terminated
	     * with a '/'. - dp
	     */
	    if (name[strlen(name)-1] == '/') {
		strcpy(buf, name);
		buf[strlen(buf)-1] = '\0';
		name = buf;
	    }
#endif
#ifndef MCC_PROXY
	  /*
	   * These are always absolute pathnames for the Navigator.
	   * Only the proxy (servers) may have pathnames relative
	   * to their current working directory (the servers chdir
	   * to their admin/config directory on startup.
	   *
	   */
	  if (name) XP_ASSERT (name[0] == '/');
#endif	/* ! MCC_PROXY */

	  break;

	case xpMailFolderSummary:
	  /* Convert /a/b/c/foo to /a/b/c/.foo.summary (note leading dot) */
	  {
		const char *slash;
		slash = strrchr (name, '/');
		if (name) XP_ASSERT (name[0] == '/');
		XP_ASSERT (slash);
		if (!slash) return 0;
		XP_MEMCPY (buf, name, slash - name + 1);
		buf [slash - name + 1] = '.';
		XP_STRCPY (buf + (slash - name) + 2, slash + 1);
		XP_STRCAT (buf, ".summary");
		name = buf;
		break;
	  }

	case xpMailSort:
#ifndef OLD_UNIX_FILES
	  sprintf(buf, "%.900s/mailsort", conf_dir);
#else  /* OLD_UNIX_FILES */
	  sprintf(buf, "%.900s/.netscape-mailsort", conf_dir);
#endif /* OLD_UNIX_FILES */
	  name = buf;
	  break;
	case xpMailPopState:
#ifndef OLD_UNIX_FILES
	  sprintf(buf, "%.900s/popstate", conf_dir);
#else  /* OLD_UNIX_FILES */
	  sprintf(buf, "%.900s/.netscape-popstate", conf_dir);
#endif /* OLD_UNIX_FILES */
	  name = buf;
	  break;
	case xpXoverCache:
	  sprintf(buf, "%.800s/xover-cache/%.128s", conf_dir, name);
	  name = buf;
	  break;

	case xpCryptoPolicy:
     {
         extern void fe_GetProgramDirectory(char *path, int len);
         char *policyFN = "policyMoz40P2";
         char *mozHome  = getenv("MOZILLA_HOME");
         char *lang     = getenv("LANG");
         int   result;
         char  dirName[1024];

         name = buf;
         if (!xp_unix_sprintf_stat(buf, conf_dir, lang, policyFN))
             break;
         if (!xp_unix_sprintf_stat(buf, mozHome, lang, policyFN))
             break;
         fe_GetProgramDirectory(dirName, sizeof dirName);
         if (!xp_unix_sprintf_stat(buf, dirName, lang, policyFN))
             break;

         /* couldn't find it, but must leave a valid file name in buf */
         sprintf(buf, "%.900s/%s", conf_dir, policyFN);
         break;
     }

	case xpPKCS12File:
	  /* Convert /a/b/c/foo to /a/b/c/foo.p12 (note leading dot) */
	  {
		  char *dot = NULL;
		  int len = 0;

		  if (name) 
			  XP_ASSERT (name[0] == '/');

		  dot = XP_STRRCHR(name, '.');
		  if (dot) {
			  len = dot - name + 1;
			  XP_MEMCPY(buf, name, len);
			  buf[len] = 0;
		  }/* if */

		  XP_STRCAT (buf, ".p12");
		  name = buf;
		break;
	  }


	default:
	  abort ();
	}

  return (char *) name;
}

char * xp_FilePlatformName(const char * name, char* path)
{
	if ((name == NULL) || (XP_STRLEN(name) > 1000))
		return NULL;
	XP_STRCPY(path, name);
	return path;
}

char * XP_PlatformFileToURL (const char *name)
{
	char *prefix = "file://";
	char *retVal = XP_ALLOC (XP_STRLEN(name) + XP_STRLEN(prefix) + 1);
	if (retVal)
	{
		XP_STRCPY (retVal, "file://");
		XP_STRCAT (retVal, name);
	}
	return retVal;
}

char *XP_PlatformPartialPathToXPPartialPath(const char *platformPath)
{
	/* using the unix XP_PlatformFileToURL, there is no escaping! */
	return XP_STRDUP(platformPath);
}


#define CACHE_SUBDIRS

char*
XP_TempDirName(void)
{
	char *tmp = FE_TempDir;
	if (!tmp || !*tmp) tmp = "/tmp";
	return XP_STRDUP(tmp);
}

char *
xp_TempName (XP_FileType type, const char * prefix, char* buf, char* buf2, unsigned int *count)
{
#define NS_BUFFER_SIZE	1024
  char *value = buf;
  time_t now;

  *buf = 0;
  XP_ASSERT (type != xpTemporaryNewsRC);

  if (type == xpCache)
	{
	  /* WH_TempName() must return relative pathnames for the cache files,
		 so that relative paths get written into the cacheFAT database,
		 making the directory relocatable.
	   */
	  *buf = 0;
	}
  else
	{
	  char *tmp = FE_TempDir;
	  if (!tmp || !*tmp) tmp = "/tmp";
	  XP_SPRINTF (buf, "%.500s", tmp);

	  if (!prefix || !*prefix)
		prefix = "tmp";
	}

  XP_ASSERT (!XP_STRCHR (prefix, '/'));
  if (*buf && buf[XP_STRLEN (buf)-1] != '/')
	XP_STRCAT (buf, "/");

  /* It's good to have the cache file names be pretty long, with a bunch of
	 inputs; this makes the variant part be 15 chars long, consisting of the
	 current time (in seconds) followed by a counter (to differentiate
	 documents opened in the same second) followed by the current pid (to
	 differentiate simultanious processes.)  This organization of the bits
	 has the effect that they are ordered the same lexicographically as by
	 creation time.

	 If name length was an issue we could cut the character count a lot by
	 printing them in base 72 [A-Za-z0-9@%-_=+.,~:].
   */
  now = time ((time_t *) 0);
  sprintf (buf2,
		   "%08X%03X%04X",
		   (unsigned int) now,
		   (unsigned int) *count,
		   (unsigned int) (getpid () & 0xFFFF));

  if (++(*count) > 4095) (*count) = 0; /* keep it 3 hex digits */

#ifdef CACHE_SUBDIRS
  if (type == xpCache)
	{
	  XP_StatStruct st;
	  char *s;
	  char *tmp = FE_CacheDir;
	  if (!tmp || !*tmp) tmp = "/tmp";
	  sprintf (buf, "%.500s", tmp);
	  if (buf [XP_STRLEN(buf)-1] != '/')
		XP_STRCAT (buf, "/");

	  s = buf + XP_STRLEN (buf);

	  value = s;		/* return a relative path! */

	  /* The name of the subdirectory is the bottom 5 bits of the time part,
		 in hex (giving a total of 32 directories.) */
	  sprintf (s, "%02X", (now & 0x1F));

	  if (XP_Stat (buf, &st, xpURL))		/* create the dir if necessary */
		XP_MakeDirectory (buf, xpCache);

	  s[2] = '/';
	  s[3] = 0;
	}
#endif /* !CACHE_SUBDIRS */

  XP_STRNCAT (value, prefix, NS_BUFFER_SIZE - XP_STRLEN(value));
  XP_STRNCAT (value, buf2, NS_BUFFER_SIZE - XP_STRLEN(value));
  value[NS_BUFFER_SIZE - 1] = '\0'; /* just in case */

  DO_TRACE(("WH_TempName called: returning: %s", value));

  return(value);
}


/* 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
 */

#define NEWSRC_PREFIX ".newsrc"
#define SNEWSRC_PREFIX ".snewsrc"

#include "nslocks.h"
#include "prnetdb.h"

PUBLIC char **
XP_GetNewsRCFiles(void)
{
	XP_Dir dir_ptr;
	XP_DirEntryStruct *dir_entry;
	char **array;
	int count=0;
	char * dirname;
	char * name;
	int len, len2;
	
	/* assume that there will never be more than 256 hosts
	 */
#define MAX_NEWSRCS 256
	array = (char **) XP_ALLOC(sizeof(char *) * (MAX_NEWSRCS+1));

	if(!array)
		return(NULL);

	XP_MEMSET(array, 0, sizeof(char *) * (MAX_NEWSRCS+1));

    name = WH_FileName ("", xpNewsRC);

	/* truncate the last slash */
	dirname = XP_STRRCHR(name, '/');
	if(dirname)
		*dirname = '\0';
	dirname = name;

	if(!dirname)
		dirname = "";

	dir_ptr = XP_OpenDir(dirname, XP_NewsRC);
	XP_FREE(name);
    if(dir_ptr == NULL)
      {
		XP_FREE(array);
		return(NULL);
      }

	len = XP_STRLEN(NEWSRC_PREFIX);
	len2 = XP_STRLEN(SNEWSRC_PREFIX);

	while((dir_entry = XP_ReadDir(dir_ptr)) != NULL
			&& count < MAX_NEWSRCS)
	  {
		char *name = dir_entry->d_name;
		char *suffix, *port;
		if (!XP_STRNCMP (name, NEWSRC_PREFIX, len))
		  suffix = name + len;
		else if (!XP_STRNCMP (name, SNEWSRC_PREFIX, len2))
		  suffix = name + len2;
		else
		  continue;

		if (suffix [strlen (suffix) - 1] == '~' ||
			suffix [strlen (suffix) - 1] == '#')
		  continue;

		if (*suffix != 0 && *suffix != '-')
		  /* accept ".newsrc" and ".newsrc-foo" but ignore
			 .newsrcSomethingElse" */
		  continue;

		if (*suffix &&
			(suffix[1] == 0 || suffix[1] == ':'))  /* reject ".newsrc-" */
		  continue;

		port = XP_STRCHR (suffix, ':');
		if (!port)
		  port = suffix + strlen (suffix);

		if (*port)
		  {
			/* If there is a "port" part, reject this file if it contains
			   non-numeric characters (meaning reject ".newsrc-host:99.tmp")
			 */
			char *s;
			for (s = port+1; *s; s++)
			  if (*s < '0' || *s > '9')
				break;
			if (*s)
			  continue;
		  }

		if (suffix != port)
		  {
			/* If there's a host name in the file, check that it is a
			   resolvable host name.

			   If this turns out to be a bad idea because of socks or
			   some other reason, then we should at least check that
			   it looks vaguely like a host name - no non-hostname
			   characters, and so on.
			 */
			char host [255];
			PRHostEnt *ok, hpbuf;
			char dbbuf[PR_NETDB_BUF_SIZE];
			if (port - suffix >= sizeof (host))
			  continue;
			strncpy (host, suffix + 1, port - suffix - 1);
			host [port - suffix - 1] = 0;
			ok = PR_gethostbyname (host, &hpbuf, dbbuf, sizeof(dbbuf), 0);
			if (!ok)
			  continue;
		  }

		StrAllocCopy(array[count++], dir_entry->d_name+1);
	  }

	XP_CloseDir (dir_ptr);
	return(array);
}

#endif /* XP_UNIX */

/*
	If we go to some sort of system-wide canonical format, routines like this make sense.
	This is intended to work on our (mail's) canonical internal file name format.
	Currently, this looks like Unix format, e.g., "/directory/subdir".
	It is NOT intended to be used on native file name formats.
	The only current use is in spool.c, to determine if a rule folder
	is an absolute or relative path (relative to mail folder directory),
	so if this gets yanked, please fix spool.c  DMB 2/13/96
*/
XP_Bool XP_FileIsFullPath(const char * name)
{
	return (*name == '/');
}

/******************************************************************************/
/* Thread-safe entry points: */

#ifndef XP_WIN

extern PRMonitor* _pr_TempName_lock;

PUBLIC char *
WH_FileName (const char *name, XP_FileType type)
{
	static char buf [1024];				/* protected by _pr_TempName_lock */
	static char configBuf [1024];		/* protected by _pr_TempName_lock */
	char* result;
#if 1 /* kill 'em all */
#ifdef NSPR
	XP_ASSERT(_pr_TempName_lock);
	PR_EnterMonitor(_pr_TempName_lock);
#endif
#endif /* 0 -- kill 'em all */
	result = xp_FileName(name, type, buf, configBuf);
	if (result)
		result = XP_STRDUP(result);
#if 1 /* kill 'em all */
#ifdef NSPR
	PR_ExitMonitor(_pr_TempName_lock);
#endif
#endif /* 0 -- kill 'em all */
	return result;
}

char * 
WH_FilePlatformName(const char * name)
{
	char* result;
	static char path[300];	/* Names longer than 300 are not dealt with in our stdio */
#if 1 /* kill 'em all */
#ifdef NSPR
	XP_ASSERT(_pr_TempName_lock);
	PR_EnterMonitor(_pr_TempName_lock);
#endif
#endif /* 0 -- kill 'em all */
	result = xp_FilePlatformName(name, path);
#if 1 /* kill 'em all */
#ifdef NSPR
	PR_ExitMonitor(_pr_TempName_lock);
#endif
#endif /* 0 -- kill 'em all */
	return result;
}

char * 
WH_TempName(XP_FileType type, const char * prefix)
{
#define NS_BUFFER_SIZE	1024
	static char buf [NS_BUFFER_SIZE];	/* protected by _pr_TempName_lock */
	static char buf2 [100];
	static unsigned int count = 0;
	char* result;
#if 1 /* kill 'em all */
#ifdef NSPR
	XP_ASSERT(_pr_TempName_lock);
	PR_EnterMonitor(_pr_TempName_lock);
#endif
#endif /* 0 -- kill 'em all */
	result = xp_TempName(type, prefix, buf, buf2, &count);
	if (result)
		result = XP_STRDUP(result);
#if 1 /* kill 'em all */
#ifdef NSPR
	PR_ExitMonitor(_pr_TempName_lock);
#endif
#endif /* 0 -- kill 'em all */
	return result;
}

#endif /* !XP_WIN */

/******************************************************************************/