2 * Copyright 1993-2002 Christopher Seiwald and Perforce Software, Inc.
4 * This file is part of Jam - see jam.c for Copyright information.
8 * filent.c - scan directories and archives on NT
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
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().
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
37 # if __BORLANDC__ < 0x550
41 # undef PATHNAME /* cpp namespace collision */
42 # define _finddata_t ffblk
46 # include <sys/stat.h>
49 * file_dirscan() - scan a directory for files
53 # define FINDTYPE long long
55 # define FINDTYPE long
59 void file_dirscan (const char *dir
, scanback func
, void *closure
) {
61 char filespec
[MAXJPATH
];
62 char filename
[MAXJPATH
];
65 struct _finddata_t finfo
[1];
67 /* First enter directory itself */
68 memset((char *)&f
, '\0', sizeof(f
));
70 f
.f_dir
.len
= strlen(dir
);
71 dir
= *dir
? dir
: ".";
73 /* Special case \ or d:\ : enter it */
74 if (f
.f_dir
.len
== 1 && f
.f_dir
.ptr
[0] == '\\') (*func
)(closure
, dir
, 0 /* not stat()'ed */, (time_t)0);
75 else if (f
.f_dir
.len
== 3 && f
.f_dir
.ptr
[1] == ':') (*func
)(closure
, dir
, 0 /* not stat()'ed */, (time_t)0);
77 /* Now enter contents of directory */
78 sprintf(filespec
, "%s/*", dir
);
79 if (DEBUG_BINDSCAN
) printf("scan directory %s\n", dir
);
81 # if defined(__BORLANDC__) && __BORLANDC__ < 0x550
82 if (ret
= findfirst(filespec
, finfo
, FA_NORMAL
| FA_DIREC
)) return;
84 time_t time_write
= finfo
->ff_fdate
;
85 time_write
= (time_write
<< 16) | finfo
->ff_ftime
;
86 f
.f_base
.ptr
= finfo
->ff_name
;
87 f
.f_base
.len
= strlen(finfo
->ff_name
);
88 path_build(&f
, filename
);
89 (*func
)(closure
, filename
, 1 /* stat()'ed */, time_write
);
90 ret
= findnext(finfo
);
93 handle
= _findfirst(filespec
, finfo
);
94 if ((ret
= (handle
== (FINDTYPE
)(-1)))) return;
96 f
.f_base
.ptr
= finfo
->name
;
97 f
.f_base
.len
= strlen(finfo
->name
);
98 path_build(&f
, filename
, 0);
99 (*func
)(closure
, filename
, 1 /* stat()'ed */, finfo
->time_write
);
100 ret
= _findnext(handle
, finfo
);
108 * file_time() - get timestamp of file, if not done by file_dirscan()
110 int file_time (const char *filename
, time_t *time
) {
111 /* On NT this is called only for C:/ */
114 if (stat(filename
, &statbuf
) < 0) return -1;
115 *time
= statbuf
.st_mtime
;
121 * file_archscan() - scan an archive for files
123 /* Straight from SunOS */
124 #define ARMAG "!<arch>\n"
140 # define SARHDR sizeof( struct ar_hdr )
142 void file_archscan (const char *archive
, scanback func
, void *closure
) {
143 struct ar_hdr ar_hdr
;
144 char *string_table
= 0;
145 long string_table_len
= 0;
150 if ((fd
= open(archive
, O_RDONLY
| O_BINARY
, 0)) < 0) return;
151 if (read(fd
, buf
, SARMAG
) != SARMAG
|| strncmp(ARMAG
, buf
, SARMAG
)) {
156 if (DEBUG_BINDSCAN
) printf("scan archive %s\n", archive
);
158 while (read(fd
, &ar_hdr
, SARHDR
) == SARHDR
&& !memcmp(ar_hdr
.ar_fmag
, ARFMAG
, SARFMAG
)) {
165 sscanf(ar_hdr
.ar_date
, "%ld", &lar_date
);
166 sscanf(ar_hdr
.ar_size
, "%ld", &lar_size
);
167 lar_size
= (lar_size
+1) & ~1;
168 if (ar_hdr
.ar_name
[0] == '/' && ar_hdr
.ar_name
[1] == '/') {
169 /* this is the "string table" entry of the symbol table,
170 ** which holds strings of filenames that are longer than
171 ** 15 characters (ie. don't fit into a ar_name
173 string_table
= malloc(lar_size
);
174 if (read(fd
, string_table
, lar_size
) != lar_size
) printf("error reading string table\n");
175 string_table_len
= lar_size
;
177 } else if (ar_hdr
.ar_name
[0] == '/' && ar_hdr
.ar_name
[1] != ' ') {
178 /* Long filenames are recognized by "/nnnn" where nnnn is
179 ** the offset of the string in the string table represented
180 ** in ASCII decimals.
182 ** however, the name end with 0 or '/', depending on
183 ** the librarian used to generate them (0 for Mingw,
184 * '/' for Visual C++)
186 long off
= atoi(ar_hdr
.ar_name
+1);
187 if (off
< 0 || off
> string_table_len
) goto Next
;
188 name
= string_table
+off
;
189 for (; off
< string_table_len
; off
++) {
190 int c
= string_table
[off
];
191 if (c
== 0 || c
== '/') break;
193 endname
= string_table
+off
;
198 name
= ar_hdr
.ar_name
;
199 for (off
= 0; off
< sizeof(ar_hdr
.ar_name
); off
++) {
200 if (name
[off
] == '/' || name
[off
] == 0) break; /* not strictly required, but safe */
204 /* strip trailing space, slashes, and backslashes */
205 while (endname
> name
) {
207 if (c
!= ' ' && c
!= '\\' && c
!= '/') break;
210 /* strip leading directory names, since they're present in
211 * files generated by the Microsoft Librarian
215 for (; p
< endname
; p
++) {
216 if (*p
== '\\') name
= p
+1;
219 /* don't count empty entries */
220 if (name
>= endname
) goto Next
;
222 sprintf(buf
, "%s(%.*s)", archive
, endname
-name
, name
);
223 (*func
)(closure
, buf
, 1 /* time valid */, (time_t)lar_date
);
225 offset
+= SARHDR
+lar_size
;
226 lseek(fd
, offset
, 0);