2 * Copyright 1993-2002 Christopher Seiwald and Perforce Software, Inc.
3 * This file is part of Jam - see jam.c for Copyright information.
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 * filent.c - scan directories and archives on NT
23 * file_dirscan() - scan a directory for files
24 * file_time() - get timestamp of file, if not done by file_dirscan()
25 * file_archscan() - scan an archive for files
27 * File_dirscan() and file_archscan() call back a caller provided function
28 * for each file found. A flag to this callback function lets file_dirscan()
29 * and file_archscan() indicate that a timestamp is being provided with the
30 * file. If file_dirscan() or file_archscan() do not provide the file's
31 * timestamp, interested parties may later call file_time().
44 #define FINDTYPE intptr_t
48 * file_dirscan() - scan a directory for files
50 // <0: error; 0: regular; 1: directory
51 int file_type (const char *diskname
) {
53 struct _finddata_t finfo
[1];
55 handle
= _findfirst(diskname
, finfo
);
56 if (handle
== (FINDTYPE
)(-1)) return -1;
57 res
= (finfo
->attrib
&(_A_SUBDIR
) ? 1 : 0);
63 void file_dirscan (const char *dir
, scanback func
, void *closure
) {
65 static char filespec
[MAXJPATH
];
66 static char filename
[MAXJPATH
];
69 struct _finddata_t finfo
[1];
70 /* first enter directory itself */
71 memset((char *)&f
, '\0', sizeof(f
));
73 f
.f_dir
.len
= strlen(dir
);
74 dir
= (*dir
? dir
: ".");
75 /* special case \ or d:\ : enter it */
76 if (f
.f_dir
.len
== 1 && f
.f_dir
.ptr
[0] == '\\') (*func
)(closure
, dir
, 0 /* not stat()'ed */, (time_t)0);
77 else if (f
.f_dir
.len
== 3 && f
.f_dir
.ptr
[1] == ':') (*func
)(closure
, dir
, 0 /* not stat()'ed */, (time_t)0);
78 /* now enter contents of directory */
79 snprintf(filespec
, sizeof(filespec
), "%s/*", dir
);
80 if (DEBUG_BINDSCAN
) printf("scan directory %s\n", dir
);
81 handle
= _findfirst(filespec
, finfo
);
82 if ((ret
= (handle
== (FINDTYPE
)(-1)))) return;
84 f
.f_base
.ptr
= finfo
->name
;
85 f
.f_base
.len
= strlen(finfo
->name
);
86 path_build(filename
, &f
);
87 (*func
)(closure
, filename
, 1 /* stat()'ed */, finfo
->time_write
);
88 ret
= _findnext(handle
, finfo
);
95 * file_time() - get timestamp of file, if not done by file_dirscan()
97 int file_time (const char *filename
, time_t *time
) {
98 /* on NT this is called only for C:/ */
100 if (stat(filename
, &statbuf
) < 0) return -1;
101 *time
= statbuf
.st_mtime
;
106 static long scan_long (const char *str
, size_t ssize
) {
108 while (ssize
> 0 && str
[0] && (str
[0] < '0' || str
[0] > '9')) { ++str
; --ssize
; }
109 if (ssize
> 0 && str
[0]) {
110 while (ssize
> 0 && str
[0] >= '0' && str
[0] <= '9') {
111 res
= res
*10+str
[0]-'0';
121 * file_archscan() - scan an archive for files
123 /* straight from SunOS */
124 #define ARMAG "!<arch>\n"
139 #define SARHDR sizeof(struct ar_hdr)
141 void file_archscan (const char *archive
, scanback func
, void *closure
) {
142 struct ar_hdr ar_hdr
;
143 char *string_table
= 0;
144 long string_table_len
= 0;
145 static char buf
[MAXJPATH
];
148 if ((fd
= open(archive
, O_RDONLY
|O_BINARY
, 0)) < 0) return;
149 if (read(fd
, buf
, SARMAG
) != SARMAG
|| strncmp(ARMAG
, buf
, SARMAG
)) {
154 if (DEBUG_BINDSCAN
) printf("scan archive %s\n", archive
);
155 while (read(fd
, &ar_hdr
, SARHDR
) == SARHDR
&& !memcmp(ar_hdr
.ar_fmag
, ARFMAG
, SARFMAG
)) {
162 sscanf(ar_hdr.ar_date, "%ld", &lar_date);
163 sscanf(ar_hdr.ar_size, "%ld", &lar_size);
165 lar_date
= scan_long(ar_hdr
.ar_date
, sizeof(ar_hdr
.ar_date
));
166 lar_size
= scan_long(ar_hdr
.ar_size
, sizeof(ar_hdr
.ar_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 */
172 string_table
= malloc(lar_size
);
173 if (read(fd
, string_table
, lar_size
) != lar_size
) printf("error reading string table\n");
174 string_table_len
= lar_size
;
176 } else if (ar_hdr
.ar_name
[0] == '/' && ar_hdr
.ar_name
[1] != ' ') {
177 /* Long filenames are recognized by "/nnnn" where nnnn is
178 * the offset of the string in the string table represented
181 * however, the name end with 0 or '/', depending on
182 * the librarian used to generate them (0 for Mingw, '/' for Visual C++) */
183 long off
= atoi(ar_hdr
.ar_name
+1);
184 if (off
< 0 || off
> string_table_len
) goto next
;
185 name
= string_table
+off
;
186 for (; off
< string_table_len
; ++off
) {
187 int c
= string_table
[off
];
188 if (c
== 0 || c
== '/') break;
190 endname
= string_table
+off
;
195 name
= ar_hdr
.ar_name
;
196 for (off
= 0; off
< sizeof(ar_hdr
.ar_name
); ++off
) {
197 if (name
[off
] == '/' || name
[off
] == 0) break; /* not strictly required, but safe */
201 /* strip trailing space, slashes, and backslashes */
202 while (endname
> name
) {
204 if (c
!= ' ' && c
!= '\\' && c
!= '/') break;
207 /* strip leading directory names, since they're present in
208 * files generated by the Microsoft Librarian */
211 for (; p
< endname
; ++p
) if (*p
== '\\') name
= p
+1;
213 /* don't count empty entries */
214 if (name
>= endname
) goto next
;
216 snprintf(buf
, sizeof(buf
), "%s(%.*s)", archive
, endname
-name
, name
);
217 (*func
)(closure
, buf
, 1 /* time valid */, (time_t)lar_date
);
219 offset
+= SARHDR
+lar_size
;
220 lseek(fd
, offset
, 0);