Imported Upstream version 1.57.0
[platform/upstream/boost.git] / tools / build / src / engine / pathnt.c
1 /*
2  * Copyright 1993-2002 Christopher Seiwald and Perforce Software, Inc.
3  *
4  * This file is part of Jam - see jam.c for Copyright information.
5  */
6
7 /* This file is ALSO:
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)
13  */
14
15 /*
16  * pathnt.c - NT specific path manipulation support
17  */
18
19 #include "pathsys.h"
20
21 #include "hash.h"
22
23 #define WIN32_LEAN_AND_MEAN
24 #include <windows.h>
25
26 #include <assert.h>
27 #include <stdlib.h>
28
29
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)
33
34
35 typedef struct path_key_entry
36 {
37     OBJECT * path;
38     OBJECT * key;
39     int exists;
40 } path_key_entry;
41
42 static struct hash * path_key_cache;
43
44
45 /*
46  * path_get_process_id_()
47  */
48
49 unsigned long path_get_process_id_( void )
50 {
51     return GetCurrentProcessId();
52 }
53
54
55 /*
56  * path_get_temp_path_()
57  */
58
59 void path_get_temp_path_( string * buffer )
60 {
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;
66 }
67
68
69 /*
70  * canonicWindowsPath() - convert a given path into its canonic/long format
71  *
72  * Appends the canonic path to the end of the given 'string' object.
73  *
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.
78  *
79  * Caches results internally, automatically caching any parent paths it has to
80  * convert to their canonic format in the process.
81  *
82  * Prerequisites:
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
86  */
87
88 static int canonicWindowsPath( char const * const path, int const path_length,
89     string * const out )
90 {
91     char const * last_element;
92     unsigned long saved_size;
93     char const * p;
94     int missing_parent;
95
96     /* This is only called via path_key(), which initializes the cache. */
97     assert( path_key_cache );
98
99     if ( !path_length )
100         return 1;
101
102     if ( path_length == 1 && path[ 0 ] == '\\' )
103     {
104         string_push_back( out, '\\' );
105         return 1;
106     }
107
108     if ( path[ 1 ] == ':' &&
109         ( path_length == 2 ||
110         ( path_length == 3 && path[ 2 ] == '\\' ) ) )
111     {
112         string_push_back( out, toupper( path[ 0 ] ) );
113         string_push_back( out, ':' );
114         string_push_back( out, '\\' );
115         return 1;
116     }
117
118     /* Find last '\\'. */
119     for ( p = path + path_length - 1; p >= path && *p != '\\'; --p );
120     last_element = p + 1;
121
122     /* Special case '\' && 'D:\' - include trailing '\'. */
123     if ( p == path ||
124         p == path + 2 && path[ 1 ] == ':' )
125         ++p;
126
127     missing_parent = 0;
128
129     if ( p >= path )
130     {
131         char const * const dir = path;
132         int const dir_length = p - path;
133         OBJECT * const dir_obj = object_new_range( dir, dir_length );
134         int found;
135         path_key_entry * const result = (path_key_entry *)hash_insert(
136             path_key_cache, dir_obj, &found );
137         if ( !found )
138         {
139             result->path = dir_obj;
140             if ( canonicWindowsPath( dir, dir_length, out ) )
141                 result->exists = 1;
142             else
143                 result->exists = 0;
144             result->key = object_new( out->value );
145         }
146         else
147         {
148             object_free( dir_obj );
149             string_append( out, object_str( result->key ) );
150         }
151         if ( !result->exists )
152             missing_parent = 1;
153     }
154
155     if ( out->size && out->value[ out->size - 1 ] != '\\' )
156         string_push_back( out, '\\' );
157
158     saved_size = out->size;
159     string_append_range( out, last_element, path + path_length );
160
161     if ( !missing_parent )
162     {
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 ] == '.' ) )
167         {
168             WIN32_FIND_DATA fd;
169             HANDLE const hf = FindFirstFileA( out->value, &fd );
170             if ( hf != INVALID_HANDLE_VALUE )
171             {
172                 string_truncate( out, saved_size );
173                 string_append( out, fd.cFileName );
174                 FindClose( hf );
175                 return 1;
176             }
177         }
178         else
179         {
180             return 1;
181         }
182     }
183     return 0;
184 }
185
186
187 /*
188  * normalize_path() - 'normalizes' the given path for the path-key mapping
189  *
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.
194  *
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.
201  *
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:\'
206  */
207
208 static void normalize_path( string * path )
209 {
210     char * s;
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 ] == '\\'
215         )
216         string_pop_back( path );
217 }
218
219
220 static path_key_entry * path_key( OBJECT * const path,
221     int const known_to_be_canonic )
222 {
223     path_key_entry * result;
224     int found;
225
226     if ( !path_key_cache )
227         path_key_cache = hashinit( sizeof( path_key_entry ), "path to key" );
228
229     result = (path_key_entry *)hash_insert( path_key_cache, path, &found );
230     if ( !found )
231     {
232         OBJECT * normalized;
233         int normalized_size;
234         path_key_entry * nresult;
235         result->path = path;
236         {
237             string buf[ 1 ];
238             string_copy( buf, object_str( path ) );
239             normalize_path( buf );
240             normalized = object_new( buf->value );
241             normalized_size = buf->size;
242             string_free( buf );
243         }
244         nresult = (path_key_entry *)hash_insert( path_key_cache, normalized,
245             &found );
246         if ( !found || nresult == result )
247         {
248             nresult->path = normalized;
249             if ( known_to_be_canonic )
250             {
251                 nresult->key = object_copy( path );
252                 nresult->exists = 1;
253             }
254             else
255             {
256                 string canonic_path[ 1 ];
257                 string_new( canonic_path );
258                 if ( canonicWindowsPath( object_str( normalized ), normalized_size,
259                         canonic_path ) )
260                     nresult->exists = 1;
261                 else
262                     nresult->exists = 0;
263                 nresult->key = object_new( canonic_path->value );
264                 string_free( canonic_path );
265             }
266         }
267         else
268             object_free( normalized );
269         if ( nresult != result )
270         {
271             result->path = object_copy( path );
272             result->key = object_copy( nresult->key );
273             result->exists = nresult->exists;
274         }
275     }
276
277     return result;
278 }
279
280
281 void path_register_key( OBJECT * canonic_path )
282 {
283     path_key( canonic_path, 1 );
284 }
285
286
287 OBJECT * path_as_key( OBJECT * path )
288 {
289     return object_copy( path_key( path, 0 )->key );
290 }
291
292
293 static void free_path_key_entry( void * xentry, void * const data )
294 {
295     path_key_entry * const entry = (path_key_entry *)xentry;
296     object_free( entry->path );
297     object_free( entry->key );
298 }
299
300
301 void path_done( void )
302 {
303     if ( path_key_cache )
304     {
305         hashenumerate( path_key_cache, &free_path_key_entry, 0 );
306         hashdone( path_key_cache );
307     }
308 }