]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
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 "jam.h" | |
20 | #include "pathsys.h" | |
21 | #include "hash.h" | |
22 | ||
23 | #define WIN32_LEAN_AND_MEAN | |
24 | #include <windows.h> | |
25 | ||
26 | #ifdef OS_CYGWIN | |
27 | # include <cygwin/version.h> | |
28 | # include <sys/cygwin.h> | |
29 | # ifdef CYGWIN_VERSION_CYGWIN_CONV | |
30 | # include <errno.h> | |
31 | # endif | |
32 | # include <windows.h> | |
33 | #endif | |
34 | ||
35 | #include <assert.h> | |
36 | #include <stdlib.h> | |
37 | ||
38 | ||
39 | /* The definition of this in winnt.h is not ANSI-C compatible. */ | |
40 | #undef INVALID_FILE_ATTRIBUTES | |
41 | #define INVALID_FILE_ATTRIBUTES ((DWORD)-1) | |
42 | ||
43 | ||
44 | typedef struct path_key_entry | |
45 | { | |
46 | OBJECT * path; | |
47 | OBJECT * key; | |
48 | int exists; | |
49 | } path_key_entry; | |
50 | ||
51 | static struct hash * path_key_cache; | |
52 | ||
53 | ||
54 | /* | |
55 | * path_get_process_id_() | |
56 | */ | |
57 | ||
58 | unsigned long path_get_process_id_( void ) | |
59 | { | |
60 | return GetCurrentProcessId(); | |
61 | } | |
62 | ||
63 | ||
64 | /* | |
65 | * path_get_temp_path_() | |
66 | */ | |
67 | ||
68 | void path_get_temp_path_( string * buffer ) | |
69 | { | |
70 | DWORD pathLength = GetTempPathA( 0, NULL ); | |
71 | string_reserve( buffer, pathLength ); | |
72 | pathLength = GetTempPathA( pathLength, buffer->value ); | |
73 | buffer->value[ pathLength - 1 ] = '\0'; | |
74 | buffer->size = pathLength - 1; | |
75 | } | |
76 | ||
77 | ||
78 | /* | |
79 | * canonicWindowsPath() - convert a given path into its canonic/long format | |
80 | * | |
81 | * Appends the canonic path to the end of the given 'string' object. | |
82 | * | |
83 | * FIXME: This function is still work-in-progress as it originally did not | |
84 | * necessarily return the canonic path format (could return slightly different | |
85 | * results for certain equivalent path strings) and could accept paths pointing | |
86 | * to non-existing file system entities as well. | |
87 | * | |
88 | * Caches results internally, automatically caching any parent paths it has to | |
89 | * convert to their canonic format in the process. | |
90 | * | |
91 | * Prerequisites: | |
92 | * - path given in normalized form, i.e. all of its folder separators have | |
93 | * already been converted into '\\' | |
94 | * - path_key_cache path/key mapping cache object already initialized | |
95 | */ | |
96 | ||
97 | static int canonicWindowsPath( char const * const path, int const path_length, | |
98 | string * const out ) | |
99 | { | |
100 | char const * last_element; | |
101 | unsigned long saved_size; | |
102 | char const * p; | |
103 | int missing_parent; | |
104 | ||
105 | /* This is only called via path_key(), which initializes the cache. */ | |
106 | assert( path_key_cache ); | |
107 | ||
108 | if ( !path_length ) | |
109 | return 1; | |
110 | ||
111 | if ( path_length == 1 && path[ 0 ] == '\\' ) | |
112 | { | |
113 | string_push_back( out, '\\' ); | |
114 | return 1; | |
115 | } | |
116 | ||
117 | if ( path[ 1 ] == ':' && | |
118 | ( path_length == 2 || | |
119 | ( path_length == 3 && path[ 2 ] == '\\' ) ) ) | |
120 | { | |
121 | string_push_back( out, toupper( path[ 0 ] ) ); | |
122 | string_push_back( out, ':' ); | |
123 | string_push_back( out, '\\' ); | |
124 | return 1; | |
125 | } | |
126 | ||
127 | /* Find last '\\'. */ | |
128 | for ( p = path + path_length - 1; p >= path && *p != '\\'; --p ); | |
129 | last_element = p + 1; | |
130 | ||
131 | /* Special case '\' && 'D:\' - include trailing '\'. */ | |
132 | if ( p == path || | |
133 | p == path + 2 && path[ 1 ] == ':' ) | |
134 | ++p; | |
135 | ||
136 | missing_parent = 0; | |
137 | ||
138 | if ( p >= path ) | |
139 | { | |
140 | char const * const dir = path; | |
141 | int const dir_length = p - path; | |
142 | OBJECT * const dir_obj = object_new_range( dir, dir_length ); | |
143 | int found; | |
144 | path_key_entry * const result = (path_key_entry *)hash_insert( | |
145 | path_key_cache, dir_obj, &found ); | |
146 | if ( !found ) | |
147 | { | |
148 | result->path = dir_obj; | |
149 | if ( canonicWindowsPath( dir, dir_length, out ) ) | |
150 | result->exists = 1; | |
151 | else | |
152 | result->exists = 0; | |
153 | result->key = object_new( out->value ); | |
154 | } | |
155 | else | |
156 | { | |
157 | object_free( dir_obj ); | |
158 | string_append( out, object_str( result->key ) ); | |
159 | } | |
160 | if ( !result->exists ) | |
161 | missing_parent = 1; | |
162 | } | |
163 | ||
164 | if ( out->size && out->value[ out->size - 1 ] != '\\' ) | |
165 | string_push_back( out, '\\' ); | |
166 | ||
167 | saved_size = out->size; | |
168 | string_append_range( out, last_element, path + path_length ); | |
169 | ||
170 | if ( !missing_parent ) | |
171 | { | |
172 | char const * const n = last_element; | |
173 | int const n_length = path + path_length - n; | |
174 | if ( !( n_length == 1 && n[ 0 ] == '.' ) | |
175 | && !( n_length == 2 && n[ 0 ] == '.' && n[ 1 ] == '.' ) ) | |
176 | { | |
177 | WIN32_FIND_DATA fd; | |
178 | HANDLE const hf = FindFirstFileA( out->value, &fd ); | |
179 | if ( hf != INVALID_HANDLE_VALUE ) | |
180 | { | |
181 | string_truncate( out, saved_size ); | |
182 | string_append( out, fd.cFileName ); | |
183 | FindClose( hf ); | |
184 | return 1; | |
185 | } | |
186 | } | |
187 | else | |
188 | { | |
189 | return 1; | |
190 | } | |
191 | } | |
192 | return 0; | |
193 | } | |
194 | ||
195 | ||
196 | /* | |
197 | * normalize_path() - 'normalizes' the given path for the path-key mapping | |
198 | * | |
199 | * The resulting string has nothing to do with 'normalized paths' as used in | |
200 | * Boost Jam build scripts and the built-in NORMALIZE_PATH rule. It is intended | |
201 | * to be used solely as an intermediate step when mapping an arbitrary path to | |
202 | * its canonical representation. | |
203 | * | |
204 | * When choosing the intermediate string the important things are for it to be | |
205 | * inexpensive to calculate and any two paths having different canonical | |
206 | * representations also need to have different calculated intermediate string | |
207 | * representations. Any implemented additional rules serve only to simplify | |
208 | * constructing the canonical path representation from the calculated | |
209 | * intermediate string. | |
210 | * | |
211 | * Implemented returned path rules: | |
212 | * - use backslashes as path separators | |
213 | * - lowercase only (since all Windows file systems are case insensitive) | |
214 | * - trim trailing path separator except in case of a root path, i.e. 'X:\' | |
215 | */ | |
216 | ||
217 | static void normalize_path( string * path ) | |
218 | { | |
219 | char * s; | |
220 | for ( s = path->value; s < path->value + path->size; ++s ) | |
221 | *s = *s == '/' ? '\\' : tolower( *s ); | |
222 | /* Strip trailing "/". */ | |
223 | if ( path->size && path->size != 3 && path->value[ path->size - 1 ] == '\\' | |
224 | ) | |
225 | string_pop_back( path ); | |
226 | } | |
227 | ||
228 | ||
229 | static path_key_entry * path_key( OBJECT * const path, | |
230 | int const known_to_be_canonic ) | |
231 | { | |
232 | path_key_entry * result; | |
233 | int found; | |
234 | ||
235 | if ( !path_key_cache ) | |
236 | path_key_cache = hashinit( sizeof( path_key_entry ), "path to key" ); | |
237 | ||
238 | result = (path_key_entry *)hash_insert( path_key_cache, path, &found ); | |
239 | if ( !found ) | |
240 | { | |
241 | OBJECT * normalized; | |
242 | int normalized_size; | |
243 | path_key_entry * nresult; | |
244 | result->path = path; | |
245 | { | |
246 | string buf[ 1 ]; | |
247 | string_copy( buf, object_str( path ) ); | |
248 | normalize_path( buf ); | |
249 | normalized = object_new( buf->value ); | |
250 | normalized_size = buf->size; | |
251 | string_free( buf ); | |
252 | } | |
253 | nresult = (path_key_entry *)hash_insert( path_key_cache, normalized, | |
254 | &found ); | |
255 | if ( !found || nresult == result ) | |
256 | { | |
257 | nresult->path = normalized; | |
258 | if ( known_to_be_canonic ) | |
259 | { | |
260 | nresult->key = object_copy( path ); | |
261 | nresult->exists = 1; | |
262 | } | |
263 | else | |
264 | { | |
265 | string canonic_path[ 1 ]; | |
266 | string_new( canonic_path ); | |
267 | if ( canonicWindowsPath( object_str( normalized ), normalized_size, | |
268 | canonic_path ) ) | |
269 | nresult->exists = 1; | |
270 | else | |
271 | nresult->exists = 0; | |
272 | nresult->key = object_new( canonic_path->value ); | |
273 | string_free( canonic_path ); | |
274 | } | |
275 | } | |
276 | else | |
277 | object_free( normalized ); | |
278 | if ( nresult != result ) | |
279 | { | |
280 | result->path = object_copy( path ); | |
281 | result->key = object_copy( nresult->key ); | |
282 | result->exists = nresult->exists; | |
283 | } | |
284 | } | |
285 | ||
286 | return result; | |
287 | } | |
288 | ||
289 | ||
290 | /* | |
291 | * translate_path_cyg2win() - conversion of a cygwin to a Windows path. | |
292 | * | |
293 | * FIXME: skip grist | |
294 | */ | |
295 | ||
296 | #ifdef OS_CYGWIN | |
297 | static int translate_path_cyg2win( string * path ) | |
298 | { | |
299 | int translated = 0; | |
300 | ||
301 | #ifdef CYGWIN_VERSION_CYGWIN_CONV | |
302 | /* Use new Cygwin API added with Cygwin 1.7. Old one had no error | |
303 | * handling and has been deprecated. | |
304 | */ | |
305 | char * dynamicBuffer = 0; | |
306 | char buffer[ MAX_PATH + 1001 ]; | |
307 | char const * result = buffer; | |
308 | cygwin_conv_path_t const conv_type = CCP_POSIX_TO_WIN_A | CCP_RELATIVE; | |
309 | ssize_t const apiResult = cygwin_conv_path( conv_type, path->value, | |
310 | buffer, sizeof( buffer ) / sizeof( *buffer ) ); | |
311 | assert( apiResult == 0 || apiResult == -1 ); | |
312 | assert( apiResult || strlen( result ) < sizeof( buffer ) / sizeof( | |
313 | *buffer ) ); | |
314 | if ( apiResult ) | |
315 | { | |
316 | result = 0; | |
317 | if ( errno == ENOSPC ) | |
318 | { | |
319 | ssize_t const size = cygwin_conv_path( conv_type, path->value, | |
320 | NULL, 0 ); | |
321 | assert( size >= -1 ); | |
322 | if ( size > 0 ) | |
323 | { | |
324 | dynamicBuffer = (char *)BJAM_MALLOC_ATOMIC( size ); | |
325 | if ( dynamicBuffer ) | |
326 | { | |
327 | ssize_t const apiResult = cygwin_conv_path( conv_type, | |
328 | path->value, dynamicBuffer, size ); | |
329 | assert( apiResult == 0 || apiResult == -1 ); | |
330 | if ( !apiResult ) | |
331 | { | |
332 | result = dynamicBuffer; | |
333 | assert( strlen( result ) < size ); | |
334 | } | |
335 | } | |
336 | } | |
337 | } | |
338 | } | |
339 | #else /* CYGWIN_VERSION_CYGWIN_CONV */ | |
340 | /* Use old Cygwin API deprecated with Cygwin 1.7. */ | |
341 | char result[ MAX_PATH + 1 ]; | |
342 | cygwin_conv_to_win32_path( path->value, result ); | |
343 | assert( strlen( result ) <= MAX_PATH ); | |
344 | #endif /* CYGWIN_VERSION_CYGWIN_CONV */ | |
345 | ||
346 | if ( result ) | |
347 | { | |
348 | string_truncate( path, 0 ); | |
349 | string_append( path, result ); | |
350 | translated = 1; | |
351 | } | |
352 | ||
353 | #ifdef CYGWIN_VERSION_CYGWIN_CONV | |
354 | if ( dynamicBuffer ) | |
355 | BJAM_FREE( dynamicBuffer ); | |
356 | #endif | |
357 | ||
358 | return translated; | |
359 | } | |
360 | #endif /* OS_CYGWIN */ | |
361 | ||
362 | ||
363 | /* | |
364 | * path_translate_to_os_() | |
365 | */ | |
366 | ||
367 | int path_translate_to_os_( char const * f, string * file ) | |
368 | { | |
369 | int translated = 0; | |
370 | ||
371 | /* by default, pass on the original path */ | |
372 | string_copy( file, f ); | |
373 | ||
374 | #ifdef OS_CYGWIN | |
375 | translated = translate_path_cyg2win( file ); | |
376 | #endif | |
377 | ||
378 | return translated; | |
379 | } | |
380 | ||
381 | ||
382 | void path_register_key( OBJECT * canonic_path ) | |
383 | { | |
384 | path_key( canonic_path, 1 ); | |
385 | } | |
386 | ||
387 | ||
388 | OBJECT * path_as_key( OBJECT * path ) | |
389 | { | |
390 | return object_copy( path_key( path, 0 )->key ); | |
391 | } | |
392 | ||
393 | ||
394 | static void free_path_key_entry( void * xentry, void * const data ) | |
395 | { | |
396 | path_key_entry * const entry = (path_key_entry *)xentry; | |
397 | object_free( entry->path ); | |
398 | object_free( entry->key ); | |
399 | } | |
400 | ||
401 | ||
402 | void path_done( void ) | |
403 | { | |
404 | if ( path_key_cache ) | |
405 | { | |
406 | hashenumerate( path_key_cache, &free_path_key_entry, 0 ); | |
407 | hashdone( path_key_cache ); | |
408 | } | |
409 | } |