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