2 * Copyright 1993-2002 Christopher Seiwald and Perforce Software, Inc.
4 * This file is part of Jam - see jam.c for Copyright information.
8 * pathunix.c - manipulate file names on UNIX, NT, OS2
12 * path_parse() - split a file name into dir/base/suffix/member
13 * path_build() - build a filename given dir/base/suffix/member
14 * path_parent() - make a PATHNAME point to its parent dir
16 * File_parse() and path_build() just manipuate a string and a structure;
17 * they do not make system calls.
19 * 04/08/94 (seiwald) - Coherent/386 support added.
20 * 12/26/93 (seiwald) - handle dir/.suffix properly in path_build()
21 * 12/19/94 (mikem) - solaris string table insanity support
22 * 12/21/94 (wingerd) Use backslashes for pathnames - the NT way.
23 * 02/14/95 (seiwald) - parse and build /xxx properly
24 * 02/23/95 (wingerd) Compilers on NT can handle "/" in pathnames, so we
25 * should expect hdr searches to come up with strings
26 * like "thing/thing.h". So we need to test for "/" as
27 * well as "\" when parsing pathnames.
28 * 03/16/95 (seiwald) - fixed accursed typo on line 69.
29 * 05/03/96 (seiwald) - split from filent.c, fileunix.c
30 * 12/20/96 (seiwald) - when looking for the rightmost . in a file name,
31 * don't include the archive member name.
32 * 01/10/01 (seiwald) - path_parse now strips the trailing : from the
33 * directory name, unless the directory name is all
34 * :'s, so that $(d:P) works.
35 * 11/04/02 (seiwald) - const-ing for string literals
46 * path_parse() - split a file name into dir/base/suffix/member
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
;
70 if( p
= strrchr( file
, DELIM
) )
73 f
->f_dir
.len
= p
- file
;
76 /* All :'s? Include last : as part of directory name */
78 while( p
> f
->f_dir
.ptr
&& *--p
== DELIM
)
81 if( p
== f
->f_dir
.ptr
)
85 end
= file
+ strlen( file
);
87 /* Look for (member) */
89 if( ( p
= strchr( file
, '(' ) ) && end
[-1] == ')' )
91 f
->f_member
.ptr
= p
+ 1;
92 f
->f_member
.len
= end
- p
- 2;
96 /* Look for .suffix */
97 /* This would be memrchr() */
102 while( q
= memchr( q
, '.', end
- q
) )
108 f
->f_suffix
.len
= end
- p
;
114 f
->f_base
.ptr
= file
;
115 f
->f_base
.len
= end
- file
;
119 * path_build() - build a filename given dir/base/suffix/member
122 # define DIR_EMPTY 0 /* "" */
123 # define DIR_DOT 1 /* : */
124 # define DIR_DOTDOT 2 /* :: */
125 # define DIR_ABS 3 /* dira:dirb: */
126 # define DIR_REL 4 /* :dira:dirb: */
128 # define G_DIR 0 /* take dir */
129 # define G_ROOT 1 /* take root */
130 # define G_CAT 2 /* prepend root to dir */
131 # define G_DTDR 3 /* :: of rel dir */
132 # define G_DDDD 4 /* make it ::: (../..) */
133 # define G_MT 5 /* leave it empty */
136 /* EMPTY DOT DOTDOT ABS REL */
137 /* EMPTY */ { G_MT
, G_DIR
, G_DIR
, G_DIR
, G_DIR
},
138 /* DOT */ { G_ROOT
, G_DIR
, G_DIR
, G_DIR
, G_DIR
},
139 /* DOTDOT */ { G_ROOT
, G_ROOT
, G_DDDD
, G_DIR
, G_DTDR
},
140 /* ABS */ { G_ROOT
, G_ROOT
, G_ROOT
, G_DIR
, G_CAT
},
141 /* REL */ { G_ROOT
, G_ROOT
, G_ROOT
, G_DIR
, G_CAT
}
151 if( len
== 1 && ptr
[0] == DELIM
)
153 if( len
== 2 && ptr
[0] == DELIM
&& ptr
[1] == DELIM
)
155 if( ptr
[0] == DELIM
)
167 int dflag
, rflag
, act
;
171 printf("build file: ");
173 printf( "root = '%.*s' ", f
->f_root
.len
, f
->f_root
.ptr
);
175 printf( "dir = '%.*s' ", f
->f_dir
.len
, f
->f_dir
.ptr
);
177 printf( "base = '%.*s' ", f
->f_base
.len
, f
->f_base
.ptr
);
180 /* Start with the grist. If the current grist isn't */
181 /* surrounded by <>'s, add them. */
185 if( f
->f_grist
.ptr
[0] != '<' ) *file
++ = '<';
186 memcpy( file
, f
->f_grist
.ptr
, f
->f_grist
.len
);
187 file
+= f
->f_grist
.len
;
188 if( file
[-1] != '>' ) *file
++ = '>';
191 /* Combine root & directory, according to the grid. */
193 dflag
= file_flags( f
->f_dir
.ptr
, f
->f_dir
.len
);
194 rflag
= file_flags( f
->f_root
.ptr
, f
->f_root
.len
);
196 switch( act
= grid
[ rflag
][ dflag
] )
205 memcpy( file
, f
->f_dir
.ptr
, f
->f_dir
.len
);
206 file
+= f
->f_dir
.len
;
211 memcpy( file
, f
->f_root
.ptr
, f
->f_root
.len
);
212 file
+= f
->f_root
.len
;
216 /* prepend root to dir */
217 memcpy( file
, f
->f_root
.ptr
, f
->f_root
.len
);
218 file
+= f
->f_root
.len
;
219 if( file
[-1] == DELIM
) --file
;
220 memcpy( file
, f
->f_dir
.ptr
, f
->f_dir
.len
);
221 file
+= f
->f_dir
.len
;
225 /* make it ::: (../..) */
226 strcpy( file
, ":::" );
231 /* Put : between dir and file (if none already) */
235 ( f
->f_base
.len
|| f
->f_suffix
.len
) )
242 memcpy( file
, f
->f_base
.ptr
, f
->f_base
.len
);
243 file
+= f
->f_base
.len
;
246 if( f
->f_suffix
.len
)
248 memcpy( file
, f
->f_suffix
.ptr
, f
->f_suffix
.len
);
249 file
+= f
->f_suffix
.len
;
252 if( f
->f_member
.len
)
255 memcpy( file
, f
->f_member
.ptr
, f
->f_member
.len
);
256 file
+= f
->f_member
.len
;
262 printf(" -> '%s'\n", ofile
);
266 * path_parent() - make a PATHNAME point to its parent dir
270 path_parent( PATHNAME
*f
)
272 /* just set everything else to nothing */
276 f
->f_member
.ptr
= "";