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 /*  This file is ALSO:
8  *  Copyright 2001-2004 David Abrahams.
9  *  Copyright 2005 Rene Rivera.
10  *  Copyright 2015 Artur Shepilko.
11  *  Distributed under the Boost Software License, Version 1.0.
12  *  (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt)
13  */
14 
15 
16 #include "jam.h"
17 #include "filesys.h"
18 
19 #include "object.h"
20 #include "pathsys.h"
21 #include "strings.h"
22 
23 
24 #ifdef OS_VMS
25 
26 /*
27  * filevms.c - manipulate file names and scan directories on VMS.
28  *
29  * This implementation is based on POSIX-style path manipulation.
30  *
31  * VMS CTRL directly supports both POSIX- and native VMS-style path expressions,
32  * with the POSIX-to-VMS path translation performed internally by the same
33  * set of functions. For the most part such processing is transparent, with
34  * few differences mainly related to file versions (in POSIX mode only the recent
35  * version is visible).
36  *
37  * This should allow us to re-use fileunix.c implementation,
38  * excluding archive/library member processing.
39  *
40  * Thus in jam-files the path references can also remain POSIX/UNIX-style on all
41  * levels EXCEPT in actions scope, where these must be translated to the native
42  * VMS-style. This approach is somewhat similar to jam CYGWIN handling.
43  *
44  *
45  * External routines:
46  *  file_archscan()                 - scan an archive for files
47  *  file_mkdir()                    - create a directory
48  *  file_supported_fmt_resolution() - file modification timestamp resolution
49  *
50  * External routines called only via routines in filesys.c:
51  *  file_collect_dir_content_() - collects directory content information
52  *  file_dirscan_()             - OS specific file_dirscan() implementation
53  *  file_query_()               - query information about a path from the OS
54  *  file_collect_archive_content_() - collects information about archive members
55  *  file_archivescan_()         - OS specific file_archivescan() implementation
56  */
57 
58 #include <assert.h>
59 #include <stdio.h>
60 
61 #include <sys/stat.h>  /* needed for mkdir() */
62 #include <unistd.h>  /* needed for read and close prototype */
63 
64 #include <dirent.h>
65 #define STRUCT_DIRENT struct dirent
66 
67 
68 void path_translate_to_os_( char const * f, string * file );
69 
70 /*
71  * file_collect_dir_content_() - collects directory content information
72  */
73 
file_collect_dir_content_(file_info_t * const d)74 int file_collect_dir_content_( file_info_t * const d )
75 {
76     LIST * files = L0;
77     PATHNAME f;
78     DIR * dd;
79     STRUCT_DIRENT * dirent;
80     string path[ 1 ];
81     char const * dirstr;
82 
83     assert( d );
84     assert( d->is_dir );
85     assert( list_empty( d->files ) );
86 
87     dirstr = object_str( d->name );
88 
89     memset( (char *)&f, '\0', sizeof( f ) );
90     f.f_dir.ptr = dirstr;
91     f.f_dir.len = strlen( dirstr );
92 
93     if ( !*dirstr ) dirstr = ".";
94 
95     if ( !( dd = opendir( dirstr ) ) )
96         return -1;
97 
98     string_new( path );
99     while ( ( dirent = readdir( dd ) ) )
100     {
101         OBJECT * name;
102         f.f_base.ptr = dirent->d_name
103         #ifdef old_sinix
104             - 2  /* Broken structure definition on sinix. */
105         #endif
106             ;
107         f.f_base.len = strlen( f.f_base.ptr );
108 
109         string_truncate( path, 0 );
110         path_build( &f, path );
111         name = object_new( path->value );
112         /* Immediately stat the file to preserve invariants. */
113         if ( file_query( name ) )
114             files = list_push_back( files, name );
115         else
116             object_free( name );
117     }
118     string_free( path );
119 
120     closedir( dd );
121 
122     d->files = files;
123     return 0;
124 }
125 
126 
127 /*
128  * file_dirscan_() - OS specific file_dirscan() implementation
129  */
130 
file_dirscan_(file_info_t * const d,scanback func,void * closure)131 void file_dirscan_( file_info_t * const d, scanback func, void * closure )
132 {
133     assert( d );
134     assert( d->is_dir );
135 
136     /* Special case / : enter it */
137     if ( !strcmp( object_str( d->name ), "/" ) )
138         (*func)( closure, d->name, 1 /* stat()'ed */, &d->time );
139 }
140 
141 
142 /*
143  * file_mkdir() - create a directory
144  */
145 
file_mkdir(char const * const path)146 int file_mkdir( char const * const path )
147 {
148     /* Explicit cast to remove const modifiers and avoid related compiler
149      * warnings displayed when using the intel compiler.
150      */
151     return mkdir( (char *)path, 0777 );
152 }
153 
154 
155 /*
156  * file_query_() - query information about a path from the OS
157  */
158 
file_query_(file_info_t * const info)159 void file_query_( file_info_t * const info )
160 {
161     file_query_posix_( info );
162 }
163 
164 
165 /*------------------------------------------------------------------------------
166 * VMS-specific processing:
167 *
168 */
169 
170 #include <descrip.h>
171 #include <lbrdef.h>
172 #include <credef.h>
173 #include <mhddef.h>
174 #include <lhidef.h>
175 #include <lib$routines.h>
176 #include <starlet.h>
177 
178 /* Supply missing prototypes for lbr$-routines*/
179 
180 #ifdef __cplusplus
181 extern "C" {
182 #endif /* __cplusplus */
183 
184 int lbr$set_module(
185     void **,
186     unsigned long *,
187     struct dsc$descriptor_s *,
188     unsigned short *,
189     void * );
190 
191 int lbr$open( void **,
192     struct dsc$descriptor_s *,
193     void *,
194     void *,
195     void *,
196     void *,
197     void * );
198 
199 int lbr$ini_control(
200     void **,
201     unsigned long *,
202     unsigned long *,
203     void * );
204 
205 int lbr$get_index(
206     void **,
207     unsigned long * const,
208     int (*func)( struct dsc$descriptor_s *, unsigned long *),
209     void * );
210 
211 int lbr$search(
212     void **,
213     unsigned long * const,
214     unsigned short *,
215     int (*func)( struct dsc$descriptor_s *, unsigned long *),
216     unsigned long *);
217 
218 int lbr$close(
219     void ** );
220 
221 #ifdef __cplusplus
222 }
223 #endif /* __cplusplus */
224 
225 
226 
227 static void
file_cvttime(unsigned int * curtime,time_t * unixtime)228 file_cvttime(
229     unsigned int *curtime,
230     time_t *unixtime )
231 {
232     static const size_t divisor = 10000000;
233     static unsigned int bastim[2] = { 0x4BEB4000, 0x007C9567 }; /* 1/1/1970 */
234     int delta[2], remainder;
235 
236     lib$subx( curtime, bastim, delta );
237     lib$ediv( &divisor, delta, unixtime, &remainder );
238 }
239 
240 
downcase_inplace(char * p)241 static void downcase_inplace( char * p )
242 {
243     for ( ; *p; ++p )
244         *p = tolower( *p );
245 }
246 
247 
248 static file_archive_info_t * m_archive = NULL;
249 static file_info_t * m_member_found = NULL;
250 static void * m_lbr_context = NULL;
251 static unsigned short * m_rfa_found = NULL;
252 static const unsigned long LBR_MODINDEX_NUM = 1,
253                            LBR_SYMINDEX_NUM = 2;  /* GST:global symbol table */
254 
255 
set_archive_symbol(struct dsc$descriptor_s * symbol,unsigned long * rfa)256 static unsigned int set_archive_symbol( struct dsc$descriptor_s *symbol,
257                                         unsigned long *rfa )
258 {
259     file_info_t * member = m_member_found;
260     char buf[ MAXJPATH ] = { 0 };
261 
262     strncpy(buf, symbol->dsc$a_pointer, symbol->dsc$w_length);
263     buf[ symbol->dsc$w_length ] = 0;
264 
265     member->files = list_push_back( member->files, object_new( buf ) );
266 
267     return ( 1 ); /* continue */
268 }
269 
270 
set_archive_member(struct dsc$descriptor_s * module,unsigned long * rfa)271 static unsigned int set_archive_member( struct dsc$descriptor_s *module,
272                                         unsigned long *rfa )
273 {
274     file_archive_info_t * archive = m_archive;
275 
276     static struct dsc$descriptor_s bufdsc =
277           {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, NULL};
278 
279     struct mhddef *mhd;
280     char filename[128] = { 0 };
281     char buf[ MAXJPATH ] = { 0 };
282 
283     int status;
284     time_t library_date;
285 
286     register int i;
287     register char *p;
288 
289     bufdsc.dsc$a_pointer = filename;
290     bufdsc.dsc$w_length = sizeof( filename );
291     status = lbr$set_module( &m_lbr_context, rfa, &bufdsc,
292                  &bufdsc.dsc$w_length, NULL );
293 
294     if ( !(status & 1) )
295         return ( 1 );  /* continue */
296 
297     mhd = (struct mhddef *)filename;
298 
299     file_cvttime( &mhd->mhd$l_datim, &library_date );
300 
301     /* strncpy( filename, module->dsc$a_pointer, module->dsc$w_length );
302     */
303     for ( i = 0, p = module->dsc$a_pointer; i < module->dsc$w_length; ++i, ++p )
304     filename[ i ] = *p;
305 
306     filename[ i ] = '\0';
307 
308     if ( strcmp( filename, "" ) != 0 )
309     {
310         file_info_t * member = 0;
311 
312         /* Construct member's filename as lowercase "module.obj" */
313         sprintf( buf, "%s.obj", filename );
314         downcase_inplace( buf );
315         archive->members = filelist_push_back( archive->members, object_new( buf ) );
316 
317         member = filelist_back( archive->members );
318         member->is_file = 1;
319         member->is_dir = 0;
320         member->exists = 0;
321         timestamp_init( &member->time, (time_t)library_date, 0 );
322 
323         m_member_found = member;
324         m_rfa_found = rfa;
325         status = lbr$search(&m_lbr_context, &LBR_SYMINDEX_NUM, m_rfa_found, set_archive_symbol, NULL);
326     }
327 
328     return ( 1 ); /* continue */
329 }
330 
331 
332 
file_archscan(char const * arch,scanback func,void * closure)333 void file_archscan( char const * arch, scanback func, void * closure )
334 {
335     OBJECT * path = object_new( arch );
336     file_archive_info_t * archive = file_archive_query( path );
337 
338     object_free( path );
339 
340     if ( filelist_empty( archive->members ) )
341     {
342         if ( DEBUG_BINDSCAN )
343             out_printf( "scan archive %s\n", object_str( archive->file->name ) );
344 
345         if ( file_collect_archive_content_( archive ) < 0 )
346             return;
347     }
348 
349     /* Report the collected archive content. */
350     {
351         FILELISTITER iter = filelist_begin( archive->members );
352         FILELISTITER const end = filelist_end( archive->members );
353         char buf[ MAXJPATH ];
354 
355         for ( ; iter != end ; iter = filelist_next( iter ) )
356         {
357             file_info_t * member_file = filelist_item( iter );
358             LIST * symbols = member_file->files;
359 
360             /* Construct member path: 'archive-path(member-name)'
361              */
362             sprintf( buf, "%s(%s)",
363                 object_str( archive->file->name ),
364                 object_str( member_file->name ) );
365             {
366                 OBJECT * const member = object_new( buf );
367                 (*func)( closure, member, 1 /* time valid */, &member_file->time );
368                 object_free( member );
369             }
370         }
371     }
372 }
373 
374 
375 /*
376  *  file_archivescan_()         - OS specific file_archivescan() implementation
377  */
file_archivescan_(file_archive_info_t * const archive,archive_scanback func,void * closure)378 void file_archivescan_( file_archive_info_t * const archive, archive_scanback func,
379                         void * closure )
380 {
381 }
382 
383 
384 /*
385  *  file_collect_archive_content_() - collects information about archive members
386  */
387 
file_collect_archive_content_(file_archive_info_t * const archive)388 int file_collect_archive_content_( file_archive_info_t * const archive )
389 {
390     unsigned short rfa[3];
391 
392     static struct dsc$descriptor_s library =
393           {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, NULL};
394 
395     unsigned long lfunc = LBR$C_READ;
396     unsigned long typ = LBR$C_TYP_UNK;
397 
398     register int status;
399     string buf[ 1 ];
400     char vmspath[ MAXJPATH ] = { 0 };
401 
402     m_archive = archive;
403 
404     if ( ! filelist_empty( archive->members ) ) filelist_free( archive->members );
405 
406     /*  Translate path to VMS
407      */
408     string_new( buf );
409     path_translate_to_os_( object_str( archive->file->name ), buf );
410     strcpy( vmspath, buf->value );
411     string_free( buf );
412 
413 
414     status = lbr$ini_control( &m_lbr_context, &lfunc, &typ, NULL );
415     if ( !( status & 1 ) )
416         return -1;
417 
418     library.dsc$a_pointer = vmspath;
419     library.dsc$w_length = strlen( vmspath );
420 
421     status = lbr$open( &m_lbr_context, &library, NULL, NULL, NULL, NULL, NULL );
422     if ( !( status & 1 ) )
423         return -1;
424 
425     /*  Scan main index for modules.
426      *  For each module search symbol-index to collect module's symbols.
427      */
428     status = lbr$get_index( &m_lbr_context, &LBR_MODINDEX_NUM, set_archive_member, NULL );
429 
430     if ( !( status & 1 ) )
431         return -1;
432 
433 
434     (void) lbr$close( &m_lbr_context );
435 
436     return 0;
437 }
438 
439 #endif  /* OS_VMS */
440 
441