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 * pathvms.c - manipulate file names on VMS 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 * WARNING! This file contains voodoo logic, as black magic is 20 * necessary for wrangling with VMS file name. Woe be to people 21 * who mess with this code. 22 * 23 * 02/09/95 (seiwald) - bungled R=[xxx] - was using directory length! 24 * 05/03/96 (seiwald) - split from filevms.c 25 * 11/04/02 (seiwald) - const-ing for string literals 26 */ 27 28# include "jam.h" 29# include "pathsys.h" 30 31# ifdef OS_VMS 32 33# define DEBUG 34 35/* 36 * path_parse() - split a file name into dir/base/suffix/member 37 */ 38 39void 40path_parse( 41 const char *file, 42 PATHNAME *f ) 43{ 44 const char *p, *q; 45 const char *end; 46 47 memset( (char *)f, 0, sizeof( *f ) ); 48 49 /* Look for <grist> */ 50 51 if( file[0] == '<' && ( p = strchr( file, '>' ) ) ) 52 { 53 f->f_grist.ptr = file; 54 f->f_grist.len = p - file; 55 file = p + 1; 56 } 57 58 /* Look for dev:[dir] or dev: */ 59 60 if( ( p = strchr( file, ']' ) ) || ( p = strchr( file, ':' ) ) ) 61 { 62 f->f_dir.ptr = file; 63 f->f_dir.len = p + 1 - file; 64 file = p + 1; 65 } 66 67 end = file + strlen( file ); 68 69 /* Look for (member) */ 70 71 if( ( p = strchr( file, '(' ) ) && end[-1] == ')' ) 72 { 73 f->f_member.ptr = p + 1; 74 f->f_member.len = end - p - 2; 75 end = p; 76 } 77 78 /* Look for .suffix */ 79 /* This would be memrchr() */ 80 81 p = 0; 82 q = file; 83 84 while( q = (char *)memchr( q, '.', end - q ) ) 85 p = q++; 86 87 if( p ) 88 { 89 f->f_suffix.ptr = p; 90 f->f_suffix.len = end - p; 91 end = p; 92 } 93 94 /* Leaves base */ 95 96 f->f_base.ptr = file; 97 f->f_base.len = end - file; 98 99 /* Is this a directory without a file spec? */ 100 101 f->parent = 0; 102} 103 104/* 105 * dir mods result 106 * --- --- ------ 107 * Rerooting: 108 * 109 * (none) :R=dev: dev: 110 * devd: :R=dev: devd: 111 * devd:[dir] :R=dev: devd:[dir] 112 * [.dir] :R=dev: dev:[dir] questionable 113 * [dir] :R=dev: dev:[dir] 114 * 115 * (none) :R=[rdir] [rdir] questionable 116 * devd: :R=[rdir] devd: 117 * devd:[dir] :R=[rdir] devd:[dir] 118 * [.dir] :R=[rdir] [rdir.dir] questionable 119 * [dir] :R=[rdir] [rdir] 120 * 121 * (none) :R=dev:[root] dev:[root] 122 * devd: :R=dev:[root] devd: 123 * devd:[dir] :R=dev:[root] devd:[dir] 124 * [.dir] :R=dev:[root] dev:[root.dir] 125 * [dir] :R=dev:[root] [dir] 126 * 127 * Climbing to parent: 128 * 129 */ 130 131# define DIR_EMPTY 0 /* empty string */ 132# define DIR_DEV 1 /* dev: */ 133# define DIR_DEVDIR 2 /* dev:[dir] */ 134# define DIR_DOTDIR 3 /* [.dir] */ 135# define DIR_DASHDIR 4 /* [-] or [-.dir] */ 136# define DIR_ABSDIR 5 /* [dir] */ 137# define DIR_ROOT 6 /* [000000] or dev:[000000] */ 138 139# define G_DIR 0 /* take just dir */ 140# define G_ROOT 1 /* take just root */ 141# define G_VAD 2 /* root's dev: + [abs] */ 142# define G_DRD 3 /* root's dev:[dir] + [.rel] */ 143# define G_VRD 4 /* root's dev: + [.rel] made [abs] */ 144# define G_DDD 5 /* root's dev:[dir] + . + [dir] */ 145 146static int grid[7][7] = { 147 148/* root/dir EMPTY DEV DEVDIR DOTDIR DASH, ABSDIR ROOT */ 149/* EMPTY */ G_DIR, G_DIR, G_DIR, G_DIR, G_DIR, G_DIR, G_DIR, 150/* DEV */ G_ROOT, G_DIR, G_DIR, G_VRD, G_VAD, G_VAD, G_VAD, 151/* DEVDIR */ G_ROOT, G_DIR, G_DIR, G_DRD, G_VAD, G_VAD, G_VAD, 152/* DOTDIR */ G_ROOT, G_DIR, G_DIR, G_DRD, G_DIR, G_DIR, G_DIR, 153/* DASHDIR */ G_ROOT, G_DIR, G_DIR, G_DRD, G_DDD, G_DIR, G_DIR, 154/* ABSDIR */ G_ROOT, G_DIR, G_DIR, G_DRD, G_DIR, G_DIR, G_DIR, 155/* ROOT */ G_ROOT, G_DIR, G_DIR, G_VRD, G_DIR, G_DIR, G_DIR, 156 157} ; 158 159struct dirinf { 160 int flags; 161 162 struct { 163 char *ptr; 164 int len; 165 } dev, dir; 166} ; 167 168static char * 169strnchr( 170 char *buf, 171 int c, 172 int len ) 173{ 174 while( len-- ) 175 if( *buf && *buf++ == c ) 176 return buf - 1; 177 178 return 0; 179} 180 181static void 182dir_flags( 183 const char *buf, 184 int len, 185 struct dirinf *i ) 186{ 187 const char *p; 188 189 if( !buf || !len ) 190 { 191 i->flags = DIR_EMPTY; 192 i->dev.ptr = 193 i->dir.ptr = 0; 194 i->dev.len = 195 i->dir.len = 0; 196 } 197 else if( p = strnchr( (char *)buf, ':', len ) ) 198 { 199 i->dev.ptr = (char *)buf; 200 i->dev.len = p + 1 - buf; 201 i->dir.ptr = (char *)buf + i->dev.len; 202 i->dir.len = len - i->dev.len; 203 i->flags = i->dir.len && *i->dir.ptr == '[' ? DIR_DEVDIR : DIR_DEV; 204 } 205 else 206 { 207 i->dev.ptr = (char *)buf; 208 i->dev.len = 0; 209 i->dir.ptr = (char *)buf; 210 i->dir.len = len; 211 212 if( *buf == '[' && buf[1] == ']' ) 213 i->flags = DIR_EMPTY; 214 else if( *buf == '[' && buf[1] == '.' ) 215 i->flags = DIR_DOTDIR; 216 else if( *buf == '[' && buf[1] == '-' ) 217 i->flags = DIR_DASHDIR; 218 else 219 i->flags = DIR_ABSDIR; 220 } 221 222 /* But if its rooted in any way */ 223 224 if( i->dir.len == 8 && !strncmp( i->dir.ptr, "[000000]", 8 ) ) 225 i->flags = DIR_ROOT; 226} 227 228/* 229 * path_build() - build a filename given dir/base/suffix/member 230 */ 231 232void 233path_build( 234 PATHNAME *f, 235 char *file, 236 int binding ) 237{ 238 char *ofile = file; 239 struct dirinf root, dir; 240 int g; 241 242 /* Start with the grist. If the current grist isn't */ 243 /* surrounded by <>'s, add them. */ 244 245 if( f->f_grist.len ) 246 { 247 if( f->f_grist.ptr[0] != '<' ) *file++ = '<'; 248 memcpy( file, f->f_grist.ptr, f->f_grist.len ); 249 file += f->f_grist.len; 250 if( file[-1] != '>' ) *file++ = '>'; 251 } 252 253 /* Get info on root and dir for combining. */ 254 255 dir_flags( f->f_root.ptr, f->f_root.len, &root ); 256 dir_flags( f->f_dir.ptr, f->f_dir.len, &dir ); 257 258 /* Combine */ 259 260 switch( g = grid[ root.flags ][ dir.flags ] ) 261 { 262 case G_DIR: 263 /* take dir */ 264 memcpy( file, f->f_dir.ptr, f->f_dir.len ); 265 file += f->f_dir.len; 266 break; 267 268 case G_ROOT: 269 /* take root */ 270 memcpy( file, f->f_root.ptr, f->f_root.len ); 271 file += f->f_root.len; 272 break; 273 274 case G_VAD: 275 /* root's dev + abs directory */ 276 memcpy( file, root.dev.ptr, root.dev.len ); 277 file += root.dev.len; 278 memcpy( file, dir.dir.ptr, dir.dir.len ); 279 file += dir.dir.len; 280 break; 281 282 case G_DRD: 283 case G_DDD: 284 /* root's dev:[dir] + rel directory */ 285 memcpy( file, f->f_root.ptr, f->f_root.len ); 286 file += f->f_root.len; 287 288 /* sanity checks: root ends with ] */ 289 290 if( file[-1] == ']' ) 291 --file; 292 293 /* Add . if separating two -'s */ 294 295 if( g == G_DDD ) 296 *file++ = '.'; 297 298 /* skip [ of dir */ 299 memcpy( file, dir.dir.ptr + 1, dir.dir.len - 1 ); 300 file += dir.dir.len - 1; 301 break; 302 303 case G_VRD: 304 /* root's dev + rel directory made abs */ 305 memcpy( file, root.dev.ptr, root.dev.len ); 306 file += root.dev.len; 307 *file++ = '['; 308 /* skip [. of rel dir */ 309 memcpy( file, dir.dir.ptr + 2, dir.dir.len - 2 ); 310 file += dir.dir.len - 2; 311 break; 312 } 313 314# ifdef DEBUG 315 if( DEBUG_SEARCH && ( root.flags || dir.flags ) ) 316 { 317 *file = 0; 318 printf( "%d x %d = %d (%s)\n", root.flags, dir.flags, 319 grid[ root.flags ][ dir.flags ], ofile ); 320 } 321# endif 322 323 /* 324 * Now do the special :P modifier when no file was present. 325 * (none) (none) 326 * [dir1.dir2] [dir1] 327 * [dir] [000000] 328 * [.dir] [] 329 * [] [] 330 */ 331 332 if( file[-1] == ']' && f->parent ) 333 { 334 while( file-- > ofile ) 335 { 336 if( *file == '.' ) 337 { 338 *file++ = ']'; 339 break; 340 } 341 else if( *file == '-' ) 342 { 343 /* handle .- or - */ 344 if( file > ofile && file[-1] == '.' ) 345 --file; 346 *file++ = ']'; 347 break; 348 } 349 else if( *file == '[' ) 350 { 351 if( file[1] == ']' ) 352 { 353 file += 2; 354 } 355 else 356 { 357 strcpy( file, "[000000]" ); 358 file += 8; 359 } 360 break; 361 } 362 } 363 } 364 365 /* Now copy the file pieces. */ 366 367 if( f->f_base.len ) 368 { 369 memcpy( file, f->f_base.ptr, f->f_base.len ); 370 file += f->f_base.len; 371 } 372 373 /* If there is no suffix, we append a "." onto all generated */ 374 /* names. This keeps VMS from appending its own (wrong) idea */ 375 /* of what the suffix should be. */ 376 377 if( f->f_suffix.len ) 378 { 379 memcpy( file, f->f_suffix.ptr, f->f_suffix.len ); 380 file += f->f_suffix.len; 381 } 382 else if( binding && f->f_base.len ) 383 { 384 *file++ = '.'; 385 } 386 387 if( f->f_member.len ) 388 { 389 *file++ = '('; 390 memcpy( file, f->f_member.ptr, f->f_member.len ); 391 file += f->f_member.len; 392 *file++ = ')'; 393 } 394 *file = 0; 395 396# ifdef DEBUG 397 if( DEBUG_SEARCH ) 398 printf("built %.*s + %.*s / %.*s suf %.*s mem %.*s -> %s\n", 399 f->f_root.len, f->f_root.ptr, 400 f->f_dir.len, f->f_dir.ptr, 401 f->f_base.len, f->f_base.ptr, 402 f->f_suffix.len, f->f_suffix.ptr, 403 f->f_member.len, f->f_member.ptr, 404 ofile ); 405# endif 406} 407 408/* 409 * path_parent() - make a PATHNAME point to its parent dir 410 */ 411 412void 413path_parent( PATHNAME *f ) 414{ 415 if( f->f_base.len ) 416 { 417 f->f_base.ptr = 418 f->f_suffix.ptr = 419 f->f_member.ptr = ""; 420 421 f->f_base.len = 422 f->f_suffix.len = 423 f->f_member.len = 0; 424 } 425 else 426 { 427 f->parent = 1; 428 } 429} 430 431# endif /* VMS */ 432