Change the magic number used to introduce a trie file, so that instead
[sgt/agedu] / winscan.c
1 #include <windows.h>
2
3 #include <stdio.h>
4 #include <string.h>
5
6 #define lenof(x) (sizeof((x))/sizeof(*(x)))
7
8 #define snew(type) \
9 ( (type *) malloc (sizeof (type)) )
10 #define snewn(number, type) \
11 ( (type *) malloc ((number) * sizeof (type)) )
12 #define sresize(array, number, type) \
13 ( (void)sizeof((array)-(type *)0), \
14 (type *) realloc ((array), (number) * sizeof (type)) )
15 #define sfree free
16 char *dupstr(const char *s) {
17 char *r = malloc(1+strlen(s));
18 strcpy(r,s);
19 return r;
20 }
21
22 typedef struct {
23 HANDLE hdl;
24 WIN32_FIND_DATA fdata;
25 int got_one, eod;
26 } dirhandle;
27
28
29
30 int open_dir(char *path, dirhandle *dh)
31 {
32 strcat(path, "\\*");
33 dh->hdl = FindFirstFile(path, &dh->fdata);
34 if (dh->hdl == INVALID_HANDLE_VALUE) {
35 int err = GetLastError();
36 if (err == ERROR_FILE_NOT_FOUND) {
37 dh->eod = 1;
38 dh->got_one = 0;
39 return 0;
40 } else {
41 return -err;
42 }
43 } else {
44 dh->eod = 0;
45 dh->got_one = 1;
46 return 0;
47 }
48 }
49
50 const char *read_dir(dirhandle *dh)
51 {
52 if (!dh->got_one) {
53 if (dh->eod)
54 return NULL;
55
56 if (FindNextFile(dh->hdl, &dh->fdata)) {
57 dh->got_one = 1;
58 } else {
59 dh->eod = 1;
60 return NULL;
61 }
62 }
63
64 dh->got_one = 0;
65 return dh->fdata.cFileName;
66 }
67
68 void close_dir(dirhandle *dh)
69 {
70 CloseHandle(dh->hdl);
71 }
72
73 static int str_cmp(const void *av, const void *bv)
74 {
75 return strcmp(*(const char **)av, *(const char **)bv);
76 }
77
78 typedef int (*gotdata_fn_t)(void *ctx, const char *pathname,
79 WIN32_FIND_DATA *dat);
80
81 static void du_recurse(char **path, size_t pathlen, size_t *pathsize,
82 gotdata_fn_t gotdata, void *gotdata_ctx)
83 {
84 const char *name;
85 dirhandle d;
86 char **names;
87 int error;
88 size_t i, nnames, namesize;
89 WIN32_FIND_DATA dat;
90 HANDLE h;
91
92 if (*pathsize <= pathlen + 10) {
93 *pathsize = pathlen * 3 / 2 + 256;
94 *path = sresize(*path, *pathsize, char);
95 }
96
97 h = FindFirstFile(*path, &dat);
98 if (h != INVALID_HANDLE_VALUE) {
99 CloseHandle(h);
100 } else if (pathlen > 0 && (*path)[pathlen-1] == '\\') {
101 dat.nFileSizeHigh = dat.nFileSizeLow = 0;
102 dat.ftLastWriteTime.dwHighDateTime = 0x19DB1DE;
103 dat.ftLastWriteTime.dwLowDateTime = 0xD53E8000;
104 dat.ftLastAccessTime.dwHighDateTime = 0x19DB1DE;
105 dat.ftLastAccessTime.dwLowDateTime = 0xD53E8000;
106 h = CreateFile(*path, GENERIC_READ, FILE_SHARE_WRITE, NULL,
107 OPEN_EXISTING, 0, NULL);
108 if (h != INVALID_HANDLE_VALUE) {
109 GetFileTime(h, &dat.ftCreationTime, &dat.ftLastAccessTime,
110 &dat.ftLastWriteTime);
111 CloseHandle(h);
112 }
113 }
114
115 if (!gotdata(gotdata_ctx, *path, &dat))
116 return;
117
118 if (!(GetFileAttributes(*path) & FILE_ATTRIBUTE_DIRECTORY))
119 return;
120
121 names = NULL;
122 nnames = namesize = 0;
123
124 if ((error = open_dir(*path, &d)) < 0) {
125 char buf[4096];
126 FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, -error, 0,
127 buf, lenof(buf), NULL);
128 buf[lenof(buf)-1] = '\0';
129 if (buf[strlen(buf)-1] == '\n')
130 buf[strlen(buf)-1] = '\0';
131 fprintf(stderr, "Unable to open directory '%s': %s\n", *path, buf);
132 return;
133 }
134 while ((name = read_dir(&d)) != NULL) {
135 if (name[0] == '.' && (!name[1] || (name[1] == '.' && !name[2]))) {
136 /* do nothing - we skip "." and ".." */
137 } else {
138 if (nnames >= namesize) {
139 namesize = nnames * 3 / 2 + 64;
140 names = sresize(names, namesize, char *);
141 }
142 names[nnames++] = dupstr(name);
143 }
144 }
145 close_dir(&d);
146
147 if (nnames == 0)
148 return;
149
150 qsort(names, nnames, sizeof(*names), str_cmp);
151
152 for (i = 0; i < nnames; i++) {
153 size_t newpathlen = pathlen + 1 + strlen(names[i]);
154 if (*pathsize <= newpathlen) {
155 *pathsize = newpathlen * 3 / 2 + 256;
156 *path = sresize(*path, *pathsize, char);
157 }
158 /*
159 * Avoid duplicating a slash if we got a trailing one to
160 * begin with (i.e. if we're starting the scan in '\\' itself).
161 */
162 if (pathlen > 0 && (*path)[pathlen-1] == '\\') {
163 strcpy(*path + pathlen, names[i]);
164 newpathlen--;
165 } else {
166 sprintf(*path + pathlen, "\\%s", names[i]);
167 }
168
169 du_recurse(path, newpathlen, pathsize, gotdata, gotdata_ctx);
170
171 sfree(names[i]);
172 }
173 sfree(names);
174 }
175
176 void du(const char *inpath, gotdata_fn_t gotdata, void *gotdata_ctx)
177 {
178 char *path;
179 size_t pathlen, pathsize;
180
181 pathlen = strlen(inpath);
182 pathsize = pathlen + 256;
183 path = snewn(pathsize, char);
184 strcpy(path, inpath);
185
186 du_recurse(&path, pathlen, &pathsize, gotdata, gotdata_ctx);
187 }
188
189 int gd(void *ctx, const char *pathname, WIN32_FIND_DATA *dat)
190 {
191 unsigned long long size, t;
192 FILETIME ft;
193 const char *p;
194
195 size = dat->nFileSizeHigh;
196 size = (size << 32) | dat->nFileSizeLow;
197 printf("%llu ", size);
198
199 if (dat->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
200 ft = dat->ftLastWriteTime;
201 else
202 ft = dat->ftLastAccessTime;
203 t = ft.dwHighDateTime;
204 t = (t << 32) | ft.dwLowDateTime;
205 t /= 10000000;
206 /*
207 * Correction factor: number of seconds between Windows's file
208 * time epoch of 1 Jan 1601 and Unix's time_t epoch of 1 Jan 1970.
209 *
210 * That's 369 years, of which 92 were divisible by 4, but
211 * three of those were century points.
212 */
213 t -= (369 * 365 + 92 - 3) * 86400;
214 printf("%llu ", t);
215
216 for (p = pathname; *p; p++) {
217 if (*p >= ' ' && *p < 127 && *p != '%')
218 putchar(*p);
219 else
220 printf("%%%02x", (unsigned char)*p);
221 }
222 putchar('\n');
223 return 1;
224 }
225
226 int main(int argc, char **argv)
227 {
228 char *dir;
229 int dirlen, dirsize;
230
231 if (argc > 1)
232 SetCurrentDirectory(argv[1]);
233
234 dirsize = 512;
235 dir = snewn(dirsize, char);
236 while ((dirlen = GetCurrentDirectory(dirsize, dir)) >= dirsize) {
237 dirsize = dirlen + 256;
238 dir = sresize(dir, dirsize, char);
239 }
240
241 printf("agedu dump file. pathsep=5c\n");
242
243 du(dir, gd, NULL);
244 }