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 * pathsys.c - platform independent path manipulation support
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
26 * File_parse() and path_build() just manipulate a string and a structure;
27 * they do not make system calls.
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
43 unsigned long path_get_process_id_( void );
44 void path_get_temp_path_( string * buffer );
48 * path_parse() - split a file name into dir/base/suffix/member
51 void path_parse( char const * file, PATHNAME * f )
57 memset( (char *)f, 0, sizeof( *f ) );
59 /* Look for '<grist>'. */
61 if ( ( file[ 0 ] == '<' ) && ( p = strchr( file, '>' ) ) )
63 f->f_grist.ptr = file;
64 f->f_grist.len = p - file;
68 /* Look for 'dir/'. */
70 p = strrchr( file, '/' );
72 #if PATH_DELIM == '\\'
73 /* On NT, look for dir\ as well */
75 char * const p1 = strrchr( p ? p + 1 : file, '\\' );
83 f->f_dir.len = p - file;
85 /* Special case for / - dirname is /, not "" */
89 #if PATH_DELIM == '\\'
90 /* Special case for D:/ - dirname is D:/, not "D:" */
91 if ( f->f_dir.len == 2 && file[ 1 ] == ':' )
98 end = file + strlen( file );
100 /* Look for '(member)'. */
101 if ( ( p = strchr( file, '(' ) ) && ( end[ -1 ] == ')' ) )
103 f->f_member.ptr = p + 1;
104 f->f_member.len = end - p - 2;
108 /* Look for '.suffix'. This would be memrchr(). */
110 for ( q = file; ( q = (char *)memchr( q, '.', end - q ) ); ++q )
115 f->f_suffix.len = end - p;
120 f->f_base.ptr = file;
121 f->f_base.len = end - file;
126 * is_path_delim() - true iff c is a path delimiter
129 static int is_path_delim( char const c )
131 return c == PATH_DELIM
132 #if PATH_DELIM == '\\'
140 * as_path_delim() - convert c to a path delimiter if it is not one already
143 static char as_path_delim( char const c )
145 return is_path_delim( c ) ? c : PATH_DELIM;
150 * path_build() - build a filename given dir/base/suffix/member
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.
161 * As an attendant patch, we had to ensure that backslashes are used explicitly
165 void path_build( PATHNAME * f, string * file )
167 file_build1( f, file );
169 /* Do not prepend root if it is '.' or the directory is rooted. */
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 ] == ':' )
179 string_append_range( file, f->f_root.ptr, f->f_root.ptr + f->f_root.len
181 /* If 'root' already ends with a path delimeter, do not add another one.
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
189 string_append_range( file, f->f_dir.ptr, f->f_dir.ptr + f->f_dir.len );
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 ] == ':' )
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 ] ) );
201 string_append_range( file, f->f_base.ptr, f->f_base.ptr + f->f_base.len
204 if ( f->f_suffix.len )
205 string_append_range( file, f->f_suffix.ptr, f->f_suffix.ptr +
208 if ( f->f_member.len )
210 string_push_back( file, '(' );
211 string_append_range( file, f->f_member.ptr, f->f_member.ptr +
213 string_push_back( file, ')' );
219 * path_parent() - make a PATHNAME point to its parent dir
222 void path_parent( PATHNAME * f )
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;
230 * path_tmpdir() - returns the system dependent temporary folder path
232 * Returned value is stored inside a static buffer and should not be modified.
233 * Returned value does *not* include a trailing path separator.
236 string const * path_tmpdir()
238 static string buffer[ 1 ];
239 static int have_result;
242 string_new( buffer );
243 path_get_temp_path_( buffer );
251 * path_tmpnam() - returns a new temporary name
254 OBJECT * path_tmpnam( void )
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;
261 sprintf( name_buffer, "jam%lx%lx.000", pid, t );
262 return object_new( name_buffer );
267 * path_tmpfile() - returns a new temporary path
270 OBJECT * path_tmpfile( void )
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 );