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/* 8 * pathunix.c - manipulate file names on UNIX, NT, OS2 9 * 10 * External routines: 11 * 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 15 * 16 * File_parse() and path_build() just manipuate a string and a structure; 17 * they do not make system calls. 18 * 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 36 */ 37 38# include "jam.h" 39# include "pathsys.h" 40 41# ifdef OS_MAC 42 43# define DELIM ':' 44 45/* 46 * path_parse() - split a file name into dir/base/suffix/member 47 */ 48 49void 50path_parse( 51 const char *file, 52 PATHNAME *f ) 53{ 54 const char *p, *q; 55 const char *end; 56 57 memset( (char *)f, 0, sizeof( *f ) ); 58 59 /* Look for <grist> */ 60 61 if( file[0] == '<' && ( p = strchr( file, '>' ) ) ) 62 { 63 f->f_grist.ptr = file; 64 f->f_grist.len = p - file; 65 file = p + 1; 66 } 67 68 /* Look for dir: */ 69 70 if( p = strrchr( file, DELIM ) ) 71 { 72 f->f_dir.ptr = file; 73 f->f_dir.len = p - file; 74 file = p + 1; 75 76 /* All :'s? Include last : as part of directory name */ 77 78 while( p > f->f_dir.ptr && *--p == DELIM ) 79 ; 80 81 if( p == f->f_dir.ptr ) 82 f->f_dir.len++; 83 } 84 85 end = file + strlen( file ); 86 87 /* Look for (member) */ 88 89 if( ( p = strchr( file, '(' ) ) && end[-1] == ')' ) 90 { 91 f->f_member.ptr = p + 1; 92 f->f_member.len = end - p - 2; 93 end = p; 94 } 95 96 /* Look for .suffix */ 97 /* This would be memrchr() */ 98 99 p = 0; 100 q = file; 101 102 while( q = memchr( q, '.', end - q ) ) 103 p = q++; 104 105 if( p ) 106 { 107 f->f_suffix.ptr = p; 108 f->f_suffix.len = end - p; 109 end = p; 110 } 111 112 /* Leaves base */ 113 114 f->f_base.ptr = file; 115 f->f_base.len = end - file; 116} 117 118/* 119 * path_build() - build a filename given dir/base/suffix/member 120 */ 121 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: */ 127 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 */ 134 135char grid[5][5] = { 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 } 142} ; 143 144static int 145file_flags( 146 const char *ptr, 147 int len ) 148{ 149 if( !len ) 150 return DIR_EMPTY; 151 if( len == 1 && ptr[0] == DELIM ) 152 return DIR_DOT; 153 if( len == 2 && ptr[0] == DELIM && ptr[1] == DELIM ) 154 return DIR_DOTDOT; 155 if( ptr[0] == DELIM ) 156 return DIR_REL; 157 return DIR_ABS; 158} 159 160void 161path_build( 162 PATHNAME *f, 163 char *file, 164 int binding ) 165{ 166 char *ofile = file; 167 int dflag, rflag, act; 168 169 if( DEBUG_SEARCH ) 170 { 171 printf("build file: "); 172 if( f->f_root.len ) 173 printf( "root = '%.*s' ", f->f_root.len, f->f_root.ptr ); 174 if( f->f_dir.len ) 175 printf( "dir = '%.*s' ", f->f_dir.len, f->f_dir.ptr ); 176 if( f->f_base.len ) 177 printf( "base = '%.*s' ", f->f_base.len, f->f_base.ptr ); 178 } 179 180 /* Start with the grist. If the current grist isn't */ 181 /* surrounded by <>'s, add them. */ 182 183 if( f->f_grist.len ) 184 { 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++ = '>'; 189 } 190 191 /* Combine root & directory, according to the grid. */ 192 193 dflag = file_flags( f->f_dir.ptr, f->f_dir.len ); 194 rflag = file_flags( f->f_root.ptr, f->f_root.len ); 195 196 switch( act = grid[ rflag ][ dflag ] ) 197 { 198 case G_DTDR: 199 /* :: of rel dir */ 200 *file++ = DELIM; 201 /* fall through */ 202 203 case G_DIR: 204 /* take dir */ 205 memcpy( file, f->f_dir.ptr, f->f_dir.len ); 206 file += f->f_dir.len; 207 break; 208 209 case G_ROOT: 210 /* take root */ 211 memcpy( file, f->f_root.ptr, f->f_root.len ); 212 file += f->f_root.len; 213 break; 214 215 case G_CAT: 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; 222 break; 223 224 case G_DDDD: 225 /* make it ::: (../..) */ 226 strcpy( file, ":::" ); 227 file += 3; 228 break; 229 } 230 231 /* Put : between dir and file (if none already) */ 232 233 if( act != G_MT && 234 file[-1] != DELIM && 235 ( f->f_base.len || f->f_suffix.len ) ) 236 { 237 *file++ = DELIM; 238 } 239 240 if( f->f_base.len ) 241 { 242 memcpy( file, f->f_base.ptr, f->f_base.len ); 243 file += f->f_base.len; 244 } 245 246 if( f->f_suffix.len ) 247 { 248 memcpy( file, f->f_suffix.ptr, f->f_suffix.len ); 249 file += f->f_suffix.len; 250 } 251 252 if( f->f_member.len ) 253 { 254 *file++ = '('; 255 memcpy( file, f->f_member.ptr, f->f_member.len ); 256 file += f->f_member.len; 257 *file++ = ')'; 258 } 259 *file = 0; 260 261 if( DEBUG_SEARCH ) 262 printf(" -> '%s'\n", ofile); 263} 264 265/* 266 * path_parent() - make a PATHNAME point to its parent dir 267 */ 268 269void 270path_parent( PATHNAME *f ) 271{ 272 /* just set everything else to nothing */ 273 274 f->f_base.ptr = 275 f->f_suffix.ptr = 276 f->f_member.ptr = ""; 277 278 f->f_base.len = 279 f->f_suffix.len = 280 f->f_member.len = 0; 281} 282 283# endif /* OS_MAC */ 284