| /* |
| * Copyright 1993-2002 Christopher Seiwald and Perforce Software, Inc. |
| * |
| * This file is part of Jam - see jam.c for Copyright information. |
| */ |
| |
| /* This file is ALSO: |
| * Copyright 2001-2004 David Abrahams. |
| * Copyright 2005 Rene Rivera. |
| * Distributed under the Boost Software License, Version 1.0. |
| * (See accompanying file LICENSE_1_0.txt or copy at |
| * http://www.boost.org/LICENSE_1_0.txt) |
| */ |
| |
| /* |
| * pathsys.c - platform independent path manipulation support |
| * |
| * External routines: |
| * path_build() - build a filename given dir/base/suffix/member |
| * path_parent() - make a PATHNAME point to its parent dir |
| * path_parse() - split a file name into dir/base/suffix/member |
| * path_tmpdir() - returns the system dependent temporary folder path |
| * path_tmpfile() - returns a new temporary path |
| * path_tmpnam() - returns a new temporary name |
| * |
| * File_parse() and path_build() just manipulate a string and a structure; |
| * they do not make system calls. |
| */ |
| |
| #include "jam.h" |
| #include "pathsys.h" |
| |
| #include "filesys.h" |
| |
| #include <stdlib.h> |
| #include <time.h> |
| |
| |
| /* Internal OS specific implementation details - have names ending with an |
| * underscore and are expected to be implemented in an OS specific pathXXX.c |
| * module. |
| */ |
| unsigned long path_get_process_id_( void ); |
| void path_get_temp_path_( string * buffer ); |
| |
| |
| /* |
| * path_parse() - split a file name into dir/base/suffix/member |
| */ |
| |
| void path_parse( char const * file, PATHNAME * f ) |
| { |
| char const * p; |
| char const * q; |
| char const * end; |
| |
| memset( (char *)f, 0, sizeof( *f ) ); |
| |
| /* Look for '<grist>'. */ |
| |
| if ( ( file[ 0 ] == '<' ) && ( p = strchr( file, '>' ) ) ) |
| { |
| f->f_grist.ptr = file; |
| f->f_grist.len = p - file; |
| file = p + 1; |
| } |
| |
| /* Look for 'dir/'. */ |
| |
| p = strrchr( file, '/' ); |
| |
| #if PATH_DELIM == '\\' |
| /* On NT, look for dir\ as well */ |
| { |
| char * const p1 = strrchr( p ? p + 1 : file, '\\' ); |
| if ( p1 ) p = p1; |
| } |
| #endif |
| |
| if ( p ) |
| { |
| f->f_dir.ptr = file; |
| f->f_dir.len = p - file; |
| |
| /* Special case for / - dirname is /, not "" */ |
| if ( !f->f_dir.len ) |
| ++f->f_dir.len; |
| |
| #if PATH_DELIM == '\\' |
| /* Special case for D:/ - dirname is D:/, not "D:" */ |
| if ( f->f_dir.len == 2 && file[ 1 ] == ':' ) |
| ++f->f_dir.len; |
| #endif |
| |
| file = p + 1; |
| } |
| |
| end = file + strlen( file ); |
| |
| /* Look for '(member)'. */ |
| if ( ( p = strchr( file, '(' ) ) && ( end[ -1 ] == ')' ) ) |
| { |
| f->f_member.ptr = p + 1; |
| f->f_member.len = end - p - 2; |
| end = p; |
| } |
| |
| /* Look for '.suffix'. This would be memrchr(). */ |
| p = 0; |
| for ( q = file; ( q = (char *)memchr( q, '.', end - q ) ); ++q ) |
| p = q; |
| if ( p ) |
| { |
| f->f_suffix.ptr = p; |
| f->f_suffix.len = end - p; |
| end = p; |
| } |
| |
| /* Leaves base. */ |
| f->f_base.ptr = file; |
| f->f_base.len = end - file; |
| } |
| |
| |
| /* |
| * is_path_delim() - true iff c is a path delimiter |
| */ |
| |
| static int is_path_delim( char const c ) |
| { |
| return c == PATH_DELIM |
| #if PATH_DELIM == '\\' |
| || c == '/' |
| #endif |
| ; |
| } |
| |
| |
| /* |
| * as_path_delim() - convert c to a path delimiter if it is not one already |
| */ |
| |
| static char as_path_delim( char const c ) |
| { |
| return is_path_delim( c ) ? c : PATH_DELIM; |
| } |
| |
| |
| /* |
| * path_build() - build a filename given dir/base/suffix/member |
| * |
| * To avoid changing slash direction on NT when reconstituting paths, instead of |
| * unconditionally appending PATH_DELIM we check the past-the-end character of |
| * the previous path element. If it is a path delimiter, we append that, and |
| * only append PATH_DELIM as a last resort. This heuristic is based on the fact |
| * that PATHNAME objects are usually the result of calling path_parse, which |
| * leaves the original slashes in the past-the-end position. Correctness depends |
| * on the assumption that all strings are zero terminated, so a past-the-end |
| * character will always be available. |
| * |
| * As an attendant patch, we had to ensure that backslashes are used explicitly |
| * in 'timestamp.c'. |
| */ |
| |
| void path_build( PATHNAME * f, string * file ) |
| { |
| file_build1( f, file ); |
| |
| /* Do not prepend root if it is '.' or the directory is rooted. */ |
| if ( f->f_root.len |
| && !( f->f_root.len == 1 && f->f_root.ptr[ 0 ] == '.' ) |
| && !( f->f_dir.len && f->f_dir.ptr[ 0 ] == '/' ) |
| #if PATH_DELIM == '\\' |
| && !( f->f_dir.len && f->f_dir.ptr[ 0 ] == '\\' ) |
| && !( f->f_dir.len && f->f_dir.ptr[ 1 ] == ':' ) |
| #endif |
| ) |
| { |
| string_append_range( file, f->f_root.ptr, f->f_root.ptr + f->f_root.len |
| ); |
| /* If 'root' already ends with a path delimeter, do not add another one. |
| */ |
| if ( !is_path_delim( f->f_root.ptr[ f->f_root.len - 1 ] ) ) |
| string_push_back( file, as_path_delim( f->f_root.ptr[ f->f_root.len |
| ] ) ); |
| } |
| |
| if ( f->f_dir.len ) |
| string_append_range( file, f->f_dir.ptr, f->f_dir.ptr + f->f_dir.len ); |
| |
| /* Put path separator between dir and file. */ |
| /* Special case for root dir: do not add another path separator. */ |
| if ( f->f_dir.len && ( f->f_base.len || f->f_suffix.len ) |
| #if PATH_DELIM == '\\' |
| && !( f->f_dir.len == 3 && f->f_dir.ptr[ 1 ] == ':' ) |
| #endif |
| && !( f->f_dir.len == 1 && is_path_delim( f->f_dir.ptr[ 0 ] ) ) ) |
| string_push_back( file, as_path_delim( f->f_dir.ptr[ f->f_dir.len ] ) ); |
| |
| if ( f->f_base.len ) |
| string_append_range( file, f->f_base.ptr, f->f_base.ptr + f->f_base.len |
| ); |
| |
| if ( f->f_suffix.len ) |
| string_append_range( file, f->f_suffix.ptr, f->f_suffix.ptr + |
| f->f_suffix.len ); |
| |
| if ( f->f_member.len ) |
| { |
| string_push_back( file, '(' ); |
| string_append_range( file, f->f_member.ptr, f->f_member.ptr + |
| f->f_member.len ); |
| string_push_back( file, ')' ); |
| } |
| } |
| |
| |
| /* |
| * path_parent() - make a PATHNAME point to its parent dir |
| */ |
| |
| void path_parent( PATHNAME * f ) |
| { |
| f->f_base.ptr = f->f_suffix.ptr = f->f_member.ptr = ""; |
| f->f_base.len = f->f_suffix.len = f->f_member.len = 0; |
| } |
| |
| |
| /* |
| * path_tmpdir() - returns the system dependent temporary folder path |
| * |
| * Returned value is stored inside a static buffer and should not be modified. |
| * Returned value does *not* include a trailing path separator. |
| */ |
| |
| string const * path_tmpdir() |
| { |
| static string buffer[ 1 ]; |
| static int have_result; |
| if ( !have_result ) |
| { |
| string_new( buffer ); |
| path_get_temp_path_( buffer ); |
| have_result = 1; |
| } |
| return buffer; |
| } |
| |
| |
| /* |
| * path_tmpnam() - returns a new temporary name |
| */ |
| |
| OBJECT * path_tmpnam( void ) |
| { |
| char name_buffer[ 64 ]; |
| unsigned long const pid = path_get_process_id_(); |
| static unsigned long t; |
| if ( !t ) t = time( 0 ) & 0xffff; |
| t += 1; |
| sprintf( name_buffer, "jam%lx%lx.000", pid, t ); |
| return object_new( name_buffer ); |
| } |
| |
| |
| /* |
| * path_tmpfile() - returns a new temporary path |
| */ |
| |
| OBJECT * path_tmpfile( void ) |
| { |
| OBJECT * result; |
| OBJECT * tmpnam; |
| |
| string file_path[ 1 ]; |
| string_copy( file_path, path_tmpdir()->value ); |
| string_push_back( file_path, PATH_DELIM ); |
| tmpnam = path_tmpnam(); |
| string_append( file_path, object_str( tmpnam ) ); |
| object_free( tmpnam ); |
| result = object_new( file_path->value ); |
| string_free( file_path ); |
| |
| return result; |
| } |