| 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 | } |