summaryrefslogtreecommitdiff
path: root/src/pal/src/cruntime/path.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/pal/src/cruntime/path.cpp')
-rw-r--r--src/pal/src/cruntime/path.cpp596
1 files changed, 596 insertions, 0 deletions
diff --git a/src/pal/src/cruntime/path.cpp b/src/pal/src/cruntime/path.cpp
new file mode 100644
index 0000000000..e5b955ebd9
--- /dev/null
+++ b/src/pal/src/cruntime/path.cpp
@@ -0,0 +1,596 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*++
+
+
+
+Module Name:
+
+ path.c
+
+Abstract:
+
+ Implementation of path functions part of Windows runtime library.
+
+Revision History:
+
+
+
+--*/
+
+#include "pal/palinternal.h"
+#include "pal/dbgmsg.h"
+#include "pal/file.h"
+#include "pal/printfcpp.hpp"
+
+#include <string.h>
+#include <stdlib.h>
+#include <sys/param.h>
+#include <errno.h>
+#include <limits.h>
+
+SET_DEFAULT_DEBUG_CHANNEL(CRT);
+
+
+/* ON_ERROR. A Helper macro for _?splitpath functions. */
+#define ON_ERROR if ( drive ) \
+ {\
+ drive[0] = 0;\
+ }\
+ if(dir)\
+ {\
+ dir[0] = 0;\
+ }\
+ if(fname)\
+ {\
+ fname[0] = 0;\
+ }\
+ if(ext)\
+ {\
+ ext[0] = 0;\
+ }\
+ goto done;\
+
+/*++
+Function:
+ _wsplitpath
+
+See MSDN doc.
+
+Notes :
+ This implementation ignores drive letters as they should not be
+ present. If the drive argument is non-NULL, it always returns an empty
+ string.
+ File names in which the only period is at the beginning (like .bashrc, but
+ not .bashrc.bak), the file is treated as having no extension
+ (fname is ".bashrc", ext is "")
+
+--*/
+void
+__cdecl
+_wsplitpath(
+ const wchar_16 *dospath,
+ wchar_16 *drive,
+ wchar_16 *dir,
+ wchar_16 *fname,
+ wchar_16 *ext)
+{
+ WCHAR path[_MAX_PATH+1];
+ LPCWSTR slash_ptr = NULL;
+ LPCWSTR period_ptr = NULL;
+ INT size = 0;
+
+ PERF_ENTRY(_wsplitpath);
+ ENTRY("_wsplitpath (path=%p (%S), drive=%p, dir=%p, fname=%p, ext=%p)\n",
+ dospath?dospath:W16_NULLSTRING,
+ dospath?dospath:W16_NULLSTRING, drive, dir, fname, ext);
+
+ /* Do performance intensive error checking only in debug builds.
+
+ NOTE: This function must fail predictably across all platforms.
+ Under Windows this function throw an access violation if NULL
+ was passed in as the value for path.
+
+ */
+#if _DEBUG
+ if ( !dospath )
+ {
+ ERROR( "path cannot be NULL!\n" );
+ }
+#endif
+
+ if( lstrlenW( dospath ) >= _MAX_PATH )
+ {
+ ERROR("Path length is > _MAX_PATH (%d)!\n", _MAX_PATH);
+ ON_ERROR;
+ }
+
+
+ PAL_wcscpy(path, dospath);
+ FILEDosToUnixPathW(path);
+
+ /* no drive letters in the PAL */
+ if( drive != NULL )
+ {
+ drive[0] = 0;
+ }
+
+ /* find last path separator char */
+ slash_ptr = PAL_wcsrchr(path, '/');
+
+ if( slash_ptr == NULL )
+ {
+ TRACE("No path separator in path\n");
+ slash_ptr = path - 1;
+ }
+ /* find extension separator, if any */
+ period_ptr = PAL_wcsrchr(path, '.');
+
+ /* make sure we only consider periods after the last path separator */
+ if( period_ptr < slash_ptr )
+ {
+ period_ptr = NULL;
+ }
+
+ /* if the only period in the file is a leading period (denoting a hidden
+ file), don't treat what follows as an extension */
+ if( period_ptr == slash_ptr+1 )
+ {
+ period_ptr = NULL;
+ }
+
+ if( period_ptr == NULL )
+ {
+ TRACE("No extension in path\n");
+ period_ptr = path + lstrlenW(path);
+ }
+
+ size = slash_ptr - path + 1;
+ if( dir != NULL )
+ {
+ INT i;
+
+ if( (size + 1 ) > _MAX_DIR )
+ {
+ ERROR("Directory component needs %d characters, _MAX_DIR is %d\n",
+ size+1, _MAX_DIR);
+ ON_ERROR;
+ }
+
+ memcpy(dir, path, size*sizeof(WCHAR));
+ dir[size] = 0;
+
+ /* only allow / separators in returned path */
+ i = 0;
+ while( dir[ i ] )
+ {
+ if( dir[ i ] == '\\' )
+ {
+ dir[i]='/';
+ }
+ i++;
+ }
+ }
+
+ size = period_ptr-slash_ptr-1;
+ if( fname != NULL )
+ {
+ if( (size+1) > _MAX_FNAME )
+ {
+ ERROR("Filename component needs %d characters, _MAX_FNAME is %d\n",
+ size+1, _MAX_FNAME);
+ ON_ERROR;
+ }
+ memcpy(fname, slash_ptr+1, size*sizeof(WCHAR));
+ fname[size] = 0;
+ }
+
+ size = 1 + lstrlenW( period_ptr );
+ if( ext != NULL )
+ {
+ if( size > _MAX_EXT )
+ {
+ ERROR("Extension component needs %d characters, _MAX_EXT is %d\n",
+ size, _MAX_EXT);
+ ON_ERROR;
+ }
+ memcpy(ext, period_ptr, size*sizeof(WCHAR));
+ ext[size-1] = 0;
+ }
+
+ TRACE("Path components are '%S' '%S' '%S'\n", dir, fname, ext);
+
+done:
+
+ LOGEXIT("_wsplitpath returns.\n");
+ PERF_EXIT(_wsplitpath);
+}
+
+
+/*++
+Function:
+ _splitpath
+
+See description above for _wsplitpath.
+
+--*/
+void
+__cdecl
+_splitpath(
+ const char *path,
+ char *drive,
+ char *dir,
+ char *fname,
+ char *ext)
+{
+ WCHAR w_path[_MAX_PATH];
+ WCHAR w_dir[_MAX_DIR];
+ WCHAR w_fname[_MAX_FNAME];
+ WCHAR w_ext[_MAX_EXT];
+
+ PERF_ENTRY(_splitpath);
+ ENTRY("_splitpath (path=%p (%s), drive=%p, dir=%p, fname=%p, ext=%p)\n",
+ path?path:"NULL",
+ path?path:"NULL", drive, dir, fname, ext);
+
+ /* Do performance intensive error checking only in debug builds.
+
+ NOTE: This function must fail predictably across all platforms.
+ Under Windows this function throw an access violation if NULL
+ was passed in as the value for path.
+
+ */
+#if _DEBUG
+ if ( !path )
+ {
+ ERROR( "path cannot be NULL!\n" );
+ }
+
+ if( strlen( path ) >= _MAX_PATH )
+ {
+ ERROR( "Path length is > _MAX_PATH (%d)!\n", _MAX_PATH);
+ }
+#endif
+
+ /* no drive letters in the PAL */
+ if(drive)
+ {
+ drive[0] = '\0';
+ }
+
+ if(0 == MultiByteToWideChar(CP_ACP, 0, path, -1, w_path, _MAX_PATH))
+ {
+ ASSERT("MultiByteToWideChar failed!\n");
+ ON_ERROR;
+ }
+
+ /* Call up to Unicode version; pass NULL for parameters the caller doesn't
+ care about */
+ _wsplitpath(w_path, NULL, dir?w_dir:NULL,
+ fname?w_fname:NULL, ext?w_ext:NULL);
+
+ /* Convert result back to MultiByte; report conversion errors but don't
+ stop because of them */
+
+ if(dir)
+ {
+ if(0 == WideCharToMultiByte(CP_ACP, 0, w_dir, -1, dir, _MAX_DIR,
+ NULL, NULL))
+ {
+ ASSERT("WideCharToMultiByte failed!\n");
+ ON_ERROR;
+ }
+ }
+ if(fname)
+ {
+ if(0 == WideCharToMultiByte(CP_ACP, 0, w_fname, -1, fname, _MAX_FNAME,
+ NULL, NULL))
+ {
+ ASSERT("WideCharToMultiByte failed!\n");
+ ON_ERROR;
+ }
+ }
+ if(ext)
+ {
+ if(0 == WideCharToMultiByte(CP_ACP, 0, w_ext, -1, ext, _MAX_EXT,
+ NULL, NULL))
+ {
+ ASSERT("WideCharToMultiByte failed!\n");
+ ON_ERROR;
+ }
+ }
+
+done:
+ LOGEXIT("_splitpath returns.\n");
+ PERF_EXIT(_splitpath);
+}
+
+
+
+/*++
+Function:
+ _makepath
+
+See MSDN doc.
+
+--*/
+void
+__cdecl
+_makepath(
+ char *path,
+ const char *drive,
+ const char *dir,
+ const char *fname,
+ const char *ext)
+{
+ UINT Length = 0;
+
+ PERF_ENTRY(_makepath);
+ ENTRY( "_makepath (path=%p, drive=%p (%s), dir=%p (%s), fname=%p (%s), ext=%p (%s))\n",
+ path, drive ? drive:"NULL", drive ? drive:"NULL", dir ? dir:"NULL", dir ? dir:"NULL", fname ? fname:"NULL", fname ? fname:"NULL",
+ ext ? ext:"NULL",
+ ext ? ext:"NULL");
+
+ path[ 0 ] = '\0';
+
+ /* According to the pal documentation, host operating systems that
+ don't support drive letters, the "drive" parameter must always be null. */
+ if ( drive != NULL && drive[0] != '\0' )
+ {
+ ASSERT( "The drive parameter must always be NULL on systems that don't"
+ "support drive letters. drive is being ignored!.\n" );
+ }
+
+ if ( dir != NULL && dir[ 0 ] != '\0' )
+ {
+ UINT DirLength = strlen( dir );
+ Length += DirLength ;
+
+ if ( Length < _MAX_PATH )
+ {
+ strncat( path, dir, DirLength );
+ if ( dir[ DirLength - 1 ] != '/' && dir[ DirLength - 1 ] != '\\' )
+ {
+ if ( Length + 1 < _MAX_PATH )
+ {
+ path[ Length ] = '/';
+ Length++;
+ path[ Length ] = '\0';
+ }
+ else
+ {
+ goto Max_Path_Error;
+ }
+ }
+ }
+ else
+ {
+ goto Max_Path_Error;
+ }
+ }
+
+ if ( fname != NULL && fname[ 0 ] != '\0' )
+ {
+ UINT fNameLength = strlen( fname );
+ Length += fNameLength;
+
+ if ( Length < _MAX_PATH )
+ {
+ strncat( path, fname, fNameLength );
+ }
+ else
+ {
+ goto Max_Path_Error;
+ }
+ }
+
+ if ( ext != NULL && ext[ 0 ] != '\0' )
+ {
+ UINT ExtLength = strlen( ext );
+ Length += ExtLength;
+
+ if ( ext[ 0 ] != '.' )
+ {
+ /* Add a '.' */
+ if ( Length + 1 < _MAX_PATH )
+ {
+ path[ Length - ExtLength ] = '.';
+ Length++;
+ path[ Length - ExtLength ] = '\0';
+ strncat( path, ext, ExtLength );
+ }
+ else
+ {
+ goto Max_Path_Error;
+ }
+ }
+ else
+ {
+ /* Already has a '.' */
+ if ( Length < _MAX_PATH )
+ {
+ strncat( path, ext, ExtLength );
+ }
+ else
+ {
+ goto Max_Path_Error;
+ }
+ }
+ }
+
+ FILEDosToUnixPathA( path );
+ LOGEXIT( "_makepath returning void.\n" );
+ PERF_EXIT(_makepath);
+ return;
+
+Max_Path_Error:
+
+ ERROR( "path cannot be greater then _MAX_PATH\n" );
+ path[ 0 ] = '\0';
+ LOGEXIT( "_makepath returning void \n" );
+ PERF_EXIT(_makepath);
+ return;
+}
+
+/*++
+Function:
+ _wmakepath
+
+See MSDN doc.
+
+--*/
+void
+__cdecl
+_wmakepath(
+ wchar_16 *path,
+ const wchar_16 *drive,
+ const wchar_16 *dir,
+ const wchar_16 *fname,
+ const wchar_16 *ext)
+{
+ CHAR Dir[ _MAX_DIR ]={0};
+ CHAR FileName[ _MAX_FNAME ]={0};
+ CHAR Ext[ _MAX_EXT ]={0};
+ CHAR Path[ _MAX_PATH ]={0};
+
+ PERF_ENTRY(_wmakepath);
+ ENTRY("_wmakepath (path=%p, drive=%p (%S), dir=%p (%S), fname=%p (%S), ext=%p (%S))\n",
+ path, drive ? drive:W16_NULLSTRING, drive ? drive:W16_NULLSTRING, dir ? dir:W16_NULLSTRING, dir ? dir:W16_NULLSTRING,
+ fname ? fname:W16_NULLSTRING,
+ fname ? fname:W16_NULLSTRING, ext ? ext:W16_NULLSTRING, ext ? ext:W16_NULLSTRING);
+
+ /* According to the pal documentation, host operating systems that
+ don't support drive letters, the "drive" parameter must always be null. */
+ if ( drive != NULL && drive[0] != '\0' )
+ {
+ ASSERT( "The drive parameter must always be NULL on systems that don't"
+ "support drive letters. drive is being ignored!.\n" );
+ }
+
+ if ((dir != NULL) && WideCharToMultiByte( CP_ACP, 0, dir, -1, Dir,
+ _MAX_DIR, NULL, NULL ) == 0 )
+ {
+ ASSERT( "An error occurred while converting dir to multibyte."
+ "Possible error: Length of dir is greater than _MAX_DIR.\n" );
+ goto error;
+ }
+
+ if ((fname != NULL) && WideCharToMultiByte( CP_ACP, 0, fname, -1, FileName,
+ _MAX_FNAME, NULL, NULL ) == 0 )
+ {
+ ASSERT( "An error occurred while converting fname to multibyte."
+ "Possible error: Length of fname is greater than _MAX_FNAME.\n" );
+ goto error;
+ }
+
+ if ((ext != NULL) && WideCharToMultiByte( CP_ACP, 0, ext, -1, Ext,
+ _MAX_EXT, NULL, NULL ) == 0 )
+ {
+ ASSERT( "An error occurred while converting ext to multibyte."
+ "Possible error: Length of ext is greater than _MAX_EXT.\n" );
+ goto error;
+ }
+
+ /* Call up to the ANSI _makepath. */
+ _makepath_s( Path, sizeof(Path), NULL, Dir, FileName, Ext );
+
+ if ( MultiByteToWideChar( CP_ACP, 0, Path, -1, path, _MAX_PATH ) == 0 )
+ {
+ ASSERT( "An error occurred while converting the back wide char."
+ "Possible error: The length of combined path is greater "
+ "than _MAX_PATH.\n" );
+ goto error;
+ }
+
+ LOGEXIT("_wmakepath returns void\n");
+ PERF_EXIT(_wmakepath);
+ return;
+
+error:
+ *path = '\0';
+ LOGEXIT("_wmakepath returns void\n");
+ PERF_EXIT(_wmakepath);
+}
+
+
+/*++
+Function:
+ _fullpath
+
+See MSDN doc.
+
+--*/
+char *
+__cdecl
+_fullpath(
+ char *absPath,
+ const char *relPath,
+ size_t maxLength)
+{
+ char realpath_buf[PATH_MAX+1];
+ char path_copy[PATH_MAX+1];
+ char *retval = NULL;
+ DWORD cPathCopy = sizeof(path_copy)/sizeof(path_copy[0]);
+ size_t min_length;
+ BOOL fBufAllocated = FALSE;
+
+ PERF_ENTRY(_fullpath);
+ ENTRY("_fullpath (absPath=%p, relPath=%p (%s), maxLength = %lu)\n",
+ absPath, relPath ? relPath:"NULL", relPath ? relPath:"NULL", maxLength);
+
+ if (strncpy_s(path_copy, sizeof(path_copy), relPath ? relPath : ".", cPathCopy) != SAFECRT_SUCCESS)
+ {
+ TRACE("_fullpath: strncpy_s failed!\n");
+ goto fullpathExit;
+ }
+
+ FILEDosToUnixPathA(path_copy);
+
+ if(NULL == realpath(path_copy, realpath_buf))
+ {
+ ERROR("realpath() failed; problem path is '%s'. errno is %d (%s)\n",
+ realpath_buf, errno, strerror(errno));
+ goto fullpathExit;
+ }
+
+ TRACE("real path is %s\n", realpath_buf);
+ min_length = strlen(realpath_buf)+1; // +1 for the NULL terminator
+
+ if(NULL == absPath)
+ {
+ absPath = static_cast<char *>(
+ PAL_malloc(_MAX_PATH * sizeof(char)));
+ if (!absPath)
+ {
+ ERROR("PAL_malloc failed with error %d\n", errno);
+ goto fullpathExit;
+ }
+ maxLength = _MAX_PATH;
+ fBufAllocated = TRUE;
+ }
+
+ if(min_length > maxLength)
+ {
+ ERROR("maxLength is %lu, we need at least %lu\n",
+ maxLength, min_length);
+ if (fBufAllocated)
+ {
+ PAL_free(absPath);
+ fBufAllocated = FALSE;
+ }
+ goto fullpathExit;
+ }
+
+ strcpy_s(absPath, maxLength, realpath_buf);
+ retval = absPath;
+
+fullpathExit:
+ LOGEXIT("_fullpath returns char * %p\n", retval);
+ PERF_EXIT(_fullpath);
+ return retval;
+}
+
+
+