2 * Copyright 1993-2002 Christopher Seiwald and Perforce Software, Inc.
4 * This file is part of Jam - see jam.c for Copyright information.
8 * Copyright 2001-2004 David Abrahams.
9 * Copyright 2005 Rene Rivera.
10 * Distributed under the Boost Software License, Version 1.0.
11 * (See accompanying file LICENSE_1_0.txt or copy at
12 * http://www.boost.org/LICENSE_1_0.txt)
16 * pathnt.c - NT specific path manipulation support
23 #define WIN32_LEAN_AND_MEAN
30 /* The definition of this in winnt.h is not ANSI-C compatible. */
31 #undef INVALID_FILE_ATTRIBUTES
32 #define INVALID_FILE_ATTRIBUTES ((DWORD)-1)
35 typedef struct path_key_entry
42 static struct hash * path_key_cache;
46 * path_get_process_id_()
49 unsigned long path_get_process_id_( void )
51 return GetCurrentProcessId();
56 * path_get_temp_path_()
59 void path_get_temp_path_( string * buffer )
61 DWORD pathLength = GetTempPathA( 0, NULL );
62 string_reserve( buffer, pathLength );
63 pathLength = GetTempPathA( pathLength, buffer->value );
64 buffer->value[ pathLength - 1 ] = '\0';
65 buffer->size = pathLength - 1;
70 * canonicWindowsPath() - convert a given path into its canonic/long format
72 * Appends the canonic path to the end of the given 'string' object.
74 * FIXME: This function is still work-in-progress as it originally did not
75 * necessarily return the canonic path format (could return slightly different
76 * results for certain equivalent path strings) and could accept paths pointing
77 * to non-existing file system entities as well.
79 * Caches results internally, automatically caching any parent paths it has to
80 * convert to their canonic format in the process.
83 * - path given in normalized form, i.e. all of its folder separators have
84 * already been converted into '\\'
85 * - path_key_cache path/key mapping cache object already initialized
88 static int canonicWindowsPath( char const * const path, int const path_length,
91 char const * last_element;
92 unsigned long saved_size;
96 /* This is only called via path_key(), which initializes the cache. */
97 assert( path_key_cache );
102 if ( path_length == 1 && path[ 0 ] == '\\' )
104 string_push_back( out, '\\' );
108 if ( path[ 1 ] == ':' &&
109 ( path_length == 2 ||
110 ( path_length == 3 && path[ 2 ] == '\\' ) ) )
112 string_push_back( out, toupper( path[ 0 ] ) );
113 string_push_back( out, ':' );
114 string_push_back( out, '\\' );
118 /* Find last '\\'. */
119 for ( p = path + path_length - 1; p >= path && *p != '\\'; --p );
120 last_element = p + 1;
122 /* Special case '\' && 'D:\' - include trailing '\'. */
124 p == path + 2 && path[ 1 ] == ':' )
131 char const * const dir = path;
132 int const dir_length = p - path;
133 OBJECT * const dir_obj = object_new_range( dir, dir_length );
135 path_key_entry * const result = (path_key_entry *)hash_insert(
136 path_key_cache, dir_obj, &found );
139 result->path = dir_obj;
140 if ( canonicWindowsPath( dir, dir_length, out ) )
144 result->key = object_new( out->value );
148 object_free( dir_obj );
149 string_append( out, object_str( result->key ) );
151 if ( !result->exists )
155 if ( out->size && out->value[ out->size - 1 ] != '\\' )
156 string_push_back( out, '\\' );
158 saved_size = out->size;
159 string_append_range( out, last_element, path + path_length );
161 if ( !missing_parent )
163 char const * const n = last_element;
164 int const n_length = path + path_length - n;
165 if ( !( n_length == 1 && n[ 0 ] == '.' )
166 && !( n_length == 2 && n[ 0 ] == '.' && n[ 1 ] == '.' ) )
169 HANDLE const hf = FindFirstFileA( out->value, &fd );
170 if ( hf != INVALID_HANDLE_VALUE )
172 string_truncate( out, saved_size );
173 string_append( out, fd.cFileName );
188 * normalize_path() - 'normalizes' the given path for the path-key mapping
190 * The resulting string has nothing to do with 'normalized paths' as used in
191 * Boost Jam build scripts and the built-in NORMALIZE_PATH rule. It is intended
192 * to be used solely as an intermediate step when mapping an arbitrary path to
193 * its canonical representation.
195 * When choosing the intermediate string the important things are for it to be
196 * inexpensive to calculate and any two paths having different canonical
197 * representations also need to have different calculated intermediate string
198 * representations. Any implemented additional rules serve only to simplify
199 * constructing the canonical path representation from the calculated
200 * intermediate string.
202 * Implemented returned path rules:
203 * - use backslashes as path separators
204 * - lowercase only (since all Windows file systems are case insensitive)
205 * - trim trailing path separator except in case of a root path, i.e. 'X:\'
208 static void normalize_path( string * path )
211 for ( s = path->value; s < path->value + path->size; ++s )
212 *s = *s == '/' ? '\\' : tolower( *s );
213 /* Strip trailing "/". */
214 if ( path->size && path->size != 3 && path->value[ path->size - 1 ] == '\\'
216 string_pop_back( path );
220 static path_key_entry * path_key( OBJECT * const path,
221 int const known_to_be_canonic )
223 path_key_entry * result;
226 if ( !path_key_cache )
227 path_key_cache = hashinit( sizeof( path_key_entry ), "path to key" );
229 result = (path_key_entry *)hash_insert( path_key_cache, path, &found );
234 path_key_entry * nresult;
238 string_copy( buf, object_str( path ) );
239 normalize_path( buf );
240 normalized = object_new( buf->value );
241 normalized_size = buf->size;
244 nresult = (path_key_entry *)hash_insert( path_key_cache, normalized,
246 if ( !found || nresult == result )
248 nresult->path = normalized;
249 if ( known_to_be_canonic )
251 nresult->key = object_copy( path );
256 string canonic_path[ 1 ];
257 string_new( canonic_path );
258 if ( canonicWindowsPath( object_str( normalized ), normalized_size,
263 nresult->key = object_new( canonic_path->value );
264 string_free( canonic_path );
268 object_free( normalized );
269 if ( nresult != result )
271 result->path = object_copy( path );
272 result->key = object_copy( nresult->key );
273 result->exists = nresult->exists;
281 void path_register_key( OBJECT * canonic_path )
283 path_key( canonic_path, 1 );
287 OBJECT * path_as_key( OBJECT * path )
289 return object_copy( path_key( path, 0 )->key );
293 static void free_path_key_entry( void * xentry, void * const data )
295 path_key_entry * const entry = (path_key_entry *)xentry;
296 object_free( entry->path );
297 object_free( entry->key );
301 void path_done( void )
303 if ( path_key_cache )
305 hashenumerate( path_key_cache, &free_path_key_entry, 0 );
306 hashdone( path_key_cache );