Imported Upstream version 1.57.0
[platform/upstream/boost.git] / tools / build / src / engine / pathsys.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  * pathsys.c - platform independent path manipulation support
17  *
18  * External routines:
19  *  path_build()   - build a filename given dir/base/suffix/member
20  *  path_parent()  - make a PATHNAME point to its parent dir
21  *  path_parse()   - split a file name into dir/base/suffix/member
22  *  path_tmpdir()  - returns the system dependent temporary folder path
23  *  path_tmpfile() - returns a new temporary path
24  *  path_tmpnam()  - returns a new temporary name
25  *
26  * File_parse() and path_build() just manipulate a string and a structure;
27  * they do not make system calls.
28  */
29
30 #include "jam.h"
31 #include "pathsys.h"
32
33 #include "filesys.h"
34
35 #include <stdlib.h>
36 #include <time.h>
37
38
39 /* Internal OS specific implementation details - have names ending with an
40  * underscore and are expected to be implemented in an OS specific pathXXX.c
41  * module.
42  */
43 unsigned long path_get_process_id_( void );
44 void path_get_temp_path_( string * buffer );
45
46
47 /*
48  * path_parse() - split a file name into dir/base/suffix/member
49  */
50
51 void path_parse( char const * file, PATHNAME * f )
52 {
53     char const * p;
54     char const * q;
55     char const * end;
56
57     memset( (char *)f, 0, sizeof( *f ) );
58
59     /* Look for '<grist>'. */
60
61     if ( ( file[ 0 ] == '<' ) && ( p = strchr( file, '>' ) ) )
62     {
63         f->f_grist.ptr = file;
64         f->f_grist.len = p - file;
65         file = p + 1;
66     }
67
68     /* Look for 'dir/'. */
69
70     p = strrchr( file, '/' );
71
72 #if PATH_DELIM == '\\'
73     /* On NT, look for dir\ as well */
74     {
75         char * const p1 = strrchr( p ? p + 1 : file, '\\' );
76         if ( p1 ) p = p1;
77     }
78 #endif
79
80     if ( p )
81     {
82         f->f_dir.ptr = file;
83         f->f_dir.len = p - file;
84
85         /* Special case for / - dirname is /, not "" */
86         if ( !f->f_dir.len )
87             ++f->f_dir.len;
88
89 #if PATH_DELIM == '\\'
90         /* Special case for D:/ - dirname is D:/, not "D:" */
91         if ( f->f_dir.len == 2 && file[ 1 ] == ':' )
92             ++f->f_dir.len;
93 #endif
94
95         file = p + 1;
96     }
97
98     end = file + strlen( file );
99
100     /* Look for '(member)'. */
101     if ( ( p = strchr( file, '(' ) ) && ( end[ -1 ] == ')' ) )
102     {
103         f->f_member.ptr = p + 1;
104         f->f_member.len = end - p - 2;
105         end = p;
106     }
107
108     /* Look for '.suffix'. This would be memrchr(). */
109     p = 0;
110     for ( q = file; ( q = (char *)memchr( q, '.', end - q ) ); ++q )
111         p = q;
112     if ( p )
113     {
114         f->f_suffix.ptr = p;
115         f->f_suffix.len = end - p;
116         end = p;
117     }
118
119     /* Leaves base. */
120     f->f_base.ptr = file;
121     f->f_base.len = end - file;
122 }
123
124
125 /*
126  * is_path_delim() - true iff c is a path delimiter
127  */
128
129 static int is_path_delim( char const c )
130 {
131     return c == PATH_DELIM
132 #if PATH_DELIM == '\\'
133         || c == '/'
134 #endif
135         ;
136 }
137
138
139 /*
140  * as_path_delim() - convert c to a path delimiter if it is not one already
141  */
142
143 static char as_path_delim( char const c )
144 {
145     return is_path_delim( c ) ? c : PATH_DELIM;
146 }
147
148
149 /*
150  * path_build() - build a filename given dir/base/suffix/member
151  *
152  * To avoid changing slash direction on NT when reconstituting paths, instead of
153  * unconditionally appending PATH_DELIM we check the past-the-end character of
154  * the previous path element. If it is a path delimiter, we append that, and
155  * only append PATH_DELIM as a last resort. This heuristic is based on the fact
156  * that PATHNAME objects are usually the result of calling path_parse, which
157  * leaves the original slashes in the past-the-end position. Correctness depends
158  * on the assumption that all strings are zero terminated, so a past-the-end
159  * character will always be available.
160  *
161  * As an attendant patch, we had to ensure that backslashes are used explicitly
162  * in 'timestamp.c'.
163  */
164
165 void path_build( PATHNAME * f, string * file )
166 {
167     file_build1( f, file );
168
169     /* Do not prepend root if it is '.' or the directory is rooted. */
170     if ( f->f_root.len
171         && !( f->f_root.len == 1 && f->f_root.ptr[ 0 ] == '.' )
172         && !( f->f_dir.len && f->f_dir.ptr[ 0 ] == '/' )
173 #if PATH_DELIM == '\\'
174         && !( f->f_dir.len && f->f_dir.ptr[ 0 ] == '\\' )
175         && !( f->f_dir.len && f->f_dir.ptr[ 1 ] == ':' )
176 #endif
177     )
178     {
179         string_append_range( file, f->f_root.ptr, f->f_root.ptr + f->f_root.len
180             );
181         /* If 'root' already ends with a path delimeter, do not add another one.
182          */
183         if ( !is_path_delim( f->f_root.ptr[ f->f_root.len - 1 ] ) )
184             string_push_back( file, as_path_delim( f->f_root.ptr[ f->f_root.len
185                 ] ) );
186     }
187
188     if ( f->f_dir.len )
189         string_append_range( file, f->f_dir.ptr, f->f_dir.ptr + f->f_dir.len );
190
191     /* Put path separator between dir and file. */
192     /* Special case for root dir: do not add another path separator. */
193     if ( f->f_dir.len && ( f->f_base.len || f->f_suffix.len )
194 #if PATH_DELIM == '\\'
195         && !( f->f_dir.len == 3 && f->f_dir.ptr[ 1 ] == ':' )
196 #endif
197         && !( f->f_dir.len == 1 && is_path_delim( f->f_dir.ptr[ 0 ] ) ) )
198         string_push_back( file, as_path_delim( f->f_dir.ptr[ f->f_dir.len ] ) );
199
200     if ( f->f_base.len )
201         string_append_range( file, f->f_base.ptr, f->f_base.ptr + f->f_base.len
202             );
203
204     if ( f->f_suffix.len )
205         string_append_range( file, f->f_suffix.ptr, f->f_suffix.ptr +
206             f->f_suffix.len );
207
208     if ( f->f_member.len )
209     {
210         string_push_back( file, '(' );
211         string_append_range( file, f->f_member.ptr, f->f_member.ptr +
212             f->f_member.len );
213         string_push_back( file, ')' );
214     }
215 }
216
217
218 /*
219  * path_parent() - make a PATHNAME point to its parent dir
220  */
221
222 void path_parent( PATHNAME * f )
223 {
224     f->f_base.ptr = f->f_suffix.ptr = f->f_member.ptr = "";
225     f->f_base.len = f->f_suffix.len = f->f_member.len = 0;
226 }
227
228
229 /*
230  * path_tmpdir() - returns the system dependent temporary folder path
231  *
232  * Returned value is stored inside a static buffer and should not be modified.
233  * Returned value does *not* include a trailing path separator.
234  */
235
236 string const * path_tmpdir()
237 {
238     static string buffer[ 1 ];
239     static int have_result;
240     if ( !have_result )
241     {
242         string_new( buffer );
243         path_get_temp_path_( buffer );
244         have_result = 1;
245     }
246     return buffer;
247 }
248
249
250 /*
251  * path_tmpnam() - returns a new temporary name
252  */
253
254 OBJECT * path_tmpnam( void )
255 {
256     char name_buffer[ 64 ];
257     unsigned long const pid = path_get_process_id_();
258     static unsigned long t;
259     if ( !t ) t = time( 0 ) & 0xffff;
260     t += 1;
261     sprintf( name_buffer, "jam%lx%lx.000", pid, t );
262     return object_new( name_buffer );
263 }
264
265
266 /*
267  * path_tmpfile() - returns a new temporary path
268  */
269
270 OBJECT * path_tmpfile( void )
271 {
272     OBJECT * result;
273     OBJECT * tmpnam;
274
275     string file_path[ 1 ];
276     string_copy( file_path, path_tmpdir()->value );
277     string_push_back( file_path, PATH_DELIM );
278     tmpnam = path_tmpnam();
279     string_append( file_path, object_str( tmpnam ) );
280     object_free( tmpnam );
281     result = object_new( file_path->value );
282     string_free( file_path );
283
284     return result;
285 }