/*
* Copyright (c) 2005-2009 Nokia Corporation and/or its subsidiary(-ies).
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of "Eclipse Public License v1.0"
* which accompanies this distribution, and is available
* at the URL "http://www.eclipse.org/legal/epl-v10.html".
*
* Initial Contributors:
* Nokia Corporation - initial contribution.
*
* Contributors:
*
* Description:
*
*/

// connectors for re-entrant networking system calls

#include <sys/errno.h>
#include "sysreent.h"
#include "aeselectreent.h"
#include <unistd.h>
#include <fcntl.h>		// for open()
#include <sys/ioctl.h>
#include <stdarg.h> 
#include <utf.h>
#include <unistd.h>
#include <sys/types.h>
#include <spawn.h>
#include <charconv.h>
#include "sys/stat.h"
#include <wchar.h>
#include "systemspecialfilercg.h"
#include "link.h"
#include "errno.h"

#include "sysif.h"
#include "lposix.h"



#define	MAXPATHLEN	256	/* E32STD.H: KMaxFullName */

TInt __EPOC32_WDIR::UpdateNarrow()
	{
	return -1;		//this is not supported.
	}
	
TInt __EPOC32_DIR::UpdateNarrow()
	{
	//update the narrow one
	
	TInt ret = CnvUtfConverter::ConvertFromUnicodeToUtf8(iCurrentNarrowName, iCurrentName);
	if (ret >= 0)
		{
		iCurrentNarrow.d_namlen=(short)iCurrentNarrowName.Length();
		memcpy(iCurrentNarrow.d_name,iCurrentNarrowName.PtrZ(),iCurrentNarrowName.Length() + 1);
		}
	return ret;
	}

__EPOC32_WDIR::~__EPOC32_WDIR()
	{
	delete iPath;
	delete iEntries;
	}

TInt __EPOC32_WDIR::Open(const wchar_t* _path,int* aErrno)
	{
	TParse name;
	TInt err = Backend()->ResolvePath(name,_path,NULL);
	if (!err)
		err = Open(name.DriveAndPath());
	if (err)
		MapError(err,*aErrno);
	return err;
	}

TInt __EPOC32_WDIR::Open(const TDesC& aPath)
	{
	delete iPath;
	iPath=aPath.Alloc();		//allocate space for a hbufc and initialise it to the TDesC contents
	return Open();
	}

TInt __EPOC32_WDIR::Open()
	{
	delete iEntries;
	iEntries=0;
	iIndex = 0;
	iCount = 0 ;
	if (iPath==0)
		{
		return ENOMEM;
		}
		
	RFs& session = Backend()->FileSession();
	TInt err=session.GetDir(*iPath,KEntryAttMaskSupported,ESortByName+EDescending,iEntries);
		
	if (!err) 
		{
		// iCount is actually a Last Index, not the number of entries!
		iCount = iEntries->Count()-1;
		iIndex = 0;
		}
	return err;
	}

extern "C" {

/* A reentrant version of open() */
EXPORT_C int _open_r (int *aErrno, const wchar_t *name, int mode, int perms)
	{	
	return Backend()->open(name, mode, perms, *aErrno);
	}

/*A reentrant version of wopen() */
EXPORT_C int _wopen_r (int *aErrno, const wchar_t *name, int mode, int perms)
	{
	return Backend()->open(name, mode, perms, *aErrno);
	}

/* A reentrant version of read()*/
EXPORT_C int _read_r (int *aErrno, int fd, char *buf, size_t nbyte)
	{
	return Backend()->read(fd, buf, nbyte, *aErrno);
	}


/* A reentrant version of write()*/
EXPORT_C int _write_r (int *aErrno, int fd, const char *buf, size_t nbyte)
	{
	return Backend()->write(fd, buf, nbyte, *aErrno);
	}


/* A reentrant version of close()*/
EXPORT_C int _close_r (int *aErrno, int fd)
	{
	return Backend()->close(fd, *aErrno);
	}

/* A reentrant version of fsync()*/
EXPORT_C int _fsync_r (int *aErrno, int fd)
	{
	return Backend()->fsync(fd, *aErrno);
	}

/*A reentrant version of fseek()*/
EXPORT_C off_t _lseek_r (int *aErrno, int fd, off_t pos, int whence)
	{
	return Backend()->lseek(fd, pos, whence, *aErrno);
	}


/* A reentrant version of fstat()*/
EXPORT_C int _fstat_r (int *aErrno, int fd, struct stat *st) 
	{
	return Backend()->fstat(fd, st, *aErrno);
	}

/* A reentrant version of stat().
*/
EXPORT_C int _stat_r (int *aErrno, const wchar_t *name, struct stat *st) 
	{
	return Backend()->stat(name, st, *aErrno);
	}
	
EXPORT_C int _utime_r (int *aErrno, const wchar_t *name,const  struct utimbuf *filetimes) 
	{
	return Backend()->utime(name, filetimes, *aErrno);
	}


/* A reentrant version of wstat().
*/
EXPORT_C int _wstat_r (int *aErrno, const wchar_t *name, struct stat *st) 
	{
	return Backend()->stat(name, st, *aErrno);
	}

/* A reentrant version of dup().
*/
EXPORT_C int _dup_r (int *aErrno, int aFid)
	{
	return Backend()->dup(aFid, *aErrno);
	}

/* A reentrant version of dup2().
*/
EXPORT_C int _dup2_r (int *aErrno, int aFid1, int aFid2)
	{
	return Backend()->dup2(aFid1, aFid2, *aErrno);
	}

/* A reentrant version of ioctl().
*/
EXPORT_C int _ioctl_r (int *aErrno, int aFid, int aCmd, void* aParam)
	{
	return Backend()->ioctl(aFid, aCmd, aParam, *aErrno);
	}

/* A wide-character version of reentrant of getcwd().
*/
EXPORT_C wchar_t * _wgetcwd_r (int *aErrno, wchar_t *_buf, size_t _size)
	{
	if (_buf==0)
		{
		_buf=(wchar_t *)User::Alloc(_size*sizeof(wchar_t));
		if (_buf==0)
			{
			*aErrno=ENOMEM;
			return _buf;
			}
		}
	return Backend()->getcwd(_buf, _size, *aErrno);
	}

/* A reentrant version of chdir().
*/
EXPORT_C int _chdir_r (int *aErrno, const wchar_t *_path)
	{
	return Backend()->chdir(_path, *aErrno);
	}

/* A reentrant version of wchdir().
*/
EXPORT_C int _wchdir_r (int *aErrno, const wchar_t *_path)
	{
	return Backend()->chdir(_path, *aErrno);
	}

/* A reentrant version of rmdir().
*/
EXPORT_C int _rmdir_r (int *aErrno, const wchar_t *_path)
	{
	 return Backend()->rmdir(_path, *aErrno);
	}


/* A reentrant version of wrmdir().
*/
EXPORT_C int _wrmdir_r (int *aErrno, const wchar_t *_path)
	{
	return Backend()->rmdir(_path, *aErrno);
	}


/* A reentrant version of mkdir().
*/
EXPORT_C int _mkdir_r (int *aErrno, const wchar_t *_path, mode_t _mode)
	{
	return Backend()->mkdir(_path, _mode, *aErrno);
	}

/* A reentrant version of wmkdir().
*/
EXPORT_C int _wmkdir_r (int *aErrno, const wchar_t *_path, mode_t _mode)
	{
	return Backend()->mkdir(_path, _mode, *aErrno);
	}


/* A reentrant version of chmod().
*/
EXPORT_C int _chmod_r (int *aErrno, const wchar_t *_path, mode_t _mode)
	{
	return Backend()->chmod(_path, _mode, *aErrno);
	}

/* A reentrant version of chmod().
*/
EXPORT_C int _fchmod_r (int *aErrno, int _fd , mode_t _mode)
	{
	return Backend()->fchmod(_fd, _mode, *aErrno);
	}
/* A reentrant version of wchmod().
*/
EXPORT_C int _wchmod_r (int *aErrno, const wchar_t *_path, mode_t _mode)
	{
	return Backend()->chmod(_path, _mode, *aErrno);
	}

/* A wide-character version of reentrant of unlink().
*/
EXPORT_C int _wunlink_r (int *aErrno, const wchar_t *_path)
	{
	return Backend()->unlink(_path, *aErrno);
	}


/* A reentrant version of rename().
*/
EXPORT_C int _rename_r (int *aErrno, const wchar_t *oldpath, const wchar_t *newpath)
	{
	return Backend()->rename(oldpath, newpath, *aErrno);
	}


/* A wide-character version of reentrant of rename().
*/
EXPORT_C int _wrename_r (int *aErrno, const wchar_t *oldpath, const wchar_t *newpath)
	{
	return Backend()->rename(oldpath, newpath, *aErrno);
	}

/* A wide-character version of reentrant of realpath().
*/
EXPORT_C wchar_t * _wrealpath_r (int *aErrno, const wchar_t *relpath, wchar_t *resolved)
	{
		if(relpath == NULL || resolved == NULL)   //invalid arguments
			{
				*aErrno = EFAULT;
				return (NULL);
			}
	TPtr16 name((TText16*)resolved,MAXPATHLEN);
	TParse path;
	TInt err = Backend()->ResolvePath(path, relpath, &name);
	if (!err)
		{
		err = path.SetNoWild(path.DriveAndPath(),NULL,&name);
		if (!err)
			{
			name = path.FullName();
			name.ZeroTerminate();
			return resolved;
			}
		}
	MapError(err, *aErrno);
	return 0;
	}

LOCAL_C TInt _parseCmd (const wchar_t* command, wchar_t* exepath, TDes& buf)
	{
	TLex lexer((TText*)command);
	buf = lexer.NextToken();
	WcsCpy(exepath,(wchar_t*)buf.PtrZ());

	lexer.SkipSpace();
	if (!lexer.Eos())
		{
		buf = lexer.Remainder();
		buf.TrimRight();
		}
	return KErrNone;
	}

EXPORT_C int _wpopen_r (int* aErrno, const wchar_t* command, const char* mode)
	{
	wchar_t exepath[KMaxPath+1];
	TFileName buf;

	if (_parseCmd(command, exepath, buf) == KErrNotFound)
		{
		// no such file
		*aErrno = ENOENT;
		return -1;
		}
		
	return Backend()->popen(exepath, (wchar_t*)buf.PtrZ(), mode, *aErrno);
	}

EXPORT_C int _pclose_r(int* aErrno, int aFid)
	{
	return Backend()->pclose(aFid, *aErrno);
	}

/* A wide-character version of reentrant of popen3().
*/
EXPORT_C int _wpopen3_r (int* aErrno, const wchar_t* file, const wchar_t* cmd, 
						 wchar_t** envp, int fids[3])
	{
			
	wchar_t* cmdargs = NULL;
	if (cmd)
		{
		TFileName buf = (TText*)cmd;
		buf.Trim();
		cmdargs = (wchar_t*)buf.PtrZ();
		}

	return Backend()->popen3(file, cmdargs, envp, fids, *aErrno);
	}


/* A reentrant version of waitpid().
*/
EXPORT_C int _waitpid_r (int *aErrno, int pid, int* status, int options)
	{
	return Backend()->waitpid(pid, status, options, *aErrno);
	}


/* A reentrant version of wait().
*/
EXPORT_C int _wait_r (int *aErrno, int* status)
	{
	return _waitpid_r(aErrno, -1, status, 0);
	}


/* A wide-character version of reentrant of system().
*/
EXPORT_C int _wsystem_r (int *aErrno, const wchar_t* command)
	{
	wchar_t exepath[KMaxPath+1];
	TFileName buf;		
	
	if (NULL == command)
		{
		return 1;	// special case, says that we do support wsystem().
		}		
		
	if (_parseCmd(command, (wchar_t*)exepath, buf) == KErrNotFound)
		{
		// no such file
		*aErrno = ENOENT;
		return -1;
		}
	
	return Backend()->system(exepath, (wchar_t*)buf.PtrZ(), *aErrno);
	}


/* A reentrant version of select().
*/
EXPORT_C int _select_r(int *aErrno, int maxfd, fd_set *readfds, fd_set *writefds,
					   fd_set *exceptfds, struct timeval *tvptr)
	{
	return Backend()->select(maxfd, readfds, writefds, exceptfds, tvptr, *aErrno);
	}	


EXPORT_C int _fchdir_r(int *aErrno, int filedesc)
	{
	struct stat st;
	
	if(_fstat_r(aErrno, filedesc, &st)!=0)
		{
		*aErrno  = EBADF ;//Not an open file descriptor
		return -1;
		}
	else 
		{
		if (!(S_ISDIR(st.st_mode)))
			{
			*aErrno = ENOTDIR ; //Not a directory
			return -1;
			}
		}
		
	const wchar_t* dirName(NULL) ;
	dirName = Backend()->GetDirName (filedesc);

	if( dirName )
		{
		return Backend()->chdir(dirName, *aErrno);
		}
	else
		{
		//fildesc does not refer to a valid fd.
		*aErrno = ENOTDIR;
		return -1;
		}
	}


/*
Gets the path name of the current working directory.
If a buffer is specified, the path name is placed in that buffer,
and the address of the buffer is returned. 
@return If successful returns buf, if a non-null pointer was specified, 
or the address of the allocated memory otherwise.
@param anErrno Ref to the error no. 
@param _buf Points to the buffer to copy the current working directory to, 
or NULL if getcwd() should allocate the buffer.
@param _size Is the size, in bytes, of the array of characters that buf points to.
*/
/* A reentrant version of getcwd()*/
EXPORT_C wchar_t * _getcwd_r (int *aErrno, wchar_t *_buf, size_t _size)
	{
	return Backend()->getcwd(_buf, _size, *aErrno);
	}

// -----------------------------------------------------------------------------
// Function name: link
// Description: Provides link functionality.
// Returns:  0    : On success
//          -1    : On error
// In case of error, errno value set
// Remark: This is a simulated functionality and not supported by the platform     
// -----------------------------------------------------------------------------
//

EXPORT_C int _link_r(int *aErrno, const wchar_t *_oldwidename, const wchar_t *_newwidename)
	{
   	return Backend()->link(_oldwidename, _newwidename, *aErrno);					            
	}


// -----------------------------------------------------------------------------
// Function name: unlink
// Description: Provides unlink functionality.
// Returns:  0    : On success
//          -1    : On error
// In case of error, errno value set
//      
// -----------------------------------------------------------------------------
//

EXPORT_C int _unlink_r(int *aErrno, const wchar_t *_pathwidename)
	{
	return Backend()->unlink(_pathwidename, *aErrno);				            
	}

   
EXPORT_C int _posix_spawn_r(pid_t* pid, const wchar_t* wpath, 
							const posix_spawn_file_actions_t* file_actions,
							const posix_spawnattr_t* attrp,
							const wchar_t* wargs, 
							wchar_t** wenvp)
	{
	return Backend()->posix_spawn(pid, wpath, file_actions, attrp, wargs, wenvp);
	}
	
// -----------------------------------------------------------------------------
// Function name: exit
// Description: Provides exit functionality.
// In case of error, errno value set
//      
// -----------------------------------------------------------------------------
//
EXPORT_C void _exit_r(int code)
	{
	return Backend()->Exit(code);
	}

// -----------------------------------------------------------------------------
// _fcntl_r() : A reentrant version of fcntl().
// This API is used for setting a file descriptor as non-blocking
// Returns: System wide error code
// -----------------------------------------------------------------------------
//
EXPORT_C int _fcntl_r (int *aErrno, int aFid, int aCmd, long anArg)
	{
	return Backend()->fcntl(aFid, aCmd, anArg, *aErrno);
	}
	
//
EXPORT_C DIR* _opendir_r (int* aErrno, const wchar_t* _path)
	{
	 __EPOC32_DIR* dp = new __EPOC32_DIR();
	if (!dp ||(Backend()->AddToDirList((DIR*)dp) != KErrNone))
		{
		*aErrno = ENOENT;
		return 0;
		}
	TInt err = dp->Open(_path,aErrno);
	if (err)
		{
		Backend()->RemoveDirFromList((DIR*)dp);
		delete dp;
		return 0;
		}
		
	return (DIR *)dp;
	}

//
EXPORT_C int _wclosedir_r(int* aErrno, WDIR* dp)
	{
	if(Backend()->RemoveDirFromList((DIR*)dp) == KErrNotFound)
		{
		*aErrno = EBADF;	
		}
	delete dp;
	return 0;
	}

// 

EXPORT_C struct dirent* _readdir_r (int* aErrno, DIR *dp)
    {
	__EPOC32_DIR *Dp  = (__EPOC32_DIR *)dp ;
	
	if(!dp || Backend()->FindInDirList((DIR*)dp) == KErrNotFound )
		{
		*aErrno = EBADF;
		return 0;	
		}
	if (_wreaddir_r(Dp))
		{
		if (Dp->UpdateNarrow()>=0)
			{
			return &Dp->iCurrentNarrow;	
			}
		else
			{
			*aErrno = EINVAL;
			}
		}
	return 0;
	}

EXPORT_C struct wdirent* _wreaddir_r(WDIR *dp)
	{
	struct wdirent *ep=&dp->iCurrent;
	if ((dp->iIndex < 0) ||  (dp->iIndex > dp->iCount ))
		return  (wdirent *) NULL;
	
	const TEntry& entry=(*dp->iEntries)[(dp->iIndex)++];
	// in practice, these files must have been created as "X:\something", so they
	// can't really be longer than KMaxFileName-3
	dp->iCurrentName.Copy(entry.iName);
	dp->iCurrentName.ZeroTerminate();
	ep->d_namlen=(short)dp->iCurrentName.Length();
	ep->d_name=(wchar_t*)dp->iCurrentName.PtrZ();
	return ep;
	}

EXPORT_C void _wrewinddir_r(WDIR *dp)
	{
	if (dp==0)
		return;
	(void) dp->Open();	// POSIX doesn't allow for rewind failing
	}
	
// -----------------------------------------------------------------------------
//int _setecho_r(int *aErrno, int aFd, size_t aEchoVal)
//
//Sets the echo flag for this fd.
// -----------------------------------------------------------------------------

EXPORT_C int _setecho_r(int *aErrno, int aFd, uint8_t aEchoVal)
	{
	return Backend()->SetEcho(aFd, aEchoVal, *aErrno);
	}
	
} //extern "C" end

// These functions should be out of extern "C"
/* A reentrant version of aselect().
*/
EXPORT_C int _aselect_r(int *aErrno, int maxfd, fd_set *readfds, fd_set *writefds, 
						fd_set *exceptfds, struct timeval *tvptr,
						TRequestStatus* requeststatus)
	{
	return Backend()->aselect(maxfd, readfds, writefds, exceptfds, tvptr,requeststatus, *aErrno);
	}	

/* A reentrant version of cancelaselect().
*/
EXPORT_C int _cancelaselect_r(int *aErrno, TRequestStatus* requeststatus)
	{
	return Backend()->cancelaselect(requeststatus, *aErrno);
	}	

/* A reentrant version of eselect().
*/
EXPORT_C int _eselect_r(int *aErrno, int maxfd, fd_set *readfds, fd_set *writefds,
						fd_set *exceptfds, struct timeval *tvptr, int numreqs,
						TRequestStatus* waitarray)
	{
	return Backend()->eselect(maxfd, readfds, writefds, exceptfds, tvptr,numreqs, waitarray,*aErrno);
	}

