/* * Copyright 1993-2002 Christopher Seiwald and Perforce Software, Inc. * * This file is part of Jam - see jam.c for Copyright information. */ /* This file is ALSO: * Copyright 2001-2004 David Abrahams. * Copyright 2005 Rene Rivera. * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) */ /* * pathnt.c - NT specific path manipulation support */ #include "pathsys.h" #include "hash.h" #define WIN32_LEAN_AND_MEAN #include #include #include /* The definition of this in winnt.h is not ANSI-C compatible. */ #undef INVALID_FILE_ATTRIBUTES #define INVALID_FILE_ATTRIBUTES ((DWORD)-1) typedef struct path_key_entry { OBJECT * path; OBJECT * key; int exists; } path_key_entry; static struct hash * path_key_cache; /* * path_get_process_id_() */ unsigned long path_get_process_id_( void ) { return GetCurrentProcessId(); } /* * path_get_temp_path_() */ void path_get_temp_path_( string * buffer ) { DWORD pathLength = GetTempPathA( 0, NULL ); string_reserve( buffer, pathLength ); pathLength = GetTempPathA( pathLength, buffer->value ); buffer->value[ pathLength - 1 ] = '\0'; buffer->size = pathLength - 1; } /* * canonicWindowsPath() - convert a given path into its canonic/long format * * Appends the canonic path to the end of the given 'string' object. * * FIXME: This function is still work-in-progress as it originally did not * necessarily return the canonic path format (could return slightly different * results for certain equivalent path strings) and could accept paths pointing * to non-existing file system entities as well. * * Caches results internally, automatically caching any parent paths it has to * convert to their canonic format in the process. * * Prerequisites: * - path given in normalized form, i.e. all of its folder separators have * already been converted into '\\' * - path_key_cache path/key mapping cache object already initialized */ static int canonicWindowsPath( char const * const path, int const path_length, string * const out ) { char const * last_element; unsigned long saved_size; char const * p; int missing_parent; /* This is only called via path_key(), which initializes the cache. */ assert( path_key_cache ); if ( !path_length ) return 1; if ( path_length == 1 && path[ 0 ] == '\\' ) { string_push_back( out, '\\' ); return 1; } if ( path[ 1 ] == ':' && ( path_length == 2 || ( path_length == 3 && path[ 2 ] == '\\' ) ) ) { string_push_back( out, toupper( path[ 0 ] ) ); string_push_back( out, ':' ); string_push_back( out, '\\' ); return 1; } /* Find last '\\'. */ for ( p = path + path_length - 1; p >= path && *p != '\\'; --p ); last_element = p + 1; /* Special case '\' && 'D:\' - include trailing '\'. */ if ( p == path || p == path + 2 && path[ 1 ] == ':' ) ++p; missing_parent = 0; if ( p >= path ) { char const * const dir = path; int const dir_length = p - path; OBJECT * const dir_obj = object_new_range( dir, dir_length ); int found; path_key_entry * const result = (path_key_entry *)hash_insert( path_key_cache, dir_obj, &found ); if ( !found ) { result->path = dir_obj; if ( canonicWindowsPath( dir, dir_length, out ) ) result->exists = 1; else result->exists = 0; result->key = object_new( out->value ); } else { object_free( dir_obj ); string_append( out, object_str( result->key ) ); } if ( !result->exists ) missing_parent = 1; } if ( out->size && out->value[ out->size - 1 ] != '\\' ) string_push_back( out, '\\' ); saved_size = out->size; string_append_range( out, last_element, path + path_length ); if ( !missing_parent ) { char const * const n = last_element; int const n_length = path + path_length - n; if ( !( n_length == 1 && n[ 0 ] == '.' ) && !( n_length == 2 && n[ 0 ] == '.' && n[ 1 ] == '.' ) ) { WIN32_FIND_DATA fd; HANDLE const hf = FindFirstFileA( out->value, &fd ); if ( hf != INVALID_HANDLE_VALUE ) { string_truncate( out, saved_size ); string_append( out, fd.cFileName ); FindClose( hf ); return 1; } } else { return 1; } } return 0; } /* * normalize_path() - 'normalizes' the given path for the path-key mapping * * The resulting string has nothing to do with 'normalized paths' as used in * Boost Jam build scripts and the built-in NORMALIZE_PATH rule. It is intended * to be used solely as an intermediate step when mapping an arbitrary path to * its canonical representation. * * When choosing the intermediate string the important things are for it to be * inexpensive to calculate and any two paths having different canonical * representations also need to have different calculated intermediate string * representations. Any implemented additional rules serve only to simplify * constructing the canonical path representation from the calculated * intermediate string. * * Implemented returned path rules: * - use backslashes as path separators * - lowercase only (since all Windows file systems are case insensitive) * - trim trailing path separator except in case of a root path, i.e. 'X:\' */ static void normalize_path( string * path ) { char * s; for ( s = path->value; s < path->value + path->size; ++s ) *s = *s == '/' ? '\\' : tolower( *s ); /* Strip trailing "/". */ if ( path->size && path->size != 3 && path->value[ path->size - 1 ] == '\\' ) string_pop_back( path ); } static path_key_entry * path_key( OBJECT * const path, int const known_to_be_canonic ) { path_key_entry * result; int found; if ( !path_key_cache ) path_key_cache = hashinit( sizeof( path_key_entry ), "path to key" ); result = (path_key_entry *)hash_insert( path_key_cache, path, &found ); if ( !found ) { OBJECT * normalized; int normalized_size; path_key_entry * nresult; result->path = path; { string buf[ 1 ]; string_copy( buf, object_str( path ) ); normalize_path( buf ); normalized = object_new( buf->value ); normalized_size = buf->size; string_free( buf ); } nresult = (path_key_entry *)hash_insert( path_key_cache, normalized, &found ); if ( !found || nresult == result ) { nresult->path = normalized; if ( known_to_be_canonic ) { nresult->key = object_copy( path ); nresult->exists = 1; } else { string canonic_path[ 1 ]; string_new( canonic_path ); if ( canonicWindowsPath( object_str( normalized ), normalized_size, canonic_path ) ) nresult->exists = 1; else nresult->exists = 0; nresult->key = object_new( canonic_path->value ); string_free( canonic_path ); } } else object_free( normalized ); if ( nresult != result ) { result->path = object_copy( path ); result->key = object_copy( nresult->key ); result->exists = nresult->exists; } } return result; } void path_register_key( OBJECT * canonic_path ) { path_key( canonic_path, 1 ); } OBJECT * path_as_key( OBJECT * path ) { return object_copy( path_key( path, 0 )->key ); } static void free_path_key_entry( void * xentry, void * const data ) { path_key_entry * const entry = (path_key_entry *)xentry; object_free( entry->path ); object_free( entry->key ); } void path_done( void ) { if ( path_key_cache ) { hashenumerate( path_key_cache, &free_path_key_entry, 0 ); hashdone( path_key_cache ); } }