2 * Copyright 1993-2002 Christopher Seiwald and Perforce Software, Inc.
4 * This file is part of Jam - see jam.c for Copyright information.
8 * pathvms.c - manipulate file names on VMS
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 * 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.
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
36 * path_parse() - split a file name into dir/base/suffix/member
47 memset( (char *)f
, 0, sizeof( *f
) );
49 /* Look for <grist> */
51 if( file
[0] == '<' && ( p
= strchr( file
, '>' ) ) )
53 f
->f_grist
.ptr
= file
;
54 f
->f_grist
.len
= p
- file
;
58 /* Look for dev:[dir] or dev: */
60 if( ( p
= strchr( file
, ']' ) ) || ( p
= strchr( file
, ':' ) ) )
63 f
->f_dir
.len
= p
+ 1 - file
;
67 end
= file
+ strlen( file
);
69 /* Look for (member) */
71 if( ( p
= strchr( file
, '(' ) ) && end
[-1] == ')' )
73 f
->f_member
.ptr
= p
+ 1;
74 f
->f_member
.len
= end
- p
- 2;
78 /* Look for .suffix */
79 /* This would be memrchr() */
84 while( q
= (char *)memchr( q
, '.', end
- q
) )
90 f
->f_suffix
.len
= end
- p
;
97 f
->f_base
.len
= end
- file
;
99 /* Is this a directory without a file spec? */
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]
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]
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]
127 * Climbing to parent:
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] */
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] */
146 static int grid
[7][7] = {
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
,
175 if( *buf
&& *buf
++ == c
)
191 i
->flags
= DIR_EMPTY
;
197 else if( p
= strnchr( (char *)buf
, ':', len
) )
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
;
207 i
->dev
.ptr
= (char *)buf
;
209 i
->dir
.ptr
= (char *)buf
;
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
;
219 i
->flags
= DIR_ABSDIR
;
222 /* But if its rooted in any way */
224 if( i
->dir
.len
== 8 && !strncmp( i
->dir
.ptr
, "[000000]", 8 ) )
229 * path_build() - build a filename given dir/base/suffix/member
239 struct dirinf root
, dir
;
242 /* Start with the grist. If the current grist isn't */
243 /* surrounded by <>'s, add them. */
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
++ = '>';
253 /* Get info on root and dir for combining. */
255 dir_flags( f
->f_root
.ptr
, f
->f_root
.len
, &root
);
256 dir_flags( f
->f_dir
.ptr
, f
->f_dir
.len
, &dir
);
260 switch( g
= grid
[ root
.flags
][ dir
.flags
] )
264 memcpy( file
, f
->f_dir
.ptr
, f
->f_dir
.len
);
265 file
+= f
->f_dir
.len
;
270 memcpy( file
, f
->f_root
.ptr
, f
->f_root
.len
);
271 file
+= f
->f_root
.len
;
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
);
284 /* root's dev:[dir] + rel directory */
285 memcpy( file
, f
->f_root
.ptr
, f
->f_root
.len
);
286 file
+= f
->f_root
.len
;
288 /* sanity checks: root ends with ] */
290 if( file
[-1] == ']' )
293 /* Add . if separating two -'s */
299 memcpy( file
, dir
.dir
.ptr
+ 1, dir
.dir
.len
- 1 );
300 file
+= dir
.dir
.len
- 1;
304 /* root's dev + rel directory made abs */
305 memcpy( file
, root
.dev
.ptr
, root
.dev
.len
);
306 file
+= root
.dev
.len
;
308 /* skip [. of rel dir */
309 memcpy( file
, dir
.dir
.ptr
+ 2, dir
.dir
.len
- 2 );
310 file
+= dir
.dir
.len
- 2;
315 if( DEBUG_SEARCH
&& ( root
.flags
|| dir
.flags
) )
318 printf( "%d x %d = %d (%s)\n", root
.flags
, dir
.flags
,
319 grid
[ root
.flags
][ dir
.flags
], ofile
);
324 * Now do the special :P modifier when no file was present.
332 if( file
[-1] == ']' && f
->parent
)
334 while( file
-- > ofile
)
341 else if( *file
== '-' )
344 if( file
> ofile
&& file
[-1] == '.' )
349 else if( *file
== '[' )
357 strcpy( file
, "[000000]" );
365 /* Now copy the file pieces. */
369 memcpy( file
, f
->f_base
.ptr
, f
->f_base
.len
);
370 file
+= f
->f_base
.len
;
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. */
377 if( f
->f_suffix
.len
)
379 memcpy( file
, f
->f_suffix
.ptr
, f
->f_suffix
.len
);
380 file
+= f
->f_suffix
.len
;
382 else if( binding
&& f
->f_base
.len
)
387 if( f
->f_member
.len
)
390 memcpy( file
, f
->f_member
.ptr
, f
->f_member
.len
);
391 file
+= f
->f_member
.len
;
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
,
409 * path_parent() - make a PATHNAME point to its parent dir
413 path_parent( PATHNAME
*f
)
419 f
->f_member
.ptr
= "";