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