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
48 void path_parse (const char *file
, PATHNAME
*f
) {
52 memset((char *)f
, 0, sizeof(*f
));
53 /* Look for <grist> */
54 if (file
[0] == '<' && (p
= strchr(file
, '>'))) {
55 f
->f_grist
.ptr
= file
;
56 f
->f_grist
.len
= p
- file
;
60 if (p
= strrchr(file
, DELIM
)) {
62 f
->f_dir
.len
= p
- file
;
64 /* All :'s? Include last : as part of directory name */
65 while (p
> f
->f_dir
.ptr
&& *--p
== DELIM
) ;
66 if (p
== f
->f_dir
.ptr
) f
->f_dir
.len
++;
68 end
= file
+strlen(file
);
69 /* Look for (member) */
70 if ((p
= strchr(file
, '(')) && end
[-1] == ')') {
71 f
->f_member
.ptr
= p
+1;
72 f
->f_member
.len
= end
-p
-2;
75 /* Look for .suffix */
76 /* This would be memrchr() */
79 while (q
= memchr(q
, '.', end
-q
)) p
= q
++;
82 f
->f_suffix
.len
= end
- p
;
87 f
->f_base
.len
= end
- file
;
92 * path_build() - build a filename given dir/base/suffix/member
94 # define DIR_EMPTY 0 /* "" */
95 # define DIR_DOT 1 /* : */
96 # define DIR_DOTDOT 2 /* :: */
97 # define DIR_ABS 3 /* dira:dirb: */
98 # define DIR_REL 4 /* :dira:dirb: */
100 # define G_DIR 0 /* take dir */
101 # define G_ROOT 1 /* take root */
102 # define G_CAT 2 /* prepend root to dir */
103 # define G_DTDR 3 /* :: of rel dir */
104 # define G_DDDD 4 /* make it ::: (../..) */
105 # define G_MT 5 /* leave it empty */
108 /* EMPTY DOT DOTDOT ABS REL */
109 /* EMPTY */ { G_MT
, G_DIR
, G_DIR
, G_DIR
, G_DIR
},
110 /* DOT */ { G_ROOT
, G_DIR
, G_DIR
, G_DIR
, G_DIR
},
111 /* DOTDOT */ { G_ROOT
, G_ROOT
, G_DDDD
, G_DIR
, G_DTDR
},
112 /* ABS */ { G_ROOT
, G_ROOT
, G_ROOT
, G_DIR
, G_CAT
},
113 /* REL */ { G_ROOT
, G_ROOT
, G_ROOT
, G_DIR
, G_CAT
}
123 if( len
== 1 && ptr
[0] == DELIM
)
125 if( len
== 2 && ptr
[0] == DELIM
&& ptr
[1] == DELIM
)
127 if( ptr
[0] == DELIM
)
139 int dflag
, rflag
, act
;
143 printf("build file: ");
145 printf( "root = '%.*s' ", f
->f_root
.len
, f
->f_root
.ptr
);
147 printf( "dir = '%.*s' ", f
->f_dir
.len
, f
->f_dir
.ptr
);
149 printf( "base = '%.*s' ", f
->f_base
.len
, f
->f_base
.ptr
);
152 /* Start with the grist. If the current grist isn't */
153 /* surrounded by <>'s, add them. */
157 if( f
->f_grist
.ptr
[0] != '<' ) *file
++ = '<';
158 memcpy( file
, f
->f_grist
.ptr
, f
->f_grist
.len
);
159 file
+= f
->f_grist
.len
;
160 if( file
[-1] != '>' ) *file
++ = '>';
163 /* Combine root & directory, according to the grid. */
165 dflag
= file_flags( f
->f_dir
.ptr
, f
->f_dir
.len
);
166 rflag
= file_flags( f
->f_root
.ptr
, f
->f_root
.len
);
168 switch( act
= grid
[ rflag
][ dflag
] )
177 memcpy( file
, f
->f_dir
.ptr
, f
->f_dir
.len
);
178 file
+= f
->f_dir
.len
;
183 memcpy( file
, f
->f_root
.ptr
, f
->f_root
.len
);
184 file
+= f
->f_root
.len
;
188 /* prepend root to dir */
189 memcpy( file
, f
->f_root
.ptr
, f
->f_root
.len
);
190 file
+= f
->f_root
.len
;
191 if( file
[-1] == DELIM
) --file
;
192 memcpy( file
, f
->f_dir
.ptr
, f
->f_dir
.len
);
193 file
+= f
->f_dir
.len
;
197 /* make it ::: (../..) */
198 strcpy( file
, ":::" );
203 /* Put : between dir and file (if none already) */
207 ( f
->f_base
.len
|| f
->f_suffix
.len
) )
214 memcpy( file
, f
->f_base
.ptr
, f
->f_base
.len
);
215 file
+= f
->f_base
.len
;
218 if( f
->f_suffix
.len
)
220 memcpy( file
, f
->f_suffix
.ptr
, f
->f_suffix
.len
);
221 file
+= f
->f_suffix
.len
;
224 if( f
->f_member
.len
)
227 memcpy( file
, f
->f_member
.ptr
, f
->f_member
.len
);
228 file
+= f
->f_member
.len
;
234 printf(" -> '%s'\n", ofile
);
238 * path_parent() - make a PATHNAME point to its parent dir
242 path_parent( PATHNAME
*f
)
244 /* just set everything else to nothing */
248 f
->f_member
.ptr
= "";