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 * filent.c - scan directories and archives on NT
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 * 07/10/95 (taylor)  Findfirst() returns the first file on NT.
23 * 05/03/96 (seiwald) split apart into pathnt.c
24 * 01/20/00 (seiwald) - Upgraded from K&R to ANSI C
25 * 10/03/00 (anton) - Porting for Borland C++ 5.5
26 * 01/08/01 (seiwald) - closure param for file_dirscan/file_archscan
27 * 11/04/02 (seiwald) - const-ing for string literals
28 * 01/23/03 (seiwald) - long long handles for NT IA64
29 */
30
31# include "jam.h"
32# include "filesys.h"
33# include "pathsys.h"
34
35# ifdef OS_NT
36
37# ifdef __BORLANDC__
38# if __BORLANDC__ < 0x550
39# include <dir.h>
40# include <dos.h>
41# endif
42# undef PATHNAME	/* cpp namespace collision */
43# define _finddata_t ffblk
44# endif
45
46# include <io.h>
47# include <sys/stat.h>
48
49/*
50 * file_dirscan() - scan a directory for files
51 */
52
53# ifdef _M_IA64
54# define FINDTYPE long long
55# else
56# define FINDTYPE long
57# endif
58
59void
60file_dirscan(
61	const char *dir,
62	scanback func,
63	void	*closure )
64{
65	PATHNAME f;
66	char filespec[ MAXJPATH ];
67	char filename[ MAXJPATH ];
68	FINDTYPE handle;
69	int ret;
70	struct _finddata_t finfo[1];
71
72	/* First enter directory itself */
73
74	memset( (char *)&f, '\0', sizeof( f ) );
75
76	f.f_dir.ptr = dir;
77	f.f_dir.len = strlen(dir);
78
79	dir = *dir ? dir : ".";
80
81 	/* Special case \ or d:\ : enter it */
82
83 	if( f.f_dir.len == 1 && f.f_dir.ptr[0] == '\\' )
84 	    (*func)( closure, dir, 0 /* not stat()'ed */, (time_t)0 );
85 	else if( f.f_dir.len == 3 && f.f_dir.ptr[1] == ':' )
86 	    (*func)( closure, dir, 0 /* not stat()'ed */, (time_t)0 );
87
88	/* Now enter contents of directory */
89
90	sprintf( filespec, "%s/*", dir );
91
92	if( DEBUG_BINDSCAN )
93	    printf( "scan directory %s\n", dir );
94
95# if defined(__BORLANDC__) && __BORLANDC__ < 0x550
96	if ( ret = findfirst( filespec, finfo, FA_NORMAL | FA_DIREC ) )
97	    return;
98
99	while( !ret )
100	{
101	    time_t time_write = finfo->ff_fdate;
102
103	    time_write = (time_write << 16) | finfo->ff_ftime;
104	    f.f_base.ptr = finfo->ff_name;
105	    f.f_base.len = strlen( finfo->ff_name );
106
107	    path_build( &f, filename );
108
109	    (*func)( closure, filename, 1 /* stat()'ed */, time_write );
110
111	    ret = findnext( finfo );
112	}
113# else
114	handle = _findfirst( filespec, finfo );
115
116	if( ret = ( handle == (FINDTYPE)(-1) ) )
117	    return;
118
119	while( !ret )
120	{
121	    f.f_base.ptr = finfo->name;
122	    f.f_base.len = strlen( finfo->name );
123
124	    path_build( &f, filename, 0 );
125
126	    (*func)( closure, filename, 1 /* stat()'ed */, finfo->time_write );
127
128	    ret = _findnext( handle, finfo );
129	}
130
131	_findclose( handle );
132# endif
133
134}
135
136/*
137 * file_time() - get timestamp of file, if not done by file_dirscan()
138 */
139
140int
141file_time(
142	const char *filename,
143	time_t	*time )
144{
145	/* On NT this is called only for C:/ */
146
147	struct stat statbuf;
148
149	if( stat( filename, &statbuf ) < 0 )
150	    return -1;
151
152	*time = statbuf.st_mtime;
153
154	return 0;
155}
156
157/*
158 * file_archscan() - scan an archive for files
159 */
160
161/* Straight from SunOS */
162
163#define	ARMAG	"!<arch>\n"
164#define	SARMAG	8
165
166#define	ARFMAG	"`\n"
167
168struct ar_hdr {
169	char	ar_name[16];
170	char	ar_date[12];
171	char	ar_uid[6];
172	char	ar_gid[6];
173	char	ar_mode[8];
174	char	ar_size[10];
175	char	ar_fmag[2];
176};
177
178# define SARFMAG 2
179# define SARHDR sizeof( struct ar_hdr )
180
181void
182file_archscan(
183	const char *archive,
184	scanback func,
185	void	*closure )
186{
187	struct ar_hdr ar_hdr;
188	char *string_table = 0;
189	char buf[ MAXJPATH ];
190	long offset;
191	int fd;
192
193	if( ( fd = open( archive, O_RDONLY | O_BINARY, 0 ) ) < 0 )
194	    return;
195
196	if( read( fd, buf, SARMAG ) != SARMAG ||
197	    strncmp( ARMAG, buf, SARMAG ) )
198	{
199	    close( fd );
200	    return;
201	}
202
203	offset = SARMAG;
204
205	if( DEBUG_BINDSCAN )
206	    printf( "scan archive %s\n", archive );
207
208	while( read( fd, &ar_hdr, SARHDR ) == SARHDR &&
209	       !memcmp( ar_hdr.ar_fmag, ARFMAG, SARFMAG ) )
210	{
211	    long    lar_date;
212	    long    lar_size;
213	    char    *name = 0;
214 	    char    *endname;
215	    char    *c;
216
217	    sscanf( ar_hdr.ar_date, "%ld", &lar_date );
218	    sscanf( ar_hdr.ar_size, "%ld", &lar_size );
219
220	    lar_size = ( lar_size + 1 ) & ~1;
221
222	    if (ar_hdr.ar_name[0] == '/' && ar_hdr.ar_name[1] == '/' )
223	    {
224		/* this is the "string table" entry of the symbol table,
225		** which holds strings of filenames that are longer than
226		** 15 characters (ie. don't fit into a ar_name
227		*/
228
229		string_table = malloc(lar_size);
230		if (read(fd, string_table, lar_size) != lar_size)
231		    printf("error reading string table\n");
232		offset += SARHDR + lar_size;
233		continue;
234	    }
235	    else if (ar_hdr.ar_name[0] == '/' && ar_hdr.ar_name[1] != ' ')
236	    {
237		/* Long filenames are recognized by "/nnnn" where nnnn is
238		** the offset of the string in the string table represented
239		** in ASCII decimals.
240		*/
241
242		name = string_table + atoi( ar_hdr.ar_name + 1 );
243		endname = name + strlen( name );
244	    }
245	    else
246	    {
247		/* normal name */
248		name = ar_hdr.ar_name;
249		endname = name + sizeof( ar_hdr.ar_name );
250	    }
251
252	    /* strip trailing space, slashes, and backslashes */
253
254	    while( endname-- > name )
255		if( *endname != ' ' && *endname != '\\' && *endname != '/' )
256		    break;
257	    *++endname = 0;
258
259	    /* strip leading directory names, an NT specialty */
260
261	    if( c = strrchr( name, '/' ) )
262		name = c + 1;
263	    if( c = strrchr( name, '\\' ) )
264		name = c + 1;
265
266	    sprintf( buf, "%s(%.*s)", archive, endname - name, name );
267	    (*func)( closure, buf, 1 /* time valid */, (time_t)lar_date );
268
269	    offset += SARHDR + lar_size;
270	    lseek( fd, offset, 0 );
271	}
272
273	close( fd );
274}
275
276# endif /* NT */
277