HuntnGather – Blame information for rev 23
?pathlinks?
Rev | Author | Line No. | Line |
---|---|---|---|
1 | office | 1 | /////////////////////////////////////////////////////////////////////////// |
2 | // Copyright (C) 2021 Wizardry and Steamworks - License: MIT // |
||
3 | /////////////////////////////////////////////////////////////////////////// |
||
4 | |||
5 | #include <stdio.h> |
||
6 | #include <stdlib.h> |
||
7 | #include <string.h> |
||
8 | #include <dirent.h> |
||
9 | #include <signal.h> |
||
10 | |||
11 | #include <sys/types.h> |
||
12 | #include <sys/stat.h> |
||
19 | office | 13 | #include <sys/syslimits.h> |
1 | office | 14 | |
15 | #include <proto/dos.h> |
||
16 | #include <proto/exec.h> |
||
17 | |||
22 | office | 18 | #if defined ___AsyncIO___ |
19 | #include <asyncio.h> |
||
20 | #endif |
||
21 | |||
1 | office | 22 | #include "StringStack.h" |
23 | |||
5 | office | 24 | #if !defined ___HAVE_GETOPT___ |
1 | office | 25 | #include "getopt.h" |
26 | #endif |
||
27 | |||
22 | office | 28 | #define PROGRAM_VERSION "1.7.3" |
19 | office | 29 | |
5 | office | 30 | #if defined ___AmigaOS___ |
31 | /*************************************************************************/ |
||
32 | /* Version string used for querrying the program version. */ |
||
33 | /*************************************************************************/ |
||
34 | TEXT version_string[] = |
||
19 | office | 35 | "\0$VER: Gather " PROGRAM_VERSION " "__DATE__" by Wizardry and Steamworks"; |
5 | office | 36 | #endif |
1 | office | 37 | |
38 | #if !defined TRUE |
||
39 | #define TRUE 1; |
||
40 | #endif |
||
41 | |||
42 | #if !defined FALSE |
||
43 | #define FALSE 0; |
||
44 | #endif |
||
45 | |||
22 | office | 46 | #define ASYNC_BUF 8192 |
2 | office | 47 | #define MAX_MEM 262144 |
11 | office | 48 | #define NAME_BUF 32 |
49 | #define PATH_BUF 128 |
||
50 | #define LINE_BUF 256 |
||
2 | office | 51 | #define DEFAULT_DATABASE_FILE "S:gather.db" |
1 | office | 52 | |
53 | typedef struct { |
||
54 | unsigned int dirs; |
||
55 | unsigned int files; |
||
56 | } stats; |
||
57 | |||
58 | int run = TRUE; |
||
59 | int verbose = TRUE; |
||
60 | |||
61 | void SignalHandler(int sig) { |
||
62 | // Toggle the run flag to stop execution. |
||
63 | run = FALSE; |
||
64 | } |
||
65 | |||
66 | int compare(const void *a, const void *b) { |
||
67 | const char **p = (const char **)a; |
||
68 | const char **q = (const char **)b; |
||
13 | office | 69 | return strncmp(*p, *q, strlen(*p)); |
1 | office | 70 | } |
71 | |||
72 | /* |
||
73 | * |
||
74 | * Sorts a database file lexicographically. |
||
75 | */ |
||
76 | void SortDatabase(char *dbFile) { |
||
22 | office | 77 | #if defined ___AsyncIO___ |
78 | struct AsyncFile *fp; |
||
79 | LONG c; |
||
80 | #else |
||
1 | office | 81 | FILE *fp; |
22 | office | 82 | char c; |
83 | #endif |
||
1 | office | 84 | char *name = NULL; |
85 | char *path = NULL; |
||
86 | char **database; |
||
87 | int i; |
||
88 | int side; |
||
89 | unsigned int line; |
||
11 | office | 90 | int name_size; |
91 | int path_size; |
||
1 | office | 92 | |
93 | // Open database file for reading. |
||
22 | office | 94 | #if defined ___AsyncIO___ |
95 | if((fp = OpenAsync(dbFile, MODE_READ, ASYNC_BUF)) == NULL) { |
||
96 | #else |
||
1 | office | 97 | if((fp = fopen(dbFile, "r")) == NULL) { |
22 | office | 98 | #endif |
1 | office | 99 | fprintf(stderr, "Unable to open gather database for reading.\n"); |
100 | return; |
||
101 | } |
||
102 | |||
19 | office | 103 | database = malloc(sizeof(*database)); |
11 | office | 104 | name_size = NAME_BUF; |
19 | office | 105 | name = malloc(name_size * sizeof(*name)); |
11 | office | 106 | path_size = PATH_BUF; |
19 | office | 107 | path = malloc(path_size * sizeof(*path)); |
22 | office | 108 | |
1 | office | 109 | line = 0; |
110 | side = 0; |
||
111 | i = 0; |
||
112 | |||
113 | if(verbose) { |
||
23 | office | 114 | fprintf(stdout, "Sorting temporary database file: '%s'\n", dbFile); |
1 | office | 115 | } |
22 | office | 116 | #if defined ___AsyncIO___ |
117 | while(run && (c = ReadCharAsync(fp)) != -1) { |
||
118 | #else |
||
1 | office | 119 | while(run && fscanf(fp, "%c", &c) == 1) { |
22 | office | 120 | #endif |
1 | office | 121 | #if defined ___AmigaOS___ |
122 | // Check if CTRL+C was pressed and abort the program. |
||
123 | if(SetSignal(0L, SIGBREAKF_CTRL_C) & SIGBREAKF_CTRL_C) { |
||
124 | run = FALSE; |
||
125 | continue; |
||
126 | } |
||
127 | #endif |
||
128 | switch(c) { |
||
129 | case '\n': |
||
130 | // Load up the name and path into the database variable. |
||
19 | office | 131 | database = realloc(database, (line + 1) * sizeof(*database)); |
132 | database[line] = malloc((strlen(name) + strlen(path) + 1 + 1) * sizeof(*database[line])); |
||
1 | office | 133 | sprintf(database[line], "%s\t%s", name, path); |
134 | ++line; |
||
135 | |||
136 | free(name); |
||
11 | office | 137 | name_size = NAME_BUF; |
19 | office | 138 | name = malloc(name_size * sizeof(*name)); |
1 | office | 139 | --side; |
140 | i = 0; |
||
141 | |||
142 | break; |
||
143 | case '\t': |
||
144 | free(path); |
||
11 | office | 145 | path_size = PATH_BUF; |
19 | office | 146 | path = malloc(path_size * sizeof(*path)); |
1 | office | 147 | ++side; |
148 | i = 0; |
||
149 | break; |
||
150 | default: |
||
151 | switch(side) { |
||
152 | case 0: |
||
11 | office | 153 | if(strlen(name) == name_size) { |
154 | name_size = name_size * 1.5; |
||
19 | office | 155 | name = realloc(name, name_size * sizeof(*name)); |
11 | office | 156 | } |
157 | //name = realloc(name, (i + 1 + 1) * sizeof(char)); |
||
1 | office | 158 | name[i] = c; |
159 | name[i + 1] = '\0'; |
||
160 | break; |
||
161 | case 1: |
||
11 | office | 162 | if(strlen(path) == path_size) { |
163 | path_size = path_size * 1.5; |
||
19 | office | 164 | path = realloc(path, path_size * sizeof(*path)); |
11 | office | 165 | } |
166 | //path = realloc(path, (i + 1 + 1) * sizeof(char)); |
||
1 | office | 167 | path[i] = c; |
168 | path[i + 1] = '\0'; |
||
169 | break; |
||
170 | default: |
||
22 | office | 171 | fprintf(stderr, "Database corrupted: %d\n", side); |
1 | office | 172 | break; |
173 | } |
||
174 | ++i; |
||
175 | break; |
||
176 | } |
||
177 | } |
||
178 | |||
22 | office | 179 | #if defined ___AsyncIO___ |
180 | CloseAsync(fp); |
||
181 | #else |
||
1 | office | 182 | fclose(fp); |
22 | office | 183 | #endif |
1 | office | 184 | |
185 | // Sort the database. |
||
186 | qsort(database, line, sizeof(char *), compare); |
||
187 | |||
188 | // Write the database lines back to the database. |
||
22 | office | 189 | #if defined ___AsyncIO___ |
23 | office | 190 | if((fp = OpenAsync(dbFile, MODE_WRITE, ASYNC_BUF)) == NULL) { |
22 | office | 191 | #else |
23 | office | 192 | if((fp = fopen(dbFile, "w")) == NULL) { |
22 | office | 193 | #endif |
1 | office | 194 | fprintf(stderr, "Unable to open gather database for writing.\n"); |
195 | return; |
||
196 | } |
||
197 | |||
198 | for(i = 0; i < line; ++i) { |
||
23 | office | 199 | #if defined ___AmigaOS___ |
200 | // Check if CTRL+C was pressed and abort the program. |
||
201 | if(SetSignal(0L, SIGBREAKF_CTRL_C) & SIGBREAKF_CTRL_C) { |
||
202 | run = FALSE; |
||
203 | continue; |
||
204 | } |
||
205 | #endif |
||
22 | office | 206 | #if defined ___AsyncIO___ |
207 | WriteAsync(fp, database[i], (LONG)(strlen(database[i]) * sizeof(char))); |
||
208 | WriteAsync(fp, "\n", 1 * sizeof(char)); |
||
209 | #else |
||
1 | office | 210 | fprintf(fp, "%s\n", database[i]); |
22 | office | 211 | #endif |
1 | office | 212 | } |
213 | |||
22 | office | 214 | #if defined ___AsyncIO___ |
215 | CloseAsync(fp); |
||
216 | #else |
||
217 | fclose(fp); |
||
218 | #endif |
||
219 | |||
1 | office | 220 | free(database); |
221 | } |
||
222 | |||
223 | /* |
||
224 | * |
||
225 | * Updates a database file "dbFile". |
||
226 | */ |
||
227 | void UpdateDatabase(char *dbFile, stringStack *dirStack, stats *stats) { |
||
22 | office | 228 | #if defined ___AsyncIO___ |
229 | struct AsyncFile *fp; |
||
230 | #else |
||
1 | office | 231 | FILE *fp; |
22 | office | 232 | #endif |
1 | office | 233 | DIR *dir; |
234 | struct dirent *dirEntry; |
||
235 | struct stat dirStat; |
||
236 | unsigned int size; |
||
237 | char *path; |
||
238 | char *subPath; |
||
239 | |||
22 | office | 240 | #if defined ___AsyncIO___ |
23 | office | 241 | if((fp = OpenAsync(dbFile, MODE_WRITE, ASYNC_BUF)) == NULL) { |
22 | office | 242 | #else |
23 | office | 243 | if((fp = fopen(dbFile, "w")) == NULL) { |
22 | office | 244 | #endif |
1 | office | 245 | fprintf(stderr, "Unable to open gather database for writing.\n"); |
246 | return; |
||
247 | } |
||
248 | |||
249 | while(run && !stringStackIsEmpty(dirStack)) { |
||
250 | #if defined ___AmigaOS___ |
||
251 | // Check if CTRL+C was pressed and abort the program. |
||
252 | if(SetSignal(0L, SIGBREAKF_CTRL_C) & SIGBREAKF_CTRL_C) { |
||
253 | run = FALSE; |
||
23 | office | 254 | continue; |
1 | office | 255 | } |
256 | #endif |
||
19 | office | 257 | if((path = stringStackPop(dirStack)) == NULL) { |
1 | office | 258 | return; |
259 | } |
||
260 | |||
261 | if((dir = opendir(path)) == NULL) { |
||
262 | return; |
||
263 | } |
||
264 | |||
265 | while(run && (dirEntry = readdir(dir)) != NULL) { |
||
266 | #if defined ___AmigaOS___ |
||
267 | // Check if CTRL+C was pressed and abort the program. |
||
268 | if(SetSignal(0L, SIGBREAKF_CTRL_C) & SIGBREAKF_CTRL_C) { |
||
269 | run = FALSE; |
||
23 | office | 270 | continue; |
1 | office | 271 | } |
272 | #endif |
||
273 | size = sizeof(path) + sizeof(dirEntry->d_name) + 1; |
||
274 | switch(path[strlen(path) - 1]) { |
||
275 | case '/': |
||
276 | case ':': // This is a drive path. |
||
11 | office | 277 | subPath = malloc(size); |
1 | office | 278 | sprintf(subPath, "%s%s", path, dirEntry->d_name); |
279 | break; |
||
280 | default: |
||
11 | office | 281 | subPath = malloc(size + 1); |
1 | office | 282 | sprintf(subPath, "%s/%s", path, dirEntry->d_name); |
283 | break; |
||
284 | } |
||
285 | stat(subPath, &dirStat); |
||
286 | if(S_ISDIR(dirStat.st_mode)) { |
||
287 | stringStackPush(dirStack, subPath); |
||
288 | |||
289 | ++stats->dirs; |
||
290 | |||
291 | if(verbose) { |
||
292 | fprintf(stdout, |
||
293 | "Gathered %d directories and %d files.\r", |
||
294 | stats->dirs, |
||
295 | stats->files); |
||
296 | } |
||
297 | |||
298 | free(subPath); |
||
299 | continue; |
||
300 | } |
||
301 | |||
302 | // Write to database file. |
||
15 | office | 303 | |
304 | #if defined ___NOCASE_FS___ |
||
305 | strupr(dirEntry->d_name); |
||
306 | #endif |
||
307 | |||
22 | office | 308 | #if defined ___AsyncIO___ |
309 | WriteAsync(fp, dirEntry->d_name, (LONG)(strlen(dirEntry->d_name) * sizeof(char))); |
||
310 | WriteAsync(fp, "\t", 1 * sizeof(char)); |
||
311 | WriteAsync(fp, subPath, (LONG)(strlen(subPath) * sizeof(char))); |
||
312 | WriteAsync(fp, "\n", 1 * sizeof(char)); |
||
313 | #else |
||
1 | office | 314 | fprintf(fp, "%s\t%s\n", dirEntry->d_name, subPath); |
22 | office | 315 | #endif |
1 | office | 316 | ++stats->files; |
317 | |||
318 | if(verbose) { |
||
319 | fprintf(stdout, |
||
320 | "Gathered %d directories and %d files.\r", |
||
321 | stats->dirs, |
||
322 | stats->files); |
||
323 | } |
||
324 | |||
325 | free(subPath); |
||
326 | } |
||
327 | |||
328 | closedir(dir); |
||
329 | free(path); |
||
330 | } |
||
331 | |||
332 | if(verbose) { |
||
333 | fprintf(stdout, "\n"); |
||
334 | } |
||
335 | |||
22 | office | 336 | #if defined ___AsyncIO___ |
337 | CloseAsync(fp); |
||
338 | #else |
||
1 | office | 339 | fclose(fp); |
22 | office | 340 | #endif |
1 | office | 341 | |
342 | } |
||
343 | |||
344 | /* |
||
345 | * |
||
23 | office | 346 | * Gets the size of a file "dbFle". |
1 | office | 347 | */ |
23 | office | 348 | int GetFileSize(char *dbFile) { |
349 | #if defined ___AsyncIO___ |
||
350 | struct AsyncFile *fp; |
||
351 | LONG size; |
||
352 | #else |
||
1 | office | 353 | FILE *fp; |
354 | int size; |
||
23 | office | 355 | #endif |
1 | office | 356 | |
23 | office | 357 | #if defined ___AsyncIO___ |
358 | if((fp = OpenAsync(dbFile, MODE_READ, ASYNC_BUF)) == NULL) { |
||
359 | #else |
||
1 | office | 360 | if((fp = fopen(dbFile, "r")) == NULL) { |
23 | office | 361 | #endif |
1 | office | 362 | fprintf(stderr, "Unable to open gather database for reading.\n"); |
363 | return 0; |
||
364 | } |
||
365 | |||
23 | office | 366 | #if defined ___AsyncIO___ |
367 | SeekAsync(fp, 0, MODE_END); |
||
368 | size = SeekAsync(fp, 0, MODE_CURRENT); |
||
369 | #else |
||
1 | office | 370 | fseek(fp, 0L, SEEK_END); |
371 | size = ftell(fp); |
||
23 | office | 372 | #endif |
1 | office | 373 | |
23 | office | 374 | #if defined ___AsyncIO___ |
375 | CloseAsync(fp); |
||
376 | #else |
||
1 | office | 377 | fclose(fp); |
23 | office | 378 | #endif |
379 | |||
1 | office | 380 | return size; |
381 | } |
||
382 | |||
383 | /* |
||
384 | * |
||
23 | office | 385 | * Counts the lines of a file. |
1 | office | 386 | */ |
23 | office | 387 | int GetFileLines(char *dbFile) { |
22 | office | 388 | #if defined ___AsyncIO___ |
389 | struct AsyncFile *fp; |
||
390 | LONG c; |
||
391 | #else |
||
1 | office | 392 | FILE *fp; |
22 | office | 393 | char c; |
394 | #endif |
||
1 | office | 395 | int lines; |
396 | |||
22 | office | 397 | #if defined ___AsyncIO___ |
398 | if((fp = OpenAsync(dbFile, MODE_READ, ASYNC_BUF)) == NULL) { |
||
399 | #else |
||
1 | office | 400 | if((fp = fopen(dbFile, "r")) == NULL) { |
22 | office | 401 | #endif |
1 | office | 402 | fprintf(stderr, "Unable to open gather database for reading.\n"); |
403 | return 0; |
||
404 | } |
||
405 | |||
406 | lines = 0; |
||
22 | office | 407 | #if defined ___AsyncIO___ |
23 | office | 408 | while(run && (c = ReadCharAsync(fp)) != -1) { |
22 | office | 409 | #else |
23 | office | 410 | while(run && fscanf(fp, "%c", &c) == 1) { |
22 | office | 411 | #endif |
23 | office | 412 | #if defined ___AmigaOS___ |
413 | // Check if CTRL+C was pressed and abort the program. |
||
414 | if(SetSignal(0L, SIGBREAKF_CTRL_C) & SIGBREAKF_CTRL_C) { |
||
415 | run = FALSE; |
||
416 | continue; |
||
417 | } |
||
418 | #endif |
||
1 | office | 419 | switch(c) { |
420 | case '\n': |
||
421 | ++lines; |
||
422 | break; |
||
423 | } |
||
424 | } |
||
425 | |||
22 | office | 426 | #if defined ___AsyncIO___ |
427 | CloseAsync(fp); |
||
428 | #else |
||
1 | office | 429 | fclose(fp); |
22 | office | 430 | #endif |
1 | office | 431 | |
432 | return lines; |
||
433 | } |
||
434 | |||
435 | /* |
||
436 | * |
||
437 | * Creates "files" temporary filenames. |
||
438 | */ |
||
23 | office | 439 | char **CreateTemporaryFiles(int files) { |
1 | office | 440 | char **tmpNames; |
441 | int count; |
||
442 | |||
23 | office | 443 | tmpNames = malloc(files * sizeof(*tmpNames)); |
1 | office | 444 | |
445 | if(verbose) { |
||
446 | fprintf(stdout, "Creating temporary files.\r"); |
||
447 | } |
||
448 | |||
449 | count = files; |
||
23 | office | 450 | while(run && --count > -1) { |
1 | office | 451 | #if defined ___AmigaOS___ |
452 | // Check if CTRL+C was pressed and abort the program. |
||
453 | if(SetSignal(0L, SIGBREAKF_CTRL_C) & SIGBREAKF_CTRL_C) { |
||
454 | run = FALSE; |
||
23 | office | 455 | continue; |
1 | office | 456 | } |
457 | #endif |
||
458 | tmpNames[count] = tmpnam(NULL); |
||
459 | |||
460 | if(verbose) { |
||
461 | fprintf(stdout, "Creating temporary files: %d%%\r", 100 - (int)(((float)count / files) * 100.0)); |
||
462 | } |
||
463 | } |
||
464 | |||
465 | if(verbose) { |
||
466 | fprintf(stdout, "\n"); |
||
467 | } |
||
468 | |||
469 | return tmpNames; |
||
470 | } |
||
471 | |||
472 | /* |
||
473 | * |
||
474 | * Writes lines from the database "dbFile" to temporary filenames "tmpNames". |
||
475 | */ |
||
23 | office | 476 | void WriteTemporaryFiles(char *dbFile, char **tmpNames, int tmpFiles, int tmpLines, int total) { |
22 | office | 477 | #if defined ___AsyncIO___ |
478 | struct AsyncFile *fp, *tp; |
||
479 | LONG c; |
||
480 | #else |
||
1 | office | 481 | FILE *fp, *tp; |
482 | char c; |
||
22 | office | 483 | #endif |
1 | office | 484 | int lines; |
485 | int linesWritten; |
||
486 | |||
22 | office | 487 | #if defined ___AsyncIO___ |
488 | if((fp = OpenAsync(dbFile, MODE_READ, ASYNC_BUF)) == NULL) { |
||
489 | #else |
||
1 | office | 490 | if((fp = fopen(dbFile, "r")) == NULL) { |
22 | office | 491 | #endif |
23 | office | 492 | fprintf(stderr, "Unable to open gather database '%s' for reading.\n", dbFile); |
1 | office | 493 | return; |
494 | } |
||
495 | |||
22 | office | 496 | #if defined ___AsyncIO___ |
23 | office | 497 | if((tp = OpenAsync(tmpNames[--tmpFiles], MODE_WRITE, ASYNC_BUF)) == NULL) { |
22 | office | 498 | #else |
23 | office | 499 | if((tp = fopen(tmpNames[--tmpFiles], "w")) == NULL) { |
22 | office | 500 | #endif |
1 | office | 501 | fprintf(stderr, "Unable to open temporary file '%s' for writing.\n", tmpNames[tmpFiles]); |
502 | return; |
||
503 | } |
||
504 | |||
505 | if(verbose) { |
||
506 | fprintf(stdout, "Writing to temporary files.\r"); |
||
507 | } |
||
508 | |||
509 | linesWritten = 0; |
||
510 | lines = 0; |
||
22 | office | 511 | #if defined ___AsyncIO___ |
512 | while(run && (c = ReadCharAsync(fp)) != -1) { |
||
513 | #else |
||
1 | office | 514 | while(run && fscanf(fp, "%c", &c) == 1) { |
22 | office | 515 | #endif |
1 | office | 516 | #if defined ___AmigaOS___ |
517 | // Check if CTRL+C was pressed and abort the program. |
||
518 | if(SetSignal(0L, SIGBREAKF_CTRL_C) & SIGBREAKF_CTRL_C) { |
||
519 | run = FALSE; |
||
23 | office | 520 | continue; |
1 | office | 521 | } |
522 | #endif |
||
523 | switch(c) { |
||
524 | case '\n': |
||
525 | // Increment the total written lines. |
||
526 | ++linesWritten; |
||
527 | |||
528 | if(verbose) { |
||
529 | fprintf(stdout, "Writing to temporary files: %d%%.\r", (int)(((float)linesWritten / total) * 100.0)); |
||
530 | } |
||
531 | |||
532 | // Write the newline character back. |
||
22 | office | 533 | #if defined ___AsyncIO___ |
534 | if(WriteCharAsync(tp, (UBYTE)c) != 1) { |
||
535 | #else |
||
1 | office | 536 | if(fprintf(tp, "%c", c) != 1) { |
22 | office | 537 | #endif |
1 | office | 538 | fprintf(stderr, "Unable to write to temporary file '%s'.\n", tmpNames[tmpFiles]); |
22 | office | 539 | #if defined ___AsyncIO___ |
540 | CloseAsync(tp); |
||
541 | CloseAsync(fp); |
||
542 | #else |
||
19 | office | 543 | fclose(tp); |
1 | office | 544 | fclose(fp); |
22 | office | 545 | #endif |
1 | office | 546 | return; |
547 | } |
||
548 | // Switch to the next temporary file. |
||
549 | if(++lines >= tmpLines) { |
||
550 | // If there are no temporary files left then run till the end. |
||
551 | if(tmpFiles - 1 < 0) { |
||
552 | break; |
||
553 | } |
||
554 | |||
555 | // Close the previous temporary file and write to the next temporary file. |
||
22 | office | 556 | #if defined ___AsyncIO___ |
557 | CloseAsync(tp); |
||
23 | office | 558 | if((tp = OpenAsync(tmpNames[--tmpFiles], MODE_WRITE, ASYNC_BUF)) == NULL) { |
22 | office | 559 | #else |
1 | office | 560 | fclose(tp); |
23 | office | 561 | if((tp = fopen(tmpNames[--tmpFiles], "w")) == NULL) { |
22 | office | 562 | #endif |
1 | office | 563 | fprintf(stderr, "Unable to open temporary file '%s' for writing.\n", tmpNames[tmpFiles]); |
22 | office | 564 | #if defined ___AsyncIO___ |
565 | CloseAsync(tp); |
||
566 | CloseAsync(fp); |
||
567 | #else |
||
19 | office | 568 | fclose(tp); |
1 | office | 569 | fclose(fp); |
22 | office | 570 | #endif |
1 | office | 571 | } |
572 | lines = 0; |
||
573 | break; |
||
574 | } |
||
575 | break; |
||
576 | default: |
||
22 | office | 577 | #if defined ___AsyncIO___ |
578 | if(WriteCharAsync(tp, (UBYTE)c) != 1) { |
||
579 | #else |
||
1 | office | 580 | if(fprintf(tp, "%c", c) != 1) { |
22 | office | 581 | #endif |
1 | office | 582 | fprintf(stderr, "Unable to write to temporary file '%s'.\n", tmpNames[tmpFiles]); |
22 | office | 583 | #if defined ___AsyncIO___ |
584 | CloseAsync(tp); |
||
585 | CloseAsync(fp); |
||
586 | #else |
||
1 | office | 587 | fclose(tp); |
588 | fclose(fp); |
||
22 | office | 589 | #endif |
1 | office | 590 | return; |
591 | } |
||
592 | break; |
||
593 | } |
||
594 | } |
||
595 | |||
596 | fprintf(stdout, "\n"); |
||
597 | |||
22 | office | 598 | #if defined ___AsyncIO___ |
599 | CloseAsync(tp); |
||
600 | CloseAsync(fp); |
||
601 | #else |
||
1 | office | 602 | fclose(tp); |
603 | fclose(fp); |
||
22 | office | 604 | #endif |
1 | office | 605 | } |
606 | |||
607 | /* |
||
608 | * |
||
609 | * Skips a line in a database file "fp". |
||
610 | */ |
||
22 | office | 611 | |
612 | #if defined ___AsyncIO___ |
||
23 | office | 613 | void SkipLine(struct AsyncFile *fp) { |
22 | office | 614 | LONG c; |
23 | office | 615 | while(run && (c = ReadCharAsync(fp)) != -1) { |
22 | office | 616 | #else |
1 | office | 617 | void SkipDatabaseLine(FILE *fp) { |
618 | char c; |
||
23 | office | 619 | while(run && fscanf(fp, "%c", &c) == 1) { |
22 | office | 620 | #endif |
23 | office | 621 | #if defined ___AmigaOS___ |
622 | // Check if CTRL+C was pressed and abort the program. |
||
623 | if(SetSignal(0L, SIGBREAKF_CTRL_C) & SIGBREAKF_CTRL_C) { |
||
624 | run = FALSE; |
||
625 | continue; |
||
626 | } |
||
627 | #endif |
||
22 | office | 628 | switch(c) { |
629 | case '\n': |
||
630 | return; |
||
1 | office | 631 | } |
632 | } |
||
633 | } |
||
634 | |||
635 | /* |
||
636 | * |
||
637 | * Reads a line from the database file "fp". |
||
638 | */ |
||
22 | office | 639 | #if defined ___AsyncIO___ |
23 | office | 640 | char *ReadLine(struct AsyncFile *fp) { |
22 | office | 641 | LONG c; |
642 | #else |
||
23 | office | 643 | char *ReadLine(FILE *fp) { |
1 | office | 644 | char c; |
22 | office | 645 | #endif |
1 | office | 646 | char *line; |
11 | office | 647 | int line_size; |
648 | int i; |
||
1 | office | 649 | |
11 | office | 650 | line_size = LINE_BUF; |
19 | office | 651 | line = malloc(line_size * sizeof(*line)); |
1 | office | 652 | |
11 | office | 653 | i = 0; |
22 | office | 654 | #if defined ___AsyncIO___ |
655 | while(run && (c = ReadCharAsync(fp)) != -1) { |
||
656 | #else |
||
1 | office | 657 | while(run && fscanf(fp, "%c", &c) == 1) { |
22 | office | 658 | #endif |
1 | office | 659 | #if defined ___AmigaOS___ |
660 | // Check if CTRL+C was pressed and abort the program. |
||
661 | if(SetSignal(0L, SIGBREAKF_CTRL_C) & SIGBREAKF_CTRL_C) { |
||
662 | run = FALSE; |
||
23 | office | 663 | continue; |
1 | office | 664 | } |
665 | #endif |
||
666 | switch(c) { |
||
667 | case '\n': |
||
668 | // Rewind the file by the number of read characters. |
||
22 | office | 669 | #if defined ___AsyncIO___ |
670 | if(SeekAsync(fp, -(i + 1), MODE_CURRENT) == -1) { |
||
671 | fprintf(stderr, "Could not seek in file.\n"); |
||
672 | return NULL; |
||
673 | } |
||
674 | #else |
||
11 | office | 675 | fseek(fp, -(i + 1), SEEK_CUR); |
22 | office | 676 | #endif |
1 | office | 677 | return line; |
678 | default: |
||
11 | office | 679 | if(strlen(line) == line_size) { |
680 | line_size = line_size * 1.5; |
||
19 | office | 681 | line = realloc(line, line_size * sizeof(*line)); |
11 | office | 682 | } |
683 | //line = realloc(line, (chars + 1 + 1) * sizeof(char)); |
||
684 | line[i] = c; |
||
685 | line[i + 1] = '\0'; |
||
1 | office | 686 | break; |
687 | } |
||
11 | office | 688 | ++i; |
1 | office | 689 | } |
690 | |||
691 | return NULL; |
||
692 | } |
||
693 | |||
694 | /* |
||
695 | * |
||
696 | * Merges temporary files "tmpNames" into a database "dbFile". |
||
697 | */ |
||
698 | void MergeDatabase(char *dbFile, char **tmpNames, int files, int lines) { |
||
22 | office | 699 | #if defined ___AsyncIO___ |
700 | struct AsyncFile *fp; |
||
701 | struct AsyncFile **tp; |
||
702 | #else |
||
1 | office | 703 | FILE *fp; |
704 | FILE **tp; |
||
22 | office | 705 | #endif |
1 | office | 706 | int i; |
14 | office | 707 | int j; |
1 | office | 708 | char *tmp; |
14 | office | 709 | char *min; |
1 | office | 710 | int count; |
711 | |||
22 | office | 712 | #if defined ___AsyncIO___ |
23 | office | 713 | if((fp = OpenAsync(dbFile, MODE_WRITE, ASYNC_BUF)) == NULL) { |
22 | office | 714 | #else |
23 | office | 715 | if((fp = fopen(dbFile, "w")) == NULL) { |
22 | office | 716 | #endif |
1 | office | 717 | fprintf(stderr, "Unable to open gather database for writing.\n"); |
718 | return; |
||
719 | } |
||
720 | |||
721 | // Allocate as many file pointers as temporary files. |
||
19 | office | 722 | tp = malloc(files * sizeof(*tp)); |
1 | office | 723 | |
724 | // Open all temporary files for reading. |
||
725 | for(i = 0; i < files; ++i) { |
||
22 | office | 726 | #if defined ___AsyncIO___ |
727 | if((tp[i] = OpenAsync(tmpNames[i], MODE_READ, ASYNC_BUF)) == NULL) { |
||
728 | #else |
||
1 | office | 729 | if((tp[i] = fopen(tmpNames[i], "r")) == NULL) { |
22 | office | 730 | #endif |
1 | office | 731 | fprintf(stderr, "Unable to open temporary file '%s' for reading.\n", tmpNames[i]); |
732 | // Close all temporary files. |
||
733 | --i; |
||
734 | while(i >= 0) { |
||
22 | office | 735 | #if defined ___AsyncIO___ |
736 | CloseAsync(tp[i]); |
||
737 | #else |
||
1 | office | 738 | fclose(tp[i]); |
22 | office | 739 | #endif |
1 | office | 740 | } |
741 | return; |
||
742 | } |
||
743 | } |
||
744 | |||
745 | if(verbose) { |
||
746 | fprintf(stdout, "Merging all database lines in temporary files.\r"); |
||
747 | } |
||
748 | |||
749 | count = lines; |
||
14 | office | 750 | j = 0; |
1 | office | 751 | while(run && --count > -1) { |
752 | #if defined ___AmigaOS___ |
||
753 | // Check if CTRL+C was pressed and abort the program. |
||
754 | if(SetSignal(0L, SIGBREAKF_CTRL_C) & SIGBREAKF_CTRL_C) { |
||
755 | run = FALSE; |
||
23 | office | 756 | continue; |
1 | office | 757 | } |
758 | #endif |
||
759 | // Find the smallest line in all temporary files. |
||
760 | if(verbose) { |
||
761 | fprintf(stdout, "Merging all database lines in temporary files: %d%%.\r", 100 - (int)(((float)count / lines) * 100.0)); |
||
762 | } |
||
763 | |||
14 | office | 764 | min = NULL; |
1 | office | 765 | for(i = 0; i < files; ++i) { |
23 | office | 766 | tmp = ReadLine(tp[i]); |
1 | office | 767 | if(tmp == NULL) { |
768 | continue; |
||
769 | } |
||
14 | office | 770 | if(min == NULL || strncmp(tmp, min, strlen(tmp)) < 0) { |
771 | if(min != NULL) { |
||
2 | office | 772 | // Free previous instance. |
14 | office | 773 | free(min); |
2 | office | 774 | } |
19 | office | 775 | min = malloc((strlen(tmp) + 1) * sizeof(*min)); |
14 | office | 776 | sprintf(min, "%s", tmp); |
1 | office | 777 | // Remember the index of the file where the smallest entry has been found. |
14 | office | 778 | j = i; |
1 | office | 779 | free(tmp); |
780 | continue; |
||
781 | } |
||
782 | free(tmp); |
||
783 | } |
||
784 | |||
785 | // Forward the file where the smallest line was found. |
||
23 | office | 786 | SkipLine(tp[j]); |
1 | office | 787 | |
788 | // Write the smallest line. |
||
14 | office | 789 | if(min != NULL) { |
22 | office | 790 | #if defined ___AsyncIO___ |
791 | WriteAsync(fp, min, (LONG)(strlen(min) * sizeof(char))); |
||
792 | WriteAsync(fp, "\n", 1 * sizeof(char)); |
||
793 | #else |
||
14 | office | 794 | fprintf(fp, "%s\n", min); |
22 | office | 795 | #endif |
14 | office | 796 | free(min); |
1 | office | 797 | } |
798 | } |
||
799 | |||
800 | // Write out any remaining contents from the temporary files. |
||
801 | for(i = 0; i < files; ++i) { |
||
23 | office | 802 | tmp = ReadLine(tp[i]); |
1 | office | 803 | if(tmp == NULL) { |
804 | continue; |
||
805 | } |
||
22 | office | 806 | #if defined ___AsyncIO___ |
807 | WriteAsync(fp, tmp, (LONG)(strlen(tmp) * sizeof(char))); |
||
808 | WriteAsync(fp, "\n", 1 * sizeof(char)); |
||
809 | #else |
||
1 | office | 810 | fprintf(fp, "%s\n", tmp); |
22 | office | 811 | #endif |
14 | office | 812 | free(tmp); |
1 | office | 813 | } |
814 | |||
815 | // Close and delete all temporary files. |
||
816 | for(i = 0; i < files; ++i) { |
||
22 | office | 817 | #if defined ___AsyncIO___ |
818 | CloseAsync(tp[i]); |
||
819 | #else |
||
1 | office | 820 | fclose(tp[i]); |
22 | office | 821 | #endif |
1 | office | 822 | // Delete temporary file. |
823 | remove(tmpNames[i]); |
||
824 | } |
||
825 | |||
826 | if(verbose) { |
||
827 | fprintf(stdout, "\n"); |
||
828 | } |
||
829 | |||
22 | office | 830 | #if defined ___AsyncIO___ |
831 | CloseAsync(fp); |
||
832 | #else |
||
1 | office | 833 | fclose(fp); |
22 | office | 834 | #endif |
1 | office | 835 | } |
836 | |||
837 | /* |
||
838 | * |
||
839 | * Indexes a "path" by creating a database "dbFile". |
||
840 | */ |
||
841 | void Gather(char *dbFile, char *path) { |
||
842 | stringStack *stack = stringStackCreate(1); |
||
843 | stats *stats = malloc(sizeof(stats)); |
||
844 | char **tmpNames; |
||
845 | int dbSize, dbLines, tmpFiles, tmpLines; |
||
846 | int i; |
||
847 | |||
848 | // Initialize metrics. |
||
849 | stats->dirs = 0; |
||
850 | stats->files = 0; |
||
851 | |||
852 | // Push the first path onto the stack. |
||
853 | stringStackPush(stack, path); |
||
854 | |||
855 | // Generate the database file. |
||
856 | UpdateDatabase(dbFile, stack, stats); |
||
857 | |||
858 | // Get the database metrics. |
||
23 | office | 859 | dbSize = GetFileSize(dbFile); |
860 | dbLines = GetFileLines(dbFile); |
||
1 | office | 861 | |
862 | // Compute the amount of temporary files needed. |
||
863 | tmpFiles = dbSize / MAX_MEM; |
||
864 | |||
865 | // In case no temporary files are required, |
||
866 | // just sort the database and terminate. |
||
2 | office | 867 | if(tmpFiles <= 1) { |
1 | office | 868 | SortDatabase(dbFile); |
869 | return; |
||
870 | } |
||
871 | |||
872 | tmpLines = dbLines / tmpFiles; |
||
873 | |||
874 | // Create temporary files. |
||
23 | office | 875 | if((tmpNames = CreateTemporaryFiles(tmpFiles)) == NULL) { |
1 | office | 876 | fprintf(stderr, "Unable to create temporary files.\n"); |
877 | return; |
||
878 | } |
||
879 | |||
880 | // Write "tmpLines" to temporary files in "tmpFiles" from "dbFile". |
||
23 | office | 881 | WriteTemporaryFiles(dbFile, tmpNames, tmpFiles, tmpLines, dbLines); |
1 | office | 882 | |
883 | // Sort the temporary files. |
||
884 | for(i = 0; i < tmpFiles; ++i) { |
||
885 | SortDatabase(tmpNames[i]); |
||
886 | } |
||
887 | |||
888 | MergeDatabase(dbFile, tmpNames, tmpFiles, dbLines); |
||
889 | } |
||
890 | |||
11 | office | 891 | void usage(char *name) { |
892 | fprintf(stdout, "Hunt & Gather - %s, a file index generating tool. \n", name); |
||
19 | office | 893 | fprintf(stdout, "Version: %s \n", PROGRAM_VERSION); |
11 | office | 894 | fprintf(stdout, " \n"); |
895 | fprintf(stdout, "SYNTAX: %s [-q] DATABASE \n", name); |
||
896 | fprintf(stdout, " \n"); |
||
897 | fprintf(stdout, " -q Do not print out any messages. \n"); |
||
898 | fprintf(stdout, " \n"); |
||
899 | fprintf(stdout, "DATABASE is a path to where the indexed results will be \n"); |
||
900 | fprintf(stdout, "stored for searching with the Hunt tool. \n"); |
||
901 | fprintf(stdout, " \n"); |
||
902 | fprintf(stdout, "(c) 2021 Wizardry and Steamworks, MIT. \n"); |
||
903 | } |
||
904 | |||
1 | office | 905 | /* |
906 | * |
||
907 | * Main entry point. |
||
908 | */ |
||
909 | int main(int argc, char **argv) { |
||
910 | int option; |
||
2 | office | 911 | char *dbFile; |
19 | office | 912 | char *path; |
10 | office | 913 | struct stat dirStat; |
19 | office | 914 | #if defined ___AmigaOS___ |
915 | BPTR lock; |
||
916 | #endif |
||
1 | office | 917 | |
918 | // Bind handler to SIGINT. |
||
919 | signal(SIGINT, SignalHandler); |
||
920 | |||
2 | office | 921 | dbFile = DEFAULT_DATABASE_FILE; |
922 | while((option = getopt(argc, argv, "hqd:")) != -1) { |
||
1 | office | 923 | switch(option) { |
2 | office | 924 | case 'd': |
925 | dbFile = optarg; |
||
926 | break; |
||
1 | office | 927 | case 'q': |
928 | verbose = FALSE; |
||
929 | break; |
||
930 | case 'h': |
||
11 | office | 931 | usage(argv[0]); |
2 | office | 932 | return 0; |
1 | office | 933 | case '?': |
934 | fprintf(stderr, "Invalid option %ct.\n", optopt); |
||
935 | return 1; |
||
936 | } |
||
937 | } |
||
938 | |||
10 | office | 939 | |
940 | if(optind >= argc) { |
||
11 | office | 941 | usage(argv[0]); |
1 | office | 942 | return 1; |
943 | } |
||
944 | |||
19 | office | 945 | #if defined ___AmigaOS___ |
946 | path = malloc(PATH_MAX * sizeof(*path)); |
||
947 | lock = Lock(argv[optind], SHARED_LOCK); |
||
948 | NameFromLock(lock, path, PATH_MAX); |
||
949 | UnLock(lock); |
||
950 | #else |
||
951 | path = realpath(argv[optind], NULL); |
||
952 | #endif |
||
953 | |||
954 | stat(path, &dirStat); |
||
10 | office | 955 | if(!S_ISDIR(dirStat.st_mode)) { |
5 | office | 956 | fprintf(stderr, "Path '%s' is not a directory.\n", argv[optind]); |
1 | office | 957 | return 1; |
958 | } |
||
959 | |||
2 | office | 960 | if(verbose) { |
961 | fprintf(stdout, "Gathering to database file: %s\n", dbFile); |
||
962 | } |
||
963 | |||
1 | office | 964 | // Gather. |
19 | office | 965 | Gather(dbFile, path); |
1 | office | 966 | |
19 | office | 967 | free(path); |
968 | |||
1 | office | 969 | return 0; |
970 | } |