/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 
 * 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 Netscape Portable Runtime (NSPR).
 * 
 * The Initial Developer of the Original Code is Netscape
 * Communications Corporation.  Portions created by Netscape are 
 * Copyright (C) 1998-2000 Netscape Communications Corporation.  All
 * Rights Reserved.
 * 
 * Contributor(s):
 * 
 * Alternatively, the contents of this file may be used under the
 * terms of the GNU General Public License Version 2 or later (the
 * "GPL"), in which case the provisions of the GPL are applicable 
 * instead of those above.  If you wish to allow use of your 
 * version of this file only under the terms of the GPL and not to
 * allow others to use your version of this file under the MPL,
 * indicate your decision by deleting the provisions above and
 * replace them with the notice and other provisions required by
 * the GPL.  If you do not delete the provisions above, a recipient
 * may use your version of this file under either the MPL or the
 * GPL.
 */

#include "nspr.h"
#include "prpriv.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef WIN32
#include <windows.h>
#include <process.h>
#endif
#if defined(_PR_PTHREADS) && !defined(_PR_DCETHREADS)
#include <pthread.h>
#endif

#if defined(XP_OS2)
#define INCL_DOSFILEMGR
#include <os2.h>
#ifdef XP_OS2_EMX
#include <getopt.h>
#include <errno.h>
#endif /* XP_OS2_EMX */
#endif /* XP_OS2 */

static int _debug_on = 0;

#ifdef XP_MAC
#include "prlog.h"
#include "primpl.h"
#define printf PR_LogPrint
#define setbuf(x,y)
extern void SetupMacPrintfLog(char *logFile);
#endif

#ifdef XP_WIN
#define mode_t int
#endif

#define DPRINTF(arg) if (_debug_on) printf arg

PRLock *lock;
PRMonitor *mon;
PRInt32 count;
int thread_count;

#ifdef WIN16
#define	BUF_DATA_SIZE	256 * 120
#else
#define	BUF_DATA_SIZE	256 * 1024
#endif

#define NUM_RDWR_THREADS	10
#define NUM_DIRTEST_THREADS	4
#define CHUNK_SIZE 512

typedef struct buffer {
	char	data[BUF_DATA_SIZE];
} buffer;

typedef struct File_Rdwr_Param {
	char	*pathname;
	char	*buf;
	int	offset;
	int	len;
} File_Rdwr_Param;

#ifdef XP_PC
#ifdef XP_OS2
char *TEST_DIR = "prdir";
#else
char *TEST_DIR = "C:\\temp\\prdir";
#endif
char *FILE_NAME = "pr_testfile";
char *HIDDEN_FILE_NAME = "hidden_pr_testfile";
#else
char *TEST_DIR = "/tmp/testfile_dir";
char *FILE_NAME = "pr_testfile";
char *HIDDEN_FILE_NAME = ".hidden_pr_testfile";
#endif
buffer *in_buf, *out_buf;
char pathname[256], renamename[256];
#define TMPDIR_LEN	64
char testdir[TMPDIR_LEN];
static PRInt32 PR_CALLBACK DirTest(void *argunused);
PRInt32 dirtest_failed = 0;

PRThread* create_new_thread(PRThreadType type,
							void (*start)(void *arg),
							void *arg,
							PRThreadPriority priority,
							PRThreadScope scope,
							PRThreadState state,
							PRUint32 stackSize, PRInt32 index)
{
PRInt32 native_thread = 0;

	PR_ASSERT(state == PR_UNJOINABLE_THREAD);

#if (defined(_PR_PTHREADS) && !defined(_PR_DCETHREADS)) || defined(WIN32) || defined(XP_OS2)

	switch(index %  4) {
		case 0:
			scope = (PR_LOCAL_THREAD);
			break;
		case 1:
			scope = (PR_GLOBAL_THREAD);
			break;
		case 2:
			scope = (PR_GLOBAL_BOUND_THREAD);
			break;
		case 3:
			native_thread = 1;
			break;
		default:
			PR_ASSERT(!"Invalid scope");
			break;
	}
	if (native_thread) {
#if defined(_PR_PTHREADS) && !defined(_PR_DCETHREADS)
		pthread_t tid;
		if (!pthread_create(&tid, NULL, start, arg))
			return((PRThread *) tid);
		else
			return (NULL);
#elif defined(XP_OS2)
        TID tid;

        tid = (TID)_beginthread((void(* _Optlink)(void*))start,
                                NULL, 32768, arg);
        if (tid == -1) {
          printf("_beginthread failed. errno %d\n", errno);
          return (NULL);
        }
        else
          return((PRThread *) tid);
#else
		HANDLE thandle;
		unsigned tid;
		
		thandle = (HANDLE) _beginthreadex(
						NULL,
						stackSize,
						(unsigned (__stdcall *)(void *))start,
						arg,
						0,
						&tid);		
		return((PRThread *) thandle);
#endif
	} else {
		return(PR_CreateThread(type,start,arg,priority,scope,state,stackSize));
	}
#else
	return(PR_CreateThread(type,start,arg,priority,scope,state,stackSize));
#endif
}

static void PR_CALLBACK File_Write(void *arg)
{
PRFileDesc *fd_file;
File_Rdwr_Param *fp = (File_Rdwr_Param *) arg;
char *name, *buf;
int offset, len;

	setbuf(stdout, NULL);
	name = fp->pathname;
	buf = fp->buf;
	offset = fp->offset;
	len = fp->len;
	
	fd_file = PR_Open(name, PR_RDWR | PR_CREATE_FILE, 0777);
	if (fd_file == NULL) {
		printf("testfile failed to create/open file %s\n",name);
		return;
	}
	if (PR_Seek(fd_file, offset, PR_SEEK_SET) < 0) {
		printf("testfile failed to seek in file %s\n",name);
		return;
	}	
	if ((PR_Write(fd_file, buf, len)) < 0) {
		printf("testfile failed to write to file %s\n",name);
		return;
	}	
	DPRINTF(("Write out_buf[0] = 0x%x\n",(*((int *) buf))));
	PR_Close(fd_file);
	PR_DELETE(fp);

	PR_EnterMonitor(mon);
	--thread_count;
	PR_Notify(mon);
	PR_ExitMonitor(mon);
}

static void PR_CALLBACK File_Read(void *arg)
{
PRFileDesc *fd_file;
File_Rdwr_Param *fp = (File_Rdwr_Param *) arg;
char *name, *buf;
int offset, len;

	setbuf(stdout, NULL);
	name = fp->pathname;
	buf = fp->buf;
	offset = fp->offset;
	len = fp->len;
	
	fd_file = PR_Open(name, PR_RDONLY, 0);
	if (fd_file == NULL) {
		printf("testfile failed to open file %s\n",name);
		return;
	}
	if (PR_Seek(fd_file, offset, PR_SEEK_SET) < 0) {
		printf("testfile failed to seek in file %s\n",name);
		return;
	}	
	if ((PR_Read(fd_file, buf, len)) < 0) {
		printf("testfile failed to read to file %s\n",name);
		return;
	}	
	DPRINTF(("Read in_buf[0] = 0x%x\n",(*((int *) buf))));
	PR_Close(fd_file);
	PR_DELETE(fp);

	PR_EnterMonitor(mon);
	--thread_count;
	PR_Notify(mon);
	PR_ExitMonitor(mon);
}


static PRInt32 Misc_File_Tests(char *pathname)
{
PRFileDesc *fd_file;
int len, rv = 0;
PRFileInfo file_info, file_info1;
char tmpname[1024];

	setbuf(stdout, NULL);
	/*
	 * Test PR_Available, PR_Seek, PR_GetFileInfo, PR_Rename, PR_Access
	 */

	fd_file = PR_Open(pathname, PR_RDWR | PR_CREATE_FILE, 0777);

	if (fd_file == NULL) {
		printf("testfile failed to create/open file %s\n",pathname);
		return -1;
	}
	if (PR_GetOpenFileInfo(fd_file, &file_info) < 0) {
		printf("testfile PR_GetFileInfo failed on file %s\n",pathname);
		rv = -1;
		goto cleanup;
	}
	if (PR_Access(pathname, PR_ACCESS_EXISTS) != 0) {
		printf("testfile PR_Access failed on file %s\n",pathname);
		rv = -1;
		goto cleanup;
	}
	if (PR_Access(pathname, PR_ACCESS_WRITE_OK) != 0) {
		printf("testfile PR_Access failed on file %s\n",pathname);
		rv = -1;
		goto cleanup;
	}
	if (PR_Access(pathname, PR_ACCESS_READ_OK) != 0) {
		printf("testfile PR_Access failed on file %s\n",pathname);
		rv = -1;
		goto cleanup;
	}


	if (PR_GetFileInfo(pathname, &file_info) < 0) {
		printf("testfile PR_GetFileInfo failed on file %s\n",pathname);
		rv = -1;
		goto cleanup;
	}
	if (file_info.type != PR_FILE_FILE) {
	printf(
	"testfile: Error - PR_GetFileInfo returned incorrect type for file %s\n",
		pathname);
		rv = -1;
		goto cleanup;
	}
	if (file_info.size != 0) {
		printf(
		"testfile PR_GetFileInfo returned incorrect size (%d should be 0) for file %s\n",
		file_info.size, pathname);
		rv = -1;
		goto cleanup;
	}
	file_info1 = file_info;

	len = PR_Available(fd_file);
	if (len < 0) {
		printf("testfile PR_Available failed on file %s\n",pathname);
		rv = -1;
		goto cleanup;
	} else if (len != 0) {
		printf(
		"testfile PR_Available failed: expected/returned = %d/%d bytes\n",
			0, len);
		rv = -1;
		goto cleanup;
	}
	if (PR_GetOpenFileInfo(fd_file, &file_info) < 0) {
		printf("testfile PR_GetFileInfo failed on file %s\n",pathname);
		goto cleanup;
	}
	if (LL_NE(file_info.creationTime , file_info1.creationTime)) {
		printf(
		"testfile PR_GetFileInfo returned incorrect status-change time: %s\n",
		pathname);
		printf("ft = %lld, ft1 = %lld\n",file_info.creationTime,
									file_info1.creationTime);
		rv = -1;
		goto cleanup;
	}
	len = PR_Write(fd_file, out_buf->data, CHUNK_SIZE);
	if (len < 0) {
		printf("testfile failed to write to file %s\n",pathname);
		rv = -1;
		goto cleanup;
	}
	if (PR_GetOpenFileInfo(fd_file, &file_info) < 0) {
		printf("testfile PR_GetFileInfo failed on file %s\n",pathname);
		goto cleanup;
	}
	if (file_info.size != CHUNK_SIZE) {
		printf(
		"testfile PR_GetFileInfo returned incorrect size (%d should be %d) for file %s\n",
		file_info.size, CHUNK_SIZE, pathname);
		rv = -1;
		goto cleanup;
	}
	if (LL_CMP(file_info.modifyTime, < , file_info1.modifyTime)) {
		printf(
		"testfile PR_GetFileInfo returned incorrect modify time: %s\n",
		pathname);
		printf("ft = %lld, ft1 = %lld\n",file_info.modifyTime,
									file_info1.modifyTime);
		rv = -1;
		goto cleanup;
	}

	len = PR_Available(fd_file);
	if (len < 0) {
		printf("testfile PR_Available failed on file %s\n",pathname);
		rv = -1;
		goto cleanup;
	} else if (len != 0) {
		printf(
		"testfile PR_Available failed: expected/returned = %d/%d bytes\n",
			0, len);
		rv = -1;
		goto cleanup;
	}
	
	PR_Seek(fd_file, 0, PR_SEEK_SET);
	len = PR_Available(fd_file);
	if (len < 0) {
		printf("testfile PR_Available failed on file %s\n",pathname);
		rv = -1;
		goto cleanup;
	} else if (len != CHUNK_SIZE) {
		printf(
		"testfile PR_Available failed: expected/returned = %d/%d bytes\n",
			CHUNK_SIZE, len);
		rv = -1;
		goto cleanup;
	}
    PR_Close(fd_file);

	strcpy(tmpname,pathname);
	strcat(tmpname,".RENAMED");
	if (PR_FAILURE == PR_Rename(pathname, tmpname)) {
		printf("testfile failed to rename file %s\n",pathname);
		rv = -1;
		goto cleanup;
	}

	fd_file = PR_Open(pathname, PR_RDWR | PR_CREATE_FILE, 0777);
	len = PR_Write(fd_file, out_buf->data, CHUNK_SIZE);
    PR_Close(fd_file);
	if (PR_SUCCESS == PR_Rename(pathname, tmpname)) {
		printf("testfile renamed to existing file %s\n",pathname);
	}

	if ((PR_Delete(tmpname)) < 0) {
		printf("testfile failed to unlink file %s\n",tmpname);
		rv = -1;
	}

cleanup:
	if ((PR_Delete(pathname)) < 0) {
		printf("testfile failed to unlink file %s\n",pathname);
		rv = -1;
	}
	return rv;
}


static PRInt32 PR_CALLBACK FileTest(void)
{
PRDir *fd_dir;
int i, offset, len, rv = 0;
PRThread *t;
PRThreadScope scope = PR_GLOBAL_THREAD;
File_Rdwr_Param *fparamp;

	/*
	 * Create Test dir
	 */
	if ((PR_MkDir(TEST_DIR, 0777)) < 0) {
		printf("testfile failed to create dir %s\n",TEST_DIR);
		return -1;
	}
	fd_dir = PR_OpenDir(TEST_DIR);
	if (fd_dir == NULL) {
		printf("testfile failed to open dir %s\n",TEST_DIR);
		rv =  -1;
		goto cleanup;	
	}

    PR_CloseDir(fd_dir);

	strcat(pathname, TEST_DIR);
	strcat(pathname, "/");
	strcat(pathname, FILE_NAME);

	in_buf = PR_NEW(buffer);
	if (in_buf == NULL) {
		printf(
		"testfile failed to alloc buffer struct\n");
		rv =  -1;
		goto cleanup;	
	}
	out_buf = PR_NEW(buffer);
	if (out_buf == NULL) {
		printf(
		"testfile failed to alloc buffer struct\n");
		rv =  -1;
		goto cleanup;	
	}

	/*
	 * Start a bunch of writer threads
	 */
	offset = 0;
	len = CHUNK_SIZE;
	PR_EnterMonitor(mon);
	for (i = 0; i < NUM_RDWR_THREADS; i++) {
		fparamp = PR_NEW(File_Rdwr_Param);
		if (fparamp == NULL) {
			printf(
			"testfile failed to alloc File_Rdwr_Param struct\n");
			rv =  -1;
			goto cleanup;	
		}
		fparamp->pathname = pathname;
		fparamp->buf = out_buf->data + offset;
		fparamp->offset = offset;
		fparamp->len = len;
		memset(fparamp->buf, i, len);

		t = create_new_thread(PR_USER_THREAD,
			      File_Write, (void *)fparamp, 
			      PR_PRIORITY_NORMAL,
			      scope,
			      PR_UNJOINABLE_THREAD,
			      0, i);
		offset += len;
	}
	thread_count = i;
	/* Wait for writer threads to exit */
	while (thread_count) {
		PR_Wait(mon, PR_INTERVAL_NO_TIMEOUT);
	}
	PR_ExitMonitor(mon);


	/*
	 * Start a bunch of reader threads
	 */
	offset = 0;
	len = CHUNK_SIZE;
	PR_EnterMonitor(mon);
	for (i = 0; i < NUM_RDWR_THREADS; i++) {
		fparamp = PR_NEW(File_Rdwr_Param);
		if (fparamp == NULL) {
			printf(
			"testfile failed to alloc File_Rdwr_Param struct\n");
			rv =  -1;
			goto cleanup;	
		}
		fparamp->pathname = pathname;
		fparamp->buf = in_buf->data + offset;
		fparamp->offset = offset;
		fparamp->len = len;

		t = create_new_thread(PR_USER_THREAD,
			      File_Read, (void *)fparamp, 
			      PR_PRIORITY_NORMAL,
			      scope,
			      PR_UNJOINABLE_THREAD,
			      0, i);
		offset += len;
		if ((offset + len) > BUF_DATA_SIZE)
			break;
	}
	thread_count = i;

	/* Wait for reader threads to exit */
	while (thread_count) {
		PR_Wait(mon, PR_INTERVAL_NO_TIMEOUT);
	}
	PR_ExitMonitor(mon);

	if (memcmp(in_buf->data, out_buf->data, offset) != 0) {
		printf("File Test failed: file data corrupted\n");
		rv =  -1;
		goto cleanup;	
	}

	if ((PR_Delete(pathname)) < 0) {
		printf("testfile failed to unlink file %s\n",pathname);
		rv =  -1;
		goto cleanup;	
	}

	/*
	 * Test PR_Available, PR_Seek, PR_GetFileInfo, PR_Rename, PR_Access
	 */
	if (Misc_File_Tests(pathname) < 0) {
		rv = -1;
	}

cleanup:
	if ((PR_RmDir(TEST_DIR)) < 0) {
		printf("testfile failed to rmdir %s\n", TEST_DIR);
		rv = -1;
	}
	return rv;
}

struct dirtest_arg {
	PRMonitor	*mon;
	PRInt32		done;
};

static PRInt32 RunDirTest(void)
{
int i;
PRThread *t;
PRMonitor *mon;
struct dirtest_arg thrarg;

	mon = PR_NewMonitor();
	if (!mon) {
		printf("RunDirTest: Error - failed to create monitor\n");
		dirtest_failed = 1;
		return -1;
	}
	thrarg.mon = mon;

	for (i = 0; i < NUM_DIRTEST_THREADS; i++) {

		thrarg.done= 0;
		t = create_new_thread(PR_USER_THREAD,
			      DirTest, &thrarg, 
			      PR_PRIORITY_NORMAL,
			      PR_LOCAL_THREAD,
			      PR_UNJOINABLE_THREAD,
			      0, i);
		if (!t) {
			printf("RunDirTest: Error - failed to create thread\n");
			dirtest_failed = 1;
			return -1;
		}
		PR_EnterMonitor(mon);
		while (!thrarg.done)
			PR_Wait(mon, PR_INTERVAL_NO_TIMEOUT);
		PR_ExitMonitor(mon);

	}
	PR_DestroyMonitor(mon);
	return 0;
}

static PRInt32 PR_CALLBACK DirTest(void *arg)
{
struct dirtest_arg *tinfo = (struct dirtest_arg *) arg;
PRFileDesc *fd_file;
PRDir *fd_dir;
int i;
int path_len;
PRDirEntry *dirEntry;
PRFileInfo info;
PRInt32 num_files = 0;
#if defined(XP_PC) && defined(WIN32)
HANDLE hfile;
#endif

#define  FILES_IN_DIR 20

	/*
	 * Create Test dir
	 */
	DPRINTF(("Creating test dir %s\n",TEST_DIR));
	if ((PR_MkDir(TEST_DIR, 0777)) < 0) {
		printf(
			"testfile failed to create dir %s [%d, %d]\n",
			TEST_DIR, PR_GetError(), PR_GetOSError());
		return -1;
	}
	fd_dir = PR_OpenDir(TEST_DIR);
	if (fd_dir == NULL) {
		printf(
			"testfile failed to open dirctory %s [%d, %d]\n",
			TEST_DIR, PR_GetError(), PR_GetOSError());
		return -1;
	}

	strcpy(pathname, TEST_DIR);
	strcat(pathname, "/");
	strcat(pathname, FILE_NAME);
	path_len = strlen(pathname);
	
	for (i = 0; i < FILES_IN_DIR; i++) {

		sprintf(pathname + path_len,"%d%s",i,"");

		DPRINTF(("Creating test file %s\n",pathname));

		fd_file = PR_Open(pathname, PR_RDWR | PR_CREATE_FILE, 0777);

		if (fd_file == NULL) {
			printf(
					"testfile failed to create/open file %s [%d, %d]\n",
					pathname, PR_GetError(), PR_GetOSError());
			return -1;
		}
        PR_Close(fd_file);
	}
#if defined(XP_UNIX) || defined(XP_MAC) || (defined(XP_PC) && defined(WIN32)) || defined(XP_OS2) || defined(XP_BEOS)
	/*
	 * Create a hidden file - a platform-dependent operation
	 */
	strcpy(pathname, TEST_DIR);
	strcat(pathname, "/");
	strcat(pathname, HIDDEN_FILE_NAME);
#if defined(XP_UNIX) || defined(XP_MAC) || defined(XP_BEOS)
	DPRINTF(("Creating hidden test file %s\n",pathname));
	fd_file = PR_Open(pathname, PR_RDWR | PR_CREATE_FILE, 0777);

	if (fd_file == NULL) {
		printf(
				"testfile failed to create/open hidden file %s [%d, %d]\n",
				pathname, PR_GetError(), PR_GetOSError());
		return -1;
	}

#if defined(XP_MAC)
	{
#include <files.h>

	OSErr			err;
	FCBPBRec		fcbpb;
	CInfoPBRec		pb;
	Str255			pascalMacPath;

	fcbpb.ioNamePtr = pascalMacPath;
	fcbpb.ioVRefNum = 0;
	fcbpb.ioRefNum = fd_file->secret->md.osfd;
	fcbpb.ioFCBIndx = 0;
	
	err = PBGetFCBInfoSync(&fcbpb);
	if (err != noErr) {
    	PR_Close(fd_file);
    	return -1;
	}
	
	pb.hFileInfo.ioNamePtr = pascalMacPath;
	pb.hFileInfo.ioVRefNum = fcbpb.ioFCBVRefNum;
	pb.hFileInfo.ioDirID = fcbpb.ioFCBParID;
	pb.hFileInfo.ioFDirIndex = 0;
	
	err = PBGetCatInfoSync(&pb);
	if (err != noErr) {
    	PR_Close(fd_file);
    	return -1;
	}

	pb.hFileInfo.ioNamePtr = pascalMacPath;
	pb.hFileInfo.ioVRefNum = fcbpb.ioFCBVRefNum;
	pb.hFileInfo.ioDirID = fcbpb.ioFCBParID;
	pb.hFileInfo.ioFDirIndex = 0;
	
	pb.hFileInfo.ioFlFndrInfo.fdFlags |= fInvisible;

	err = PBSetCatInfoSync(&pb);
	if (err != noErr) {
    	PR_Close(fd_file);
    	return -1;
	}

	}
#endif

    PR_Close(fd_file);

	
#elif defined(XP_PC) && defined(WIN32)
	DPRINTF(("Creating hidden test file %s\n",pathname));
	hfile = CreateFile(pathname, GENERIC_READ,
						FILE_SHARE_READ|FILE_SHARE_WRITE,
						NULL,
						CREATE_NEW,
						FILE_ATTRIBUTE_HIDDEN,
						NULL);
	if (hfile == INVALID_HANDLE_VALUE) {
		printf("testfile failed to create/open hidden file %s [0, %d]\n",
				pathname, GetLastError());
		return -1;
	}
	CloseHandle(hfile);
						
#elif defined(OS2)
	DPRINTF(("Creating hidden test file %s\n",pathname));
	fd_file = PR_Open(pathname, PR_RDWR | PR_CREATE_FILE, (int)FILE_HIDDEN);

	if (fd_file == NULL) {
		printf("testfile failed to create/open hidden file %s [%d, %d]\n",
				pathname, PR_GetError(), PR_GetOSError());
		return -1;
	}
	PR_Close(fd_file);
#endif	/* XP _UNIX || XP_MAC*/

#endif	/* XP_UNIX || XP_MAC ||(XP_PC && WIN32) */


	if (PR_FAILURE == PR_CloseDir(fd_dir))
	{
		printf(
			"testfile failed to close dirctory %s [%d, %d]\n",
			TEST_DIR, PR_GetError(), PR_GetOSError());
		return -1;
	}
	fd_dir = PR_OpenDir(TEST_DIR);
	if (fd_dir == NULL) {
		printf(
			"testfile failed to reopen dirctory %s [%d, %d]\n",
			TEST_DIR, PR_GetError(), PR_GetOSError());
		return -1;
	}
  
	/*
	 * List all files, including hidden files
	 */
	DPRINTF(("Listing all files in directory %s\n",TEST_DIR));
#if defined(XP_UNIX) || defined(XP_MAC) || (defined(XP_PC) && defined(WIN32)) || defined(XP_OS2) || defined(XP_BEOS)
	num_files = FILES_IN_DIR + 1;
#else
	num_files = FILES_IN_DIR;
#endif
	while ((dirEntry = PR_ReadDir(fd_dir, PR_SKIP_BOTH)) != NULL) {
		num_files--;
		strcpy(pathname, TEST_DIR);
		strcat(pathname, "/");
		strcat(pathname, dirEntry->name);
		DPRINTF(("\t%s\n",dirEntry->name));

		if ((PR_GetFileInfo(pathname, &info)) < 0) {
			printf(
				"testfile failed to GetFileInfo file %s [%d, %d]\n",
				pathname, PR_GetError(), PR_GetOSError());
			return -1;
		}
		
		if (info.type != PR_FILE_FILE) {
			printf(
				"testfile incorrect fileinfo for file %s [%d, %d]\n",
				pathname, PR_GetError(), PR_GetOSError());
			return -1;
		}
	}
	if (num_files != 0)
	{
		printf(
			"testfile failed to find all files in directory %s [%d, %d]\n",
			TEST_DIR, PR_GetError(), PR_GetOSError());
		return -1;
	}

    PR_CloseDir(fd_dir);

#if defined(XP_UNIX) || defined(XP_MAC) || (defined(XP_PC) && defined(WIN32)) || defined(XP_OS2) || defined(XP_BEOS)

	/*
	 * List all files, except hidden files
	 */

	fd_dir = PR_OpenDir(TEST_DIR);
	if (fd_dir == NULL) {
		printf(
			"testfile failed to reopen dirctory %s [%d, %d]\n",
			TEST_DIR, PR_GetError(), PR_GetOSError());
		return -1;
	}
  
	DPRINTF(("Listing non-hidden files in directory %s\n",TEST_DIR));
	while ((dirEntry = PR_ReadDir(fd_dir, PR_SKIP_HIDDEN)) != NULL) {
		DPRINTF(("\t%s\n",dirEntry->name));
		if (!strcmp(HIDDEN_FILE_NAME, dirEntry->name)) {
			printf("testfile found hidden file %s\n", pathname);
			return -1;
		}

	}
	/*
	 * Delete hidden file
	 */
	strcpy(pathname, TEST_DIR);
	strcat(pathname, "/");
	strcat(pathname, HIDDEN_FILE_NAME);
	if (PR_FAILURE == PR_Delete(pathname)) {
		printf(
			"testfile failed to delete hidden file %s [%d, %d]\n",
			pathname, PR_GetError(), PR_GetOSError());
		return -1;
	}

    PR_CloseDir(fd_dir);
#endif	/* XP_UNIX || XP_MAC || (XP_PC && WIN32) */

	strcpy(renamename, TEST_DIR);
	strcat(renamename, ".RENAMED");
	if (PR_FAILURE == PR_Rename(TEST_DIR, renamename)) {
		printf(
			"testfile failed to rename directory %s [%d, %d]\n",
			TEST_DIR, PR_GetError(), PR_GetOSError());
		return -1;
	}
    
	if (PR_FAILURE == PR_MkDir(TEST_DIR, 0777)) {
		printf(
			"testfile failed to recreate dir %s [%d, %d]\n",
			TEST_DIR, PR_GetError(), PR_GetOSError());
		return -1;
	}
	if (PR_SUCCESS == PR_Rename(renamename, TEST_DIR)) {
		printf(
			"testfile renamed directory to existing name %s\n",
			renamename);
		return -1;
	}

	if (PR_FAILURE == PR_RmDir(TEST_DIR)) {
		printf(
			"testfile failed to rmdir %s [%d, %d]\n",
			TEST_DIR, PR_GetError(), PR_GetOSError());
		return -1;
	}

	if (PR_FAILURE == PR_Rename(renamename, TEST_DIR)) {
		printf(
			"testfile failed to rename directory %s [%d, %d]\n",
			renamename, PR_GetError(), PR_GetOSError());
		return -1;
	}
	fd_dir = PR_OpenDir(TEST_DIR);
	if (fd_dir == NULL) {
		printf(
			"testfile failed to reopen directory %s [%d, %d]\n",
			TEST_DIR, PR_GetError(), PR_GetOSError());
		return -1;
	}

	strcpy(pathname, TEST_DIR);
	strcat(pathname, "/");
	strcat(pathname, FILE_NAME);
	path_len = strlen(pathname);
	
	for (i = 0; i < FILES_IN_DIR; i++) {

		sprintf(pathname + path_len,"%d%s",i,"");

		if (PR_FAILURE == PR_Delete(pathname)) {
			printf(
				"testfile failed to delete file %s [%d, %d]\n",
				pathname, PR_GetError(), PR_GetOSError());
			return -1;
		}
	}

    PR_CloseDir(fd_dir);

	if (PR_FAILURE == PR_RmDir(TEST_DIR)) {
		printf(
			"testfile failed to rmdir %s [%d, %d]\n",
			TEST_DIR, PR_GetError(), PR_GetOSError());
		return -1;
	}
	PR_EnterMonitor(tinfo->mon);
	tinfo->done = 1;
	PR_Notify(tinfo->mon);
	PR_ExitMonitor(tinfo->mon);

	return 0;
}
/************************************************************************/

/*
 * Test file and directory NSPR APIs
 */

int main(int argc, char **argv)
{
#ifdef WIN32
	PRUint32 len;
#endif
#if defined(XP_UNIX) || defined(XP_OS2_EMX)
        int opt;
        extern char *optarg;
	extern int optind;
#endif
#if defined(XP_UNIX) || defined(XP_OS2_EMX)
        while ((opt = getopt(argc, argv, "d")) != EOF) {
                switch(opt) {
                        case 'd':
                                _debug_on = 1;
                                break;
                        default:
                                break;
                }
        }
#endif
	PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0);
    PR_STDIO_INIT();

#ifdef XP_MAC
	SetupMacPrintfLog("testfile.log");
#endif

	mon = PR_NewMonitor();
	if (mon == NULL) {
		printf("testfile: PR_NewMonitor failed\n");
		exit(2);
	}
#ifdef WIN32
	len = GetTempPath(TMPDIR_LEN, testdir);
	if ((len > 0) && (len < (TMPDIR_LEN - 6))) {
		/*
		 * enough space for prdir
		 */
		strcpy((testdir + len),"prdir");
		TEST_DIR = testdir;
		printf("TEST_DIR = %s\n",TEST_DIR);
	}
	
#endif

	if (FileTest() < 0) {
		printf("File Test failed\n");
		exit(2);
	}
	printf("File Test passed\n");
	if ((RunDirTest() < 0) || dirtest_failed) {
		printf("Dir Test failed\n");
		exit(2);
	}
	printf("Dir Test passed\n");

	PR_DestroyMonitor(mon);
	PR_Cleanup();
    return 0;
}
