/*++

Copyright (c) 1990  Microsoft Corporation

Module Name:

    curdir.c

Abstract:

    Current directory support

Author:

    Mark Lucovsky (markl) 10-Oct-1990

Revision History:

--*/

#include "basedll.h"

BOOL
CheckForSameCurdir(
    PUNICODE_STRING PathName
    )
{
    PCURDIR CurDir;
    UNICODE_STRING CurrentDir;
    BOOL rv;


    CurDir = &(NtCurrentPeb()->ProcessParameters->CurrentDirectory);

    if (CurDir->DosPath.Length > 6 ) {
        if ( (CurDir->DosPath.Length-2) != PathName->Length ) {
            return FALSE;
            }
        }
    else {
        if ( CurDir->DosPath.Length != PathName->Length ) {
            return FALSE;
            }
        }

    RtlAcquirePebLock();

    CurrentDir = CurDir->DosPath;
    if ( CurrentDir.Length > 6 ) {
        CurrentDir.Length -= 2;
        }
    rv = FALSE;

    if ( RtlEqualUnicodeString(&CurrentDir,PathName,TRUE) ) {
        rv = TRUE;
        }
    RtlReleasePebLock();

    return rv;
}


DWORD
APIENTRY
GetFullPathNameA(
    LPCSTR lpFileName,
    DWORD nBufferLength,
    LPSTR lpBuffer,
    LPSTR *lpFilePart
    )

/*++

Routine Description:

    ANSI thunk to GetFullPathNameW

--*/

{

    NTSTATUS Status;
    ULONG UnicodeLength;
    UNICODE_STRING UnicodeString;
    UNICODE_STRING UnicodeResult;
    ANSI_STRING AnsiResult;
    PWSTR Ubuff;
    PWSTR FilePart;
    PWSTR *FilePartPtr;
    INT PrefixLength = 0;

    if ( ARGUMENT_PRESENT(lpFilePart) ) {
        FilePartPtr = &FilePart;
    } else {
        FilePartPtr = NULL;
    }

    if (!Basep8BitStringToDynamicUnicodeString( &UnicodeString, lpFileName )) {
        return 0;
    }

    Ubuff = RtlAllocateHeap(RtlProcessHeap(), MAKE_TAG( TMP_TAG ), (MAX_PATH<<1) + sizeof(UNICODE_NULL));
    if ( !Ubuff ) {
        RtlFreeUnicodeString(&UnicodeString);
        BaseSetLastNTError(STATUS_NO_MEMORY);
        return 0;
    }

    UnicodeLength = RtlGetFullPathName_U(
                        UnicodeString.Buffer,
                        (MAX_PATH<<1),
                        Ubuff,
                        FilePartPtr
                        );

    //
    // UnicodeLength contains the byte count of unicode string.
    // Original code does "UnicodeLength / sizeof(WCHAR)" to get
    // the size of corresponding ansi string.
    // This is correct in SBCS environment. However in DBCS environment,
    // it's definitely WRONG.
    //
    if ( UnicodeLength <= MAX_PATH * sizeof (WCHAR) ) {

        Status = RtlUnicodeToMultiByteSize(&UnicodeLength, Ubuff, UnicodeLength);
        //
        // At this point, UnicodeLength variable contains
        // Ansi based byte length.
        //
        if ( NT_SUCCESS(Status) ) {
            if ( UnicodeLength && ARGUMENT_PRESENT(lpFilePart) && FilePart != NULL ) {
                INT UnicodePrefixLength;

                UnicodePrefixLength = (INT)(FilePart - Ubuff) * sizeof(WCHAR);
                Status = RtlUnicodeToMultiByteSize( &PrefixLength,
                                                    Ubuff,
                                                    UnicodePrefixLength );
                //
                // At this point, PrefixLength variable contains
                // Ansi based byte length.
                //
                if ( !NT_SUCCESS(Status) ) {
                    BaseSetLastNTError(Status);
                    UnicodeLength = 0;
                }
            }
        } else {
            BaseSetLastNTError(Status);
            UnicodeLength = 0;
        }
    } else {
        //
        // we exceed the MAX_PATH limit. we should log the error and
        // return zero. however US code returns the byte count of
        // buffer required and doesn't log any error.
        //
        UnicodeLength = 0;
    }
    if ( UnicodeLength && UnicodeLength < nBufferLength ) {
        RtlInitUnicodeString(&UnicodeResult,Ubuff);
        Status = BasepUnicodeStringTo8BitString(&AnsiResult,&UnicodeResult,TRUE);
        if ( NT_SUCCESS(Status) ) {
            RtlMoveMemory(lpBuffer,AnsiResult.Buffer,UnicodeLength+1);
            RtlFreeAnsiString(&AnsiResult);

            if ( ARGUMENT_PRESENT(lpFilePart) ) {
                if ( FilePart == NULL ) {
                    *lpFilePart = NULL;
                } else {
                    *lpFilePart = lpBuffer + PrefixLength;
                }
            }
        } else {
            BaseSetLastNTError(Status);
            UnicodeLength = 0;
        }
    } else {
        if ( UnicodeLength ) {
            UnicodeLength++;
        }
    }
    RtlFreeUnicodeString(&UnicodeString);
    RtlFreeHeap(RtlProcessHeap(), 0,Ubuff);

    return (DWORD)UnicodeLength;
}

DWORD
APIENTRY
GetFullPathNameW(
    LPCWSTR lpFileName,
    DWORD nBufferLength,
    LPWSTR lpBuffer,
    LPWSTR *lpFilePart
    )

/*++

Routine Description:

    This function is used to return the fully qualified path name
    corresponding to the specified file name.

    This function is used to return a fully qualified pathname
    corresponding to the specified filename.  It does this by merging
    the current drive and directory together with the specified file
    name.  In addition to this, it calculates the address of the file
    name portion of the fully qualified pathname.

Arguments:

    lpFileName - Supplies the file name of the file whose fully
        qualified pathname is to be returned.

    nBufferLength - Supplies the length in bytes of the buffer that is
        to receive the fully qualified path.

    lpBuffer - Returns the fully qualified pathname corresponding to the
        specified file.

    lpFilePart - Returns the address of the last component of the fully
        qualified pathname.

Return Value:

    The return value is the length of the string copied to lpBuffer, not
    including the terminating null character.  If the return value is
    greater than nBufferLength, the return value is the size of the buffer
    required to hold the pathname.  The return value is zero if the
    function failed.

--*/

{

    return (DWORD) RtlGetFullPathName_U(
                        lpFileName,
                        nBufferLength*2,
                        lpBuffer,
                        lpFilePart
                        )/2;
}


DWORD
APIENTRY
GetCurrentDirectoryA(
    DWORD nBufferLength,
    LPSTR lpBuffer
    )

/*++

Routine Description:

   ANSI thunk to GetCurrentDirectoryW

--*/

{
    PUNICODE_STRING Unicode;
    ANSI_STRING AnsiString;
    NTSTATUS Status;
    DWORD ReturnValue;
    ULONG cbAnsiString;

    if ( nBufferLength > MAXUSHORT ) {
        nBufferLength = MAXUSHORT-2;
        }

    Unicode = &NtCurrentTeb()->StaticUnicodeString;
    Unicode->Length = (USHORT)RtlGetCurrentDirectory_U(
                                    Unicode->MaximumLength,
                                    Unicode->Buffer
                                    );

    //
    // Unicode->Length contains the byte count of unicode string.
    // Original code does "UnicodeLength / sizeof(WCHAR)" to
    // get the size of corresponding ansi string.
    // This is correct in SBCS environment. However in DBCS
    // environment, it's definitely WRONG.
    //
    Status = RtlUnicodeToMultiByteSize( &cbAnsiString, Unicode->Buffer, Unicode->Length );
    if ( !NT_SUCCESS(Status) ) {
        BaseSetLastNTError(Status);
        ReturnValue = 0;
        }
    else {
        if ( nBufferLength > (DWORD)(cbAnsiString ) ) {
            AnsiString.Buffer = lpBuffer;
            AnsiString.MaximumLength = (USHORT)(nBufferLength+1);
            Status = BasepUnicodeStringTo8BitString(&AnsiString,Unicode,FALSE);

            if ( !NT_SUCCESS(Status) ) {
                BaseSetLastNTError(Status);
                ReturnValue = 0;
                }
            else {
                ReturnValue = AnsiString.Length;
                }
            }
        else {
            // The return value is the size of the buffer required to hold the
            // pathname (including the terminating null character).

                ReturnValue = cbAnsiString + 1;
            }
        }
    return ReturnValue;
}

DWORD
APIENTRY
GetCurrentDirectoryW(
    DWORD nBufferLength,
    LPWSTR lpBuffer
    )

/*++

Routine Description:

    The current directory for a process can be retreived using
    GetCurrentDirectory.

Arguments:

    nBufferLength - Supplies the length in bytes of the buffer that is to
        receive the current directory string.

    lpBuffer - Returns the current directory string for the current
        process.  The string is a null terminated string and specifies
        the absolute path to the current directory.

Return Value:

    The return value is the length of the string copied to lpBuffer, not
    including the terminating null character.  If the return value is
    greater than nBufferLength, the return value is the size of the buffer
    required to hold the pathname.  The return value is zero if the
    function failed.

--*/

{
    return (DWORD)RtlGetCurrentDirectory_U(nBufferLength*2,lpBuffer)/2;
}


BOOL
APIENTRY
SetCurrentDirectoryA(
    LPCSTR lpPathName
    )

/*++

Routine Description:

    ANSI thunk to SetCurrentDirectoryW

--*/

{

    NTSTATUS Status;
    PUNICODE_STRING Unicode;
    BOOL rv;

    Unicode = Basep8BitStringToStaticUnicodeString( lpPathName );
    if (Unicode == NULL) {
        return FALSE;
    }

    if ( !CheckForSameCurdir(Unicode) ) {

        Status = RtlSetCurrentDirectory_U(Unicode);

        if ( !NT_SUCCESS(Status) ) {

            //
            // claris works 5.0 has a bug where it doesn't strip leading/trailing
            // quotes properly. It ends up calling SetCurrentDirectoryA with a
            // leading quote, and WinExec with a trailing quote. This error path
            // logic will compensate for the leading quote problem
            //
            if ( Unicode->Buffer[0] == L'"' && Unicode->Length > 2 ) {

                Unicode = Basep8BitStringToStaticUnicodeString( lpPathName+1 );
                if (Unicode == NULL) {
                    return FALSE;
                    }
                Status = RtlSetCurrentDirectory_U(Unicode);
                if ( !NT_SUCCESS(Status) ) {
                    BaseSetLastNTError(Status);
                    rv = FALSE;
                    }
                else {
                    rv = TRUE;
                    }
                }
            else {
                BaseSetLastNTError(Status);
                rv = FALSE;
                }
        } else {
            rv = TRUE;
        }
    } else {
        rv = TRUE;
    }

    return rv;

}

BOOL
APIENTRY
SetCurrentDirectoryW(
    LPCWSTR lpPathName
    )

/*++

Routine Description:

    The current directory for a process is changed using
    SetCurrentDirectory.

    Each process has a single current directory.  A current directory is
    made up of type parts.

        - A disk designator either which is either a drive letter followed
          by a colon, or a UNC servername/sharename "\\servername\sharename".

        - A directory on the disk designator.

    For APIs that manipulate files, the file names may be relative to
    the current directory.  A filename is relative to the entire current
    directory if it does not begin with a disk designator or a path name
    separator.  If the file name begins with a path name separator, then
    it is relative to the disk designator of the current directory.  If
    a file name begins with a disk designator, than it is a fully
    qualified absolute path name.


    The value of lpPathName supplies the current directory.  The value
    of lpPathName, may be a relative path name as described above, or a
    fully qualified absolute path name.  In either case, the fully
    qualified absolute path name of the specified directory is
    calculated and is stored as the current directory.

Arguments:

    lpPathName - Supplies the pathname of the directory that is to be
        made the current directory.

Return Value:

    TRUE - The operation was successful.

    FALSE/NULL - The operation failed. Extended error status is available
        using GetLastError.

--*/

{

    NTSTATUS Status;
    UNICODE_STRING UnicodeString;
    BOOL rv;

    RtlInitUnicodeString(&UnicodeString,lpPathName);

    if ( !CheckForSameCurdir(&UnicodeString) ) {

        Status = RtlSetCurrentDirectory_U(&UnicodeString);

        if ( !NT_SUCCESS(Status) ) {
            BaseSetLastNTError(Status);
            rv = FALSE;
            }
        else {
            rv = TRUE;
            }
        }
    else {
        rv = TRUE;
        }
    return rv;
}



DWORD
APIENTRY
GetLogicalDrives(
    VOID
    )
{
    NTSTATUS Status;
    PROCESS_DEVICEMAP_INFORMATION ProcessDeviceMapInfo;

    Status = NtQueryInformationProcess( NtCurrentProcess(),
                                        ProcessDeviceMap,
                                        &ProcessDeviceMapInfo.Query,
                                        sizeof( ProcessDeviceMapInfo.Query ),
                                        NULL
                                      );
    if (NT_SUCCESS (Status)) {
        if (ProcessDeviceMapInfo.Query.DriveMap == 0) {
            SetLastError(NO_ERROR);
        }
        return ProcessDeviceMapInfo.Query.DriveMap;
    } else {
        BaseSetLastNTError(Status);
        return 0;
    }
}
