/* * Copyright 1993-2002 Christopher Seiwald and Perforce Software, Inc. * * This file is part of Jam - see jam.c for Copyright information. */ /* This file is ALSO: * Copyright 2001-2004 David Abrahams. * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt) */ # include "jam.h" # include "pathsys.h" # ifdef OS_VMS # define DEBUG /* * pathvms.c - manipulate file names on VMS * * External routines: * * path_parse() - split a file name into dir/base/suffix/member * path_build() - build a filename given dir/base/suffix/member * path_parent() - make a PATHNAME point to its parent dir * * File_parse() and path_build() just manipuate a string and a structure; * they do not make system calls. * * WARNING! This file contains voodoo logic, as black magic is * necessary for wrangling with VMS file name. Woe be to people * who mess with this code. * * 02/09/95 (seiwald) - bungled R=[xxx] - was using directory length! * 05/03/96 (seiwald) - split from filevms.c */ /* * path_parse() - split a file name into dir/base/suffix/member. */ void path_parse( char * file, PATHNAME * f ) { char * p; char * q; char * end; memset( (char *)f, 0, sizeof( *f ) ); /* Look for */ if ( ( file[0] == '<' ) && ( p = strchr( file, '>' ) ) ) { f->f_grist.ptr = file; f->f_grist.len = p - file; file = p + 1; } /* Look for dev:[dir] or dev: */ if ( ( p = strchr( file, ']' ) ) || ( p = strchr( file, ':' ) ) ) { f->f_dir.ptr = file; f->f_dir.len = p + 1 - file; file = p + 1; } end = file + strlen( file ); /* Look for (member). */ if ( ( p = strchr( file, '(' ) ) && ( end[ -1 ] == ')' ) ) { f->f_member.ptr = p + 1; f->f_member.len = end - p - 2; end = p; } /* Look for .suffix */ /* This would be memrchr(). */ p = 0; q = file; while ( q = (char *)memchr( q, '.', end - q ) ) p = q++; if ( p ) { f->f_suffix.ptr = p; f->f_suffix.len = end - p; end = p; } /* Leaves base. */ f->f_base.ptr = file; f->f_base.len = end - file; /* Is this a directory without a file spec? */ f->parent = 0; } /* * dir mods result * --- --- ------ * Rerooting: * * (none) :R=dev: dev: * devd: :R=dev: devd: * devd:[dir] :R=dev: devd:[dir] * [.dir] :R=dev: dev:[dir] questionable * [dir] :R=dev: dev:[dir] * * (none) :R=[rdir] [rdir] questionable * devd: :R=[rdir] devd: * devd:[dir] :R=[rdir] devd:[dir] * [.dir] :R=[rdir] [rdir.dir] questionable * [dir] :R=[rdir] [rdir] * * (none) :R=dev:[root] dev:[root] * devd: :R=dev:[root] devd: * devd:[dir] :R=dev:[root] devd:[dir] * [.dir] :R=dev:[root] dev:[root.dir] * [dir] :R=dev:[root] [dir] * * Climbing to parent: * */ # define DIR_EMPTY 0 /* empty string */ # define DIR_DEV 1 /* dev: */ # define DIR_DEVDIR 2 /* dev:[dir] */ # define DIR_DOTDIR 3 /* [.dir] */ # define DIR_DASHDIR 4 /* [-] or [-.dir] */ # define DIR_ABSDIR 5 /* [dir] */ # define DIR_ROOT 6 /* [000000] or dev:[000000] */ # define G_DIR 0 /* take just dir */ # define G_ROOT 1 /* take just root */ # define G_VAD 2 /* root's dev: + [abs] */ # define G_DRD 3 /* root's dev:[dir] + [.rel] */ # define G_VRD 4 /* root's dev: + [.rel] made [abs] */ # define G_DDD 5 /* root's dev:[dir] + . + [dir] */ static int grid[7][7] = { /* root/dir EMPTY DEV DEVDIR DOTDIR DASH, ABSDIR ROOT */ /* EMPTY */ G_DIR, G_DIR, G_DIR, G_DIR, G_DIR, G_DIR, G_DIR, /* DEV */ G_ROOT, G_DIR, G_DIR, G_VRD, G_VAD, G_VAD, G_VAD, /* DEVDIR */ G_ROOT, G_DIR, G_DIR, G_DRD, G_VAD, G_VAD, G_VAD, /* DOTDIR */ G_ROOT, G_DIR, G_DIR, G_DRD, G_DIR, G_DIR, G_DIR, /* DASHDIR */ G_ROOT, G_DIR, G_DIR, G_DRD, G_DDD, G_DIR, G_DIR, /* ABSDIR */ G_ROOT, G_DIR, G_DIR, G_DRD, G_DIR, G_DIR, G_DIR, /* ROOT */ G_ROOT, G_DIR, G_DIR, G_VRD, G_DIR, G_DIR, G_DIR, }; struct dirinf { int flags; struct { char * ptr; int len; } dev, dir; }; static char * strnchr( char * buf, int c, int len ) { while ( len-- ) if ( *buf && ( *buf++ == c ) ) return buf - 1; return 0; } static void dir_flags( char * buf, int len, struct dirinf * i ) { char * p; if ( !buf || !len ) { i->flags = DIR_EMPTY; i->dev.ptr = i->dir.ptr = 0; i->dev.len = i->dir.len = 0; } else if ( p = strnchr( buf, ':', len ) ) { i->dev.ptr = buf; i->dev.len = p + 1 - buf; i->dir.ptr = buf + i->dev.len; i->dir.len = len - i->dev.len; i->flags = i->dir.len && *i->dir.ptr == '[' ? DIR_DEVDIR : DIR_DEV; } else { i->dev.ptr = buf; i->dev.len = 0; i->dir.ptr = buf; i->dir.len = len; if ( ( *buf == '[' ) && ( buf[1] == ']' ) ) i->flags = DIR_EMPTY; else if ( ( *buf == '[' ) && ( buf[1] == '.' ) ) i->flags = DIR_DOTDIR; else if ( ( *buf == '[' ) && ( buf[1] == '-' ) ) i->flags = DIR_DASHDIR; else i->flags = DIR_ABSDIR; } /* But if its rooted in any way. */ if ( ( i->dir.len == 8 ) && !strncmp( i->dir.ptr, "[000000]", 8 ) ) i->flags = DIR_ROOT; } /* * path_build() - build a filename given dir/base/suffix/member */ void path_build( PATHNAME * f, string * file, int binding ) { struct dirinf root; struct dirinf dir; int g; file_build1( f, file ); /* Get info on root and dir for combining. */ dir_flags( f->f_root.ptr, f->f_root.len, &root ); dir_flags( f->f_dir.ptr, f->f_dir.len, &dir ); /* Combine. */ switch ( g = grid[ root.flags ][ dir.flags ] ) { case G_DIR: /* take dir */ string_append_range( file, f->f_dir.ptr, f->f_dir.ptr + f->f_dir.len ); break; case G_ROOT: /* take root */ string_append_range( file, f->f_root.ptr, f->f_root.ptr + f->f_root.len ); break; case G_VAD: /* root's dev + abs directory */ string_append_range( file, root.dev.ptr, root.dev.ptr + root.dev.len ); string_append_range( file, dir.dir.ptr, dir.dir.ptr + dir.dir.len ); break; case G_DRD: case G_DDD: /* root's dev:[dir] + rel directory */ string_append_range( file, f->f_root.ptr, f->f_root.ptr + f->f_root.len ); /* sanity checks: root ends with ] */ if ( file->value[file->size - 1] == ']' ) string_pop_back( file ); /* Add . if separating two -'s */ if ( g == G_DDD ) string_push_back( file, '.' ); /* skip [ of dir */ string_append_range( file, dir.dir.ptr + 1, dir.dir.ptr + 1 + dir.dir.len - 1 ); break; case G_VRD: /* root's dev + rel directory made abs */ string_append_range( file, root.dev.ptr, root.dev.ptr + root.dev.len ); string_push_back( file, '[' ); /* skip [. of rel dir */ string_append_range( file, dir.dir.ptr + 2, dir.dir.ptr + 2 + dir.dir.len - 2 ); break; } # ifdef DEBUG if ( DEBUG_SEARCH && ( root.flags || dir.flags ) ) printf( "%d x %d = %d (%s)\n", root.flags, dir.flags, grid[ root.flags ][ dir.flags ], file->value ); # endif /* * Now do the special :P modifier when no file was present. * (none) (none) * [dir1.dir2] [dir1] * [dir] [000000] * [.dir] (none) * [] [] */ if ( ( file->value[ file->size - 1 ] == ']' ) && f->parent ) { char * p = file->value + file->size; while ( p-- > file->value ) { if ( *p == '.' ) { /* If we've truncated everything and left with '[', return empty string. */ if ( p == file->value + 1 ) string_truncate( file, 0 ); else { string_truncate( file, p - file->value ); string_push_back( file, ']' ); } break; } if ( *p == '-' ) { /* handle .- or - */ if ( ( p > file->value ) && ( p[ -1 ] == '.' ) ) --p; *p++ = ']'; break; } if ( *p == '[' ) { if ( p[ 1 ] == ']' ) { /* CONSIDER: I don't see any use of this code. We immediately break, and 'p' is a local variable. */ p += 2; } else { string_truncate( file, p - file->value ); string_append( file, "[000000]" ); } break; } } } /* Now copy the file pieces. */ if ( f->f_base.len ) { string_append_range( file, f->f_base.ptr, f->f_base.ptr + f->f_base.len ); } /* If there is no suffix, we append a "." onto all generated names. This * keeps VMS from appending its own (wrong) idea of what the suffix should * be. */ if ( f->f_suffix.len ) string_append_range( file, f->f_suffix.ptr, f->f_suffix.ptr + f->f_suffix.len ); else if ( binding && f->f_base.len ) string_push_back( file, '.' ); if ( f->f_member.len ) { string_push_back( file, '(' ); string_append_range( file, f->f_member.ptr, f->f_member.ptr + f->f_member.len ); string_push_back( file, ')' ); } # ifdef DEBUG if ( DEBUG_SEARCH ) printf( "built %.*s + %.*s / %.*s suf %.*s mem %.*s -> %s\n", f->f_root.len, f->f_root.ptr, f->f_dir.len, f->f_dir.ptr, f->f_base.len, f->f_base.ptr, f->f_suffix.len, f->f_suffix.ptr, f->f_member.len, f->f_member.ptr, file->value ); # endif } /* * path_parent() - make a PATHNAME point to its parent dir */ void path_parent( PATHNAME * f ) { if ( f->f_base.len ) { f->f_base.ptr = f->f_suffix.ptr = f->f_member.ptr = ""; f->f_base.len = f->f_suffix.len = f->f_member.len = 0; } else { f->parent = 1; } } # endif /* VMS */