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 * fileunix.c - manipulate file names and scan directories on UNIX/AmigaOS
9 *
10 * External routines:
11 *
12 *	file_dirscan() - scan a directory for files
13 *	file_time() - get timestamp of file, if not done by file_dirscan()
14 *	file_archscan() - scan an archive for files
15 *
16 * File_dirscan() and file_archscan() call back a caller provided function
17 * for each file found.  A flag to this callback function lets file_dirscan()
18 * and file_archscan() indicate that a timestamp is being provided with the
19 * file.   If file_dirscan() or file_archscan() do not provide the file's
20 * timestamp, interested parties may later call file_time().
21 *
22 * 04/08/94 (seiwald) - Coherent/386 support added.
23 * 12/19/94 (mikem) - solaris string table insanity support
24 * 02/14/95 (seiwald) - parse and build /xxx properly
25 * 05/03/96 (seiwald) - split into pathunix.c
26 * 11/21/96 (peterk) - BEOS does not have Unix-style archives
27 * 01/08/01 (seiwald) - closure param for file_dirscan/file_archscan
28 * 04/03/01 (seiwald) - AIX uses SARMAG
29 * 07/16/02 (seiwald) - Support BSD style long filename in archives.
30 * 11/04/02 (seiwald) - const-ing for string literals
31 * 12/27/02 (seiwald) - support for AIX big archives
32 * 12/30/02 (seiwald) - terminate ar_hdr for solaris sscanf()
33 * 12/30/02 (seiwald) - skip solaris' empty archive member names (/, //xxx)
34 */
35
36# include "jam.h"
37# include "filesys.h"
38# include "pathsys.h"
39
40# ifdef USE_FILEUNIX
41
42# if defined( OS_SEQUENT ) || \
43     defined( OS_DGUX ) || \
44     defined( OS_SCO ) || \
45     defined( OS_ISC )
46# define PORTAR 1
47# endif
48
49# if defined( OS_RHAPSODY ) || \
50     defined( OS_MACOSX ) || \
51     defined( OS_NEXT )
52/* need unistd for rhapsody's proper lseek */
53# include <sys/dir.h>
54# include <unistd.h>
55# define STRUCT_DIRENT struct direct
56# else
57# include <dirent.h>
58# define STRUCT_DIRENT struct dirent
59# endif
60
61# ifdef __CYGWIN__
62# include <unistd.h>
63# endif
64
65# ifdef OS_COHERENT
66# include <arcoff.h>
67# define HAVE_AR
68# endif
69
70# if defined( OS_MVS ) || \
71     defined( OS_INTERIX )
72
73#define	ARMAG	"!<arch>\n"
74#define	SARMAG	8
75#define	ARFMAG	"`\n"
76
77struct ar_hdr		/* archive file member header - printable ascii */
78{
79	char	ar_name[16];	/* file member name - `/' terminated */
80	char	ar_date[12];	/* file member date - decimal */
81	char	ar_uid[6];	/* file member user id - decimal */
82	char	ar_gid[6];	/* file member group id - decimal */
83	char	ar_mode[8];	/* file member mode - octal */
84	char	ar_size[10];	/* file member size - decimal */
85	char	ar_fmag[2];	/* ARFMAG - string to end header */
86};
87
88# define HAVE_AR
89# endif
90
91# if defined( OS_QNX ) || \
92     defined( OS_BEOS ) || \
93     defined( OS_HAIKU ) || \
94     defined( OS_MPEIX )
95# define NO_AR
96# define HAVE_AR
97# endif
98
99# ifndef HAVE_AR
100# ifdef _AIX43
101/* AIX 43 ar SUPPORTs only __AR_BIG__ */
102# define __AR_BIG__
103# endif
104# include <ar.h>
105# endif
106
107# ifdef OPT_STAT_CACHE_SERVER_EXT
108# include "beos_stat_cache.h"
109# define opendir	beos_stat_cache_opendir
110# define readdir	beos_stat_cache_readdir
111# define closedir	beos_stat_cache_closedir
112# endif
113
114/*
115 * file_dirscan() - scan a directory for files
116 */
117
118void
119file_dirscan(
120	const char *dir,
121	scanback func,
122	void *closure )
123{
124	PATHNAME f;
125	DIR *d;
126	STRUCT_DIRENT *dirent;
127	char filename[ MAXJPATH ];
128
129	/* First enter directory itself */
130
131	memset( (char *)&f, '\0', sizeof( f ) );
132
133	f.f_dir.ptr = dir;
134	f.f_dir.len = strlen(dir);
135
136	dir = *dir ? dir : ".";
137
138	/* Special case / : enter it */
139
140	if( f.f_dir.len == 1 && f.f_dir.ptr[0] == '/' )
141	    (*func)( closure, dir, 0 /* not stat()'ed */, (time_t)0 );
142
143	/* Now enter contents of directory */
144
145	if( !( d = opendir( dir ) ) )
146	    return;
147
148	if( DEBUG_BINDSCAN )
149	    printf( "scan directory %s\n", dir );
150
151	while( dirent = readdir( d ) )
152	{
153# ifdef old_sinix
154	    /* Broken structure definition on sinix. */
155	    f.f_base.ptr = dirent->d_name - 2;
156# else
157	    f.f_base.ptr = dirent->d_name;
158# endif
159	    f.f_base.len = strlen( f.f_base.ptr );
160
161	    path_build( &f, filename, 0 );
162
163	    (*func)( closure, filename, 0 /* not stat()'ed */, (time_t)0 );
164	}
165
166	closedir( d );
167}
168
169/*
170 * file_time() - get timestamp of file, if not done by file_dirscan()
171 */
172
173int
174file_time(
175	const char *filename,
176	time_t	*time )
177{
178	struct stat statbuf;
179
180# ifdef OPT_STAT_CACHE_SERVER_EXT
181	if( beos_stat_cache_stat( filename, &statbuf ) < 0 )
182	    return -1;
183# else
184	if( stat( filename, &statbuf ) < 0 )
185	    return -1;
186# endif
187
188	*time = statbuf.st_mtime;
189	return 0;
190}
191
192/*
193 * file_archscan() - scan an archive for files
194 */
195
196# ifndef AIAMAG	/* God-fearing UNIX */
197
198# define SARFMAG 2
199# define SARHDR sizeof( struct ar_hdr )
200
201void
202file_archscan(
203	const char *archive,
204	scanback func,
205	void *closure )
206{
207# ifndef NO_AR
208	struct ar_hdr ar_hdr;
209	char buf[ MAXJPATH ];
210	long offset;
211	char    *string_table = 0;
212	int fd;
213
214	if( ( fd = open( archive, O_RDONLY, 0 ) ) < 0 )
215	    return;
216
217	if( read( fd, buf, SARMAG ) != SARMAG ||
218	    strncmp( ARMAG, buf, SARMAG ) )
219	{
220	    close( fd );
221	    return;
222	}
223
224	offset = SARMAG;
225
226	if( DEBUG_BINDSCAN )
227	    printf( "scan archive %s\n", archive );
228
229	while( read( fd, &ar_hdr, SARHDR ) == SARHDR &&
230	       !memcmp( ar_hdr.ar_fmag, ARFMAG, SARFMAG ) )
231	{
232	    long    lar_date;
233	    long    lar_size;
234	    char    lar_name[256];
235	    char    *dst = lar_name;
236
237	    /* solaris sscanf() does strlen first, so terminate somewhere */
238
239	    ar_hdr.ar_fmag[0] = 0;
240
241	    /* Get date & size */
242
243	    sscanf( ar_hdr.ar_date, "%ld", &lar_date );
244	    sscanf( ar_hdr.ar_size, "%ld", &lar_size );
245
246	    /* Handle solaris string table.
247	    ** The entry under the name // is the table,
248	    ** and entries with the name /nnnn refer to the table.
249	    */
250
251	    if( ar_hdr.ar_name[0] != '/' )
252	    {
253		/* traditional archive entry names:
254		** ends at the first space, /, or null.
255		*/
256
257		char *src = ar_hdr.ar_name;
258		const char *e = src + sizeof( ar_hdr.ar_name );
259
260		while( src < e && *src && *src != ' ' && *src != '/' )
261		    *dst++ = *src++;
262	    }
263	    else if( ar_hdr.ar_name[1] == '/' )
264	    {
265		/* this is the "string table" entry of the symbol table,
266		** which holds strings of filenames that are longer than
267		** 15 characters (ie. don't fit into a ar_name)
268		*/
269
270		string_table = (char *)malloc(lar_size);
271
272		lseek(fd, offset + SARHDR, 0);
273		if( read(fd, string_table, lar_size) != lar_size )
274		    printf( "error reading string table\n" );
275	    }
276	    else if( string_table && ar_hdr.ar_name[1] != ' ' )
277	    {
278		/* Long filenames are recognized by "/nnnn" where nnnn is
279		** the offset of the string in the string table represented
280		** in ASCII decimals.
281		*/
282
283		char *src = string_table + atoi( ar_hdr.ar_name + 1 );
284
285		while( *src != '/' )
286		    *dst++ = *src++;
287	    }
288
289	    /* Terminate lar_name */
290
291	    *dst = 0;
292
293	    /* Modern (BSD4.4) long names: if the name is "#1/nnnn",
294	    ** then the actual name is the nnnn bytes after the header.
295	    */
296
297	    if( !strcmp( lar_name, "#1" ) )
298	    {
299		int len = atoi( ar_hdr.ar_name + 3 );
300		if( read( fd, lar_name, len ) != len )
301		    printf("error reading archive entry\n");
302		lar_name[len] = 0;
303	    }
304
305	    /* Build name and pass it on.  */
306
307	    if( lar_name[0] )
308	    {
309		if( DEBUG_BINDSCAN )
310		    printf( "archive name %s found\n", lar_name );
311
312		sprintf( buf, "%s(%s)", archive, lar_name );
313
314		(*func)( closure, buf, 1 /* time valid */, (time_t)lar_date );
315	    }
316
317	    /* Position at next member */
318
319	    offset += SARHDR + ( ( lar_size + 1 ) & ~1 );
320	    lseek( fd, offset, 0 );
321	}
322
323	if (string_table)
324	    free(string_table);
325
326	close( fd );
327
328# endif /* NO_AR */
329
330}
331
332# else /* AIAMAG - RS6000 AIX */
333
334void
335file_archscan(
336	const char *archive,
337	scanback func,
338	void *closure )
339{
340	struct fl_hdr fl_hdr;
341
342	struct {
343		struct ar_hdr hdr;
344		char pad[ 256 ];
345	} ar_hdr ;
346
347	char buf[ MAXJPATH ];
348	long offset;
349	int fd;
350
351	if( ( fd = open( archive, O_RDONLY, 0 ) ) < 0 )
352	    return;
353
354# ifdef __AR_BIG__
355
356	if( read( fd, (char *)&fl_hdr, FL_HSZ ) != FL_HSZ ||
357	    strncmp( AIAMAGBIG, fl_hdr.fl_magic, SAIAMAG ) )
358	{
359	    if( strncmp( AIAMAG, fl_hdr.fl_magic, SAIAMAG ) )
360		printf( "Can't read new archive %s before AIX 4.3.\n" );
361
362	    close( fd );
363	    return;
364	}
365
366# else
367
368	if( read( fd, (char *)&fl_hdr, FL_HSZ ) != FL_HSZ ||
369	    strncmp( AIAMAG, fl_hdr.fl_magic, SAIAMAG ) )
370	{
371	    close( fd );
372	    return;
373	}
374
375# endif
376
377	sscanf( fl_hdr.fl_fstmoff, "%ld", &offset );
378
379	if( DEBUG_BINDSCAN )
380	    printf( "scan archive %s\n", archive );
381
382	while( offset > 0 &&
383	       lseek( fd, offset, 0 ) >= 0 &&
384	       read( fd, &ar_hdr, sizeof( ar_hdr ) ) >= sizeof( ar_hdr.hdr ) )
385	{
386	    long    lar_date;
387	    int	    lar_namlen;
388
389	    sscanf( ar_hdr.hdr.ar_namlen, "%d", &lar_namlen );
390	    sscanf( ar_hdr.hdr.ar_date, "%ld", &lar_date );
391	    sscanf( ar_hdr.hdr.ar_nxtmem, "%ld", &offset );
392
393	    if( !lar_namlen )
394		continue;
395
396	    ar_hdr.hdr._ar_name.ar_name[ lar_namlen ] = '\0';
397
398	    sprintf( buf, "%s(%s)", archive, ar_hdr.hdr._ar_name.ar_name );
399
400	    (*func)( closure, buf, 1 /* time valid */, (time_t)lar_date );
401	}
402
403	close( fd );
404}
405
406# endif /* AIAMAG - RS6000 AIX */
407
408# endif /* USE_FILEUNIX */
409
410