text: /*++
  
  
   Copyright (c) 2006 Microsoft Corporation.  All rights reserved.
  
   The use and distribution terms for this software are contained in the file
   named license.txt, which can be found in the root of this distribution.
   By using this software in any fashion, you are agreeing to be bound by the
   terms of this license.
  
   You must not remove this notice, or any other, from this software.
  
  
  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 <string.h>
  #include <stdlib.h>
  #include <sys/param.h>
  #include <errno.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",
            path?path:W16_NULLSTRING,
            path?path: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 rotor_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 rotor_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 occured 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 occured 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 occured 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( Path, NULL, Dir, FileName, Ext );
  
      if ( MultiByteToWideChar( CP_ACP, 0, Path, -1, path, _MAX_PATH ) == 0 )
      {
          ASSERT( "An error occured 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[MAXPATHLEN];
      char path_copy[MAXPATHLEN];
      char *retval = NULL;
  
      PERF_ENTRY(_fullpath);
      ENTRY("_fullpath (absPath=%p, relPath=%p (%s), maxLength = %d)\n",
            absPath, relPath ? relPath:"NULL", relPath ? relPath:"NULL", maxLength);
      
      strcpy(path_copy, relPath ? relPath : ".");
      FILEDosToUnixPathA(path_copy);
  
      if(NULL == realpath(path_copy, realpath_buf))
      {
          TRACE("realpath() failed; problem path is '%s'. errno is %d (%s)\n",
                realpath_buf, errno, strerror(errno));
      }   
      else 
      {
          int min_length;
  
          TRACE("real path is %s\n", realpath_buf);
          min_length = strlen(realpath_buf)+1;
  
          if(NULL == absPath)
          {
              ASSERT("Invalid absPath parameter value (NULL)\n");
          }
          else
          {
              if(min_length > maxLength)
              {
                  TRACE("maxLength is %d, we need at least %d\n",
                        maxLength, min_length);
              }
              else
              {
                  strcpy(absPath, realpath_buf);
                  retval = absPath;
              }
          }
      }
      
      LOGEXIT("_fullpath returns char * %p\n", retval);
      PERF_EXIT(_fullpath);
      return retval;
  }
  
  
  
  