HuntnGather – Blame information for rev 53

Subversion Repositories:
Rev:
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>
38 office 8 #include <math.h>
29 office 9 #if !defined ___AmigaOS___
1 office 10 #include <dirent.h>
29 office 11 #include <sys/stat.h>
1 office 12 #include <signal.h>
27 office 13 #endif
1 office 14  
15 #include <sys/types.h>
19 office 16 #include <sys/syslimits.h>
1 office 17  
26 office 18 #if defined ___AmigaOS___
1 office 19 #include <proto/dos.h>
20 #include <proto/exec.h>
26 office 21 #include <proto/locale.h>
33 office 22 #include <clib/utility_protos.h>
26 office 23 #endif
1 office 24  
22 office 25 #if defined ___AsyncIO___
26 #include <asyncio.h>
27 #endif
28  
5 office 29 #if !defined ___HAVE_GETOPT___
33 office 30 #include "/shared/getopt.h"
1 office 31 #endif
32  
46 office 33 #include "stack.h"
33 office 34 #include "/shared/utilities.h"
31 office 35  
52 office 36 #define PROGRAM_VERSION "1.7.7"
19 office 37  
5 office 38 #if defined ___AmigaOS___
39 /*************************************************************************/
40 /* Version string used for querrying the program version. */
41 /*************************************************************************/
42 TEXT version_string[] =
19 office 43 "\0$VER: Gather " PROGRAM_VERSION " "__DATE__" by Wizardry and Steamworks";
5 office 44 #endif
1 office 45  
33 office 46 int PROGRAM_RUN = TRUE;
47 int PROGRAM_VERBOSE = TRUE;
48 int maxmem = MAX_MEM;
1 office 49  
26 office 50 // Define global locale for string compare.
51 #if defined ___AmigaOS___
52 struct Locale *locale;
53 #endif
54  
1 office 55 void SignalHandler(int sig) {
33 office 56 /* Toggle the run flag to stop execution. */
57 PROGRAM_RUN = FALSE;
1 office 58 }
59  
26 office 60 /*
61 *
62 * Used for sorting database lines.
63 */
64 int QsortCompare(const void *a, const void *b) {
65 #if defined ___AmigaOS___
31 office 66 return StrnCmp(
67 locale,
68 (STRPTR)(*(const char **)a),
69 (STRPTR)*((const char **)b),
70 -1,
71 SC_ASCII
72 );
26 office 73 #else
31 office 74 return strcmp(*(const char **)a, *(const char **)b);
26 office 75 #endif
1 office 76 }
77  
78 /*
79 *
33 office 80 * Sorts a database file lexicographically.
1 office 81 */
33 office 82 void SortDatabase(char *dbFile, int lines) {
22 office 83 #if defined ___AsyncIO___
84 struct AsyncFile *fp;
85 #else
1 office 86 FILE *fp;
22 office 87 #endif
33 office 88 char **database;
89 dbEntry *entry;
38 office 90 dbLine *line;
33 office 91 char *rem;
92 int count;
93 int i;
1 office 94  
33 office 95 // Open database file for reading.
22 office 96 #if defined ___AsyncIO___
97 if((fp = OpenAsync(dbFile, MODE_READ, ASYNC_BUF)) == NULL) {
98 #else
1 office 99 if((fp = fopen(dbFile, "r")) == NULL) {
22 office 100 #endif
33 office 101 fprintf(stderr, "Could not open file '%s' for reading.\n", dbFile);
102 return;
1 office 103 }
104  
33 office 105 if((database = malloc(lines * sizeof(*database))) == NULL) {
106 fprintf(stderr, "Memory allocation failure.\n");
26 office 107 #if defined ___AsyncIO___
33 office 108 CloseAsync(fp);
26 office 109 #else
110 fclose(fp);
111 #endif
33 office 112 return;
26 office 113 }
22 office 114  
33 office 115 if(PROGRAM_VERBOSE) {
116 fprintf(stdout, "Reading lines from file '%s' to array...\n", dbFile);
1 office 117 }
24 office 118  
33 office 119 count = 0;
120 while(PROGRAM_RUN && (line = ReadLine(fp)) != NULL) {
1 office 121 #if defined ___AmigaOS___
122 // Check if CTRL+C was pressed and abort the program.
33 office 123 if(SetSignal(0L, SIGBREAKF_CTRL_C) & SIGBREAKF_CTRL_C) {
124 PROGRAM_RUN = FALSE;
1 office 125 continue;
126 }
127 #endif
33 office 128 if((entry = CreateDatabaseEntry(line)) == NULL) {
129 fprintf(stderr, "Unable to create database entry.\n");
38 office 130  
131 free(line->string);
33 office 132 free(line);
38 office 133 line = NULL;
134  
22 office 135 #if defined ___AsyncIO___
33 office 136 CloseAsync(fp);
22 office 137 #else
33 office 138 fclose(fp);
22 office 139 #endif
33 office 140 return;
26 office 141 }
142  
33 office 143 if((database[count] = malloc((strlen(entry->name) + strlen(entry->path) + 1 + 1) * sizeof(*database[count]))) == NULL) {
144 fprintf(stderr, "Memory allocation failure.\n");
38 office 145  
146 // Free database entry.
33 office 147 free(entry->name);
148 free(entry->path);
149 free(entry);
38 office 150 entry = NULL;
151  
152 // Free the line.
153 free(line->string);
33 office 154 free(line);
38 office 155 line = NULL;
156  
22 office 157 #if defined ___AsyncIO___
33 office 158 CloseAsync(fp);
22 office 159 #else
33 office 160 fclose(fp);
22 office 161 #endif
33 office 162 return;
23 office 163 }
26 office 164  
33 office 165 sprintf(database[count], "%s\t%s", entry->name, entry->path);
166 ++count;
26 office 167  
33 office 168 // Free the database entry.
169 free(entry->name);
170 free(entry->path);
171 free(entry);
38 office 172 entry = NULL;
1 office 173  
38 office 174 // Free the line.
175 free(line->string);
31 office 176 free(line);
38 office 177 line = NULL;
31 office 178 }
179  
26 office 180 #if defined ___AsyncIO___
33 office 181 CloseAsync(fp);
26 office 182 #else
33 office 183 fclose(fp);
26 office 184 #endif
1 office 185  
33 office 186 if(PROGRAM_VERBOSE) {
187 fprintf(stdout, "Sorting %d lines in '%s'...\n", count, dbFile);
26 office 188 }
15 office 189  
33 office 190 // Sort the database.
191 qsort(database, (unsigned int)count, sizeof(char *), QsortCompare);
15 office 192  
33 office 193 if(PROGRAM_VERBOSE) {
194 fprintf(stdout, "Writing %d sorted lines to '%s'...\n", count, dbFile);
1 office 195 }
196  
26 office 197 // Write the database lines back to the database.
23 office 198 #if defined ___AsyncIO___
26 office 199 if((fp = OpenAsync(dbFile, MODE_WRITE, ASYNC_BUF)) == NULL) {
23 office 200 #else
26 office 201 if((fp = fopen(dbFile, "w")) == NULL) {
23 office 202 #endif
33 office 203 fprintf(stderr, "Could not open file '%s' for writing.\n", dbFile);
26 office 204 return;
1 office 205 }
206  
26 office 207 rem = NULL;
33 office 208 for(i = 0; PROGRAM_RUN && i < count; ++i) {
26 office 209 #if defined ___AmigaOS___
210 // Check if CTRL+C was pressed and abort the program.
30 office 211 if(SetSignal(0L, SIGBREAKF_CTRL_C) & SIGBREAKF_CTRL_C) {
33 office 212 PROGRAM_RUN = FALSE;
26 office 213 continue;
214 }
215 #endif
216  
217 if(rem != NULL) {
218 #if defined ___AmigaOS___
33 office 219 if(StrnCmp(locale, database[i], rem, -1, SC_ASCII) == 0) {
26 office 220 #else
33 office 221 if(strcmp(database[i], rem) == 0) {
26 office 222 #endif
223 continue;
224 }
225 }
226  
23 office 227 #if defined ___AsyncIO___
33 office 228 WriteAsync(fp, database[i], (LONG)strlen(database[i]));
26 office 229 WriteAsync(fp, "\n", 1);
23 office 230 #else
33 office 231 fprintf(fp, "%s\n", database[i]);
23 office 232 #endif
1 office 233  
26 office 234 if(rem != NULL) {
235 free(rem);
38 office 236 rem = NULL;
26 office 237 }
238  
33 office 239 rem = malloc((strlen(database[i]) + 1) * sizeof(*rem));
240 sprintf(rem, "%s", database[i]);
26 office 241 }
242  
31 office 243 if(rem != NULL) {
244 free(rem);
38 office 245 rem = NULL;
31 office 246 }
247  
23 office 248 #if defined ___AsyncIO___
26 office 249 CloseAsync(fp);
23 office 250 #else
1 office 251 fclose(fp);
23 office 252 #endif
253  
33 office 254 if(PROGRAM_VERBOSE) {
255 fprintf(stdout, "Disposing %d lines of file '%s'...\n", count, dbFile);
26 office 256 }
257  
33 office 258 // Free up database.
259 for(i = 0; i < count; ++i) {
260 free(database[i]);
38 office 261 database[i] = NULL;
26 office 262 }
263  
33 office 264 free(database);
1 office 265 }
266  
267 /*
268 *
26 office 269 * Updates a database file "dbFile".
270 */
38 office 271 dbStats *CollectFiles(char *dbFile, VECTOR *paths) {
26 office 272 #if defined ___AsyncIO___
273 struct AsyncFile *fp;
274 #else
275 FILE *fp;
276 #endif
28 office 277 #if defined ___AmigaOS___
33 office 278 struct FileInfoBlock *FIB;
279 BPTR lock;
28 office 280 #else
26 office 281 DIR *dir;
282 struct dirent *entry;
28 office 283 #endif
46 office 284 stack *stack;
33 office 285 dbStats *stats = NULL;
26 office 286 int i;
287 char *path;
28 office 288 char *sub;
26 office 289  
290 #if defined ___AsyncIO___
291 if((fp = OpenAsync(dbFile, MODE_APPEND, ASYNC_BUF)) == NULL) {
292 #else
30 office 293 if((fp = fopen(dbFile, "a")) == NULL) {
26 office 294 #endif
33 office 295 fprintf(stderr, "Could not open file '%s' for writing.\n", dbFile);
31 office 296 return NULL;
26 office 297 }
298  
33 office 299 if(PROGRAM_VERBOSE) {
26 office 300 fprintf(stdout, "Collecting files...\r");
1 office 301 }
302  
31 office 303 // Initialize metrics.
304 if((stats = malloc(sizeof(*stats))) == NULL) {
305 fprintf(stderr, "Memory allocation failure.\n");
306 #if defined ___AsyncIO___
307 CloseAsync(fp);
308 #else
309 fclose(fp);
310 #endif
311 return NULL;
312 }
313  
314 stats->dirs = 0;
315 stats->files = 0;
33 office 316 stats->lines = 0;
317 stats->size = 0;
31 office 318  
26 office 319 // Push the first path onto the stack.
46 office 320 stack = stackCreate((unsigned int)paths->length);
38 office 321 for(i = 0; PROGRAM_RUN && i < paths->length; ++i) {
47 office 322 if(PROGRAM_VERBOSE) {
323 fprintf(stdout, "Pushing path '%s'\n", (char *)paths->array[i]);
324 }
46 office 325 stackPush(stack, paths->array[i], (strlen(paths->array[i]) + 1) * sizeof(char));
26 office 326 }
327  
46 office 328 while(PROGRAM_RUN && !stackIsEmpty(stack)) {
1 office 329 #if defined ___AmigaOS___
330 // Check if CTRL+C was pressed and abort the program.
331 if(SetSignal(0L, SIGBREAKF_CTRL_C) & SIGBREAKF_CTRL_C) {
33 office 332 PROGRAM_RUN = FALSE;
23 office 333 continue;
1 office 334 }
335 #endif
46 office 336 if((path = (char *)stackPop(stack)) == NULL) {
26 office 337 break;
338 }
1 office 339  
28 office 340 #if defined ___AmigaOS___
33 office 341 if((lock = Lock(path, ACCESS_READ)) == NULL) {
28 office 342 fprintf(stderr, "Could not lock path '%s' for reading.\n", path);
343 free(path);
38 office 344 path = NULL;
28 office 345 continue;
26 office 346 }
24 office 347  
33 office 348 if((FIB = AllocDosObject(DOS_FIB, NULL)) == NULL) {
349 fprintf(stderr, "File information block for path '%s' could not be allocated.\n", path);
350 UnLock(lock);
28 office 351 free(path);
38 office 352 path = NULL;
28 office 353 continue;
354 }
355  
33 office 356 if(Examine(lock, FIB) == FALSE) {
28 office 357 fprintf(stderr, "Path '%s' could not be examined.\n", path);
33 office 358 FreeDosObject(DOS_FIB, FIB);
38 office 359 FIB = NULL;
33 office 360 UnLock(lock);
28 office 361 free(path);
38 office 362 path = NULL;
28 office 363 continue;
364 }
365  
33 office 366 while(PROGRAM_RUN && ExNext(lock, FIB)) {
26 office 367 // Check if CTRL+C was pressed and abort the program.
368 if(SetSignal(0L, SIGBREAKF_CTRL_C) & SIGBREAKF_CTRL_C) {
33 office 369 PROGRAM_RUN = FALSE;
31 office 370 continue;
26 office 371 }
28 office 372 #else
373  
374 if((dir = opendir(path)) == NULL) {
33 office 375 fprintf(stderr, "Directory '%s' could not be opened.\n", path);
28 office 376 free(path);
38 office 377 path = NULL;
28 office 378 continue;
379 }
380  
33 office 381 while(PROGRAM_RUN && (entry = readdir(dir)) != NULL) {
26 office 382 #endif
383 switch(path[strlen(path) - 1]) {
384 case '/':
385 case ':': // This is a drive path.
28 office 386 #if defined ___AmigaOS___
33 office 387 if((sub = malloc(strlen(path) + strlen(FIB->fib_FileName) + 1)) == NULL) {
28 office 388 #else
30 office 389 if((sub = malloc(strlen(path) + strlen(entry->d_name) + 1)) == NULL) {
28 office 390 #endif
26 office 391 fprintf(stderr, "Memory allocation failure.\n");
28 office 392 #if defined ___AmigaOS___
33 office 393 FreeDosObject(DOS_FIB, FIB);
38 office 394 FIB = NULL;
33 office 395 UnLock(lock);
28 office 396 #else
26 office 397 closedir(dir);
28 office 398 #endif
26 office 399 free(path);
38 office 400 path = NULL;
401  
46 office 402 stackDestroy(stack);
26 office 403 #if defined ___AsyncIO___
404 CloseAsync(fp);
405 #else
406 fclose(fp);
407 #endif
408 return NULL;
409 }
28 office 410 #if defined ___AmigaOS___
33 office 411 sprintf(sub, "%s%s", path, FIB->fib_FileName);
28 office 412 #else
413 sprintf(sub, "%s%s", path, entry->d_name);
414 #endif
26 office 415 break;
416 default:
28 office 417 #if defined ___AmigaOS___
33 office 418 if((sub = malloc(strlen(path) + strlen(FIB->fib_FileName) + 1 + 1)) == NULL) {
28 office 419 #else
30 office 420 if((sub = malloc(strlen(path) + strlen(entry->d_name) + 1 + 1)) == NULL) {
28 office 421 #endif
26 office 422 fprintf(stderr, "Memory allocation failure.\n");
28 office 423 #if defined ___AmigaOS___
33 office 424 FreeDosObject(DOS_FIB, FIB);
38 office 425 FIB = NULL;
33 office 426 UnLock(lock);
28 office 427 #else
26 office 428 closedir(dir);
28 office 429 #endif
26 office 430 free(path);
38 office 431 path = NULL;
432  
46 office 433 stackDestroy(stack);
26 office 434 #if defined ___AsyncIO___
435 CloseAsync(fp);
436 #else
437 fclose(fp);
438 #endif
439 return NULL;
440 }
28 office 441 #if defined ___AmigaOS___
33 office 442 sprintf(sub, "%s/%s", path, FIB->fib_FileName);
28 office 443 #else
444 sprintf(sub, "%s/%s", path, entry->d_name);
445 #endif
26 office 446 break;
447 }
28 office 448  
33 office 449 switch(GetFsType(sub)) {
450 case UNKNOWN:
451 free(sub);
38 office 452 sub = NULL;
33 office 453 continue;
454 case REGULAR:
455 ++stats->files;
30 office 456  
33 office 457 if(PROGRAM_VERBOSE) {
458 fprintf(stdout,
38 office 459 "Gathered '%d' directories and '%d' files.\r",
26 office 460 stats->dirs,
461 stats->files);
33 office 462 }
463 break;
464 case DIRECTORY:
46 office 465 stackPush(stack, sub, (strlen(sub) + 1) * sizeof(char));
26 office 466  
33 office 467 ++stats->dirs;
26 office 468  
33 office 469 if(PROGRAM_VERBOSE) {
470 fprintf(stdout,
38 office 471 "Gathered '%d' directories and '%d' files.\r",
33 office 472 stats->dirs,
473 stats->files);
474 }
28 office 475  
33 office 476 free(sub);
38 office 477 sub = NULL;
33 office 478 continue;
479 }
30 office 480  
26 office 481 #if defined ___NOCASE_FS___
28 office 482 #if defined ___AmigaOS___
33 office 483 StrUpr(FIB->fib_FileName);
28 office 484 #else
33 office 485 StrUpr(entry->d_name);
26 office 486 #endif
28 office 487 #endif
30 office 488  
26 office 489 // Write to database file.
490 #if defined ___AsyncIO___
28 office 491 #if defined ___AmigaOS___
33 office 492 WriteAsync(fp, FIB->fib_FileName, (LONG)strlen(FIB->fib_FileName));
493 stats->size = stats->size + strlen(FIB->fib_FileName);
28 office 494 #else
26 office 495 WriteAsync(fp, entry->d_name, (LONG)strlen(entry->d_name));
33 office 496 stats->size = stats->size + strlen(entry->d_name);
28 office 497 #endif
26 office 498 WriteAsync(fp, "\t", 1);
33 office 499 ++stats->size;
28 office 500 WriteAsync(fp, sub, (LONG)strlen(sub));
33 office 501 stats->size = stats->size + strlen(sub);
26 office 502 WriteAsync(fp, "\n", 1);
33 office 503 ++stats->size;
26 office 504 #else
28 office 505 #if defined ___AmigaOS___
33 office 506 fprintf(fp, "%s\t%s\n", FIB->fib_FileName, sub);
507 stats->size = stats->size + strlen(FIB->fib_FileName) + strlen(sub) + 1 + 1 + 1;
28 office 508 #else
509 fprintf(fp, "%s\t%s\n", entry->d_name, sub);
33 office 510 stats->size = stats->size + strlen(entry->d_name) + strlen(sub) + 1 + 1 + 1;
26 office 511 #endif
28 office 512 #endif
33 office 513  
514 ++stats->lines;
515  
28 office 516 free(sub);
38 office 517 sub = NULL;
26 office 518 }
519  
28 office 520 #if defined ___AmigaOS___
33 office 521 FreeDosObject(DOS_FIB, FIB);
38 office 522 FIB = NULL;
33 office 523 UnLock(lock);
28 office 524 #else
26 office 525 closedir(dir);
28 office 526 #endif
26 office 527 free(path);
38 office 528 path = NULL;
1 office 529 }
530  
33 office 531 if(PROGRAM_VERBOSE) {
1 office 532 fprintf(stdout, "\n");
533 }
534  
46 office 535 stackDestroy(stack);
26 office 536  
537 #if defined ___AsyncIO___
538 CloseAsync(fp);
539 #else
540 fclose(fp);
541 #endif
542  
543 return stats;
544  
1 office 545 }
546  
547 /*
548 *
549 * Writes lines from the database "dbFile" to temporary filenames "tmpNames".
550 */
38 office 551 void WriteTemporaryFiles(char *dbFile, VECTOR *tmpNames, int tmpLines, int total) {
22 office 552 #if defined ___AsyncIO___
553 struct AsyncFile *fp, *tp;
554 #else
1 office 555 FILE *fp, *tp;
22 office 556 #endif
1 office 557 int lines;
26 office 558 int write;
38 office 559 int files;
41 office 560 dbLine *line = NULL;
1 office 561  
22 office 562 #if defined ___AsyncIO___
563 if((fp = OpenAsync(dbFile, MODE_READ, ASYNC_BUF)) == NULL) {
564 #else
1 office 565 if((fp = fopen(dbFile, "r")) == NULL) {
22 office 566 #endif
33 office 567 fprintf(stderr, "Could not open file '%s' for reading.\n", dbFile);
1 office 568 return;
569 }
570  
38 office 571 files = tmpNames->length;
22 office 572 #if defined ___AsyncIO___
38 office 573 if((tp = OpenAsync(tmpNames->array[--files], MODE_WRITE, ASYNC_BUF)) == NULL) {
22 office 574 #else
38 office 575 if((tp = fopen(tmpNames->array[--files], "w")) == NULL) {
22 office 576 #endif
38 office 577 fprintf(stderr, "Could not open file '%s' for writing.\n", (char *)tmpNames->array[files]);
24 office 578 #if defined ___AsyncIO___
579 CloseAsync(fp);
580 #else
581 fclose(fp);
582 #endif
1 office 583 return;
584 }
585  
33 office 586 if(PROGRAM_VERBOSE) {
24 office 587 fprintf(stdout, "Writing to temporary files...\r");
1 office 588 }
589  
26 office 590 write = 0;
1 office 591 lines = 0;
41 office 592  
593 while(PROGRAM_RUN && (line = ReadLine(fp)) != NULL) {
1 office 594 #if defined ___AmigaOS___
595 // Check if CTRL+C was pressed and abort the program.
596 if(SetSignal(0L, SIGBREAKF_CTRL_C) & SIGBREAKF_CTRL_C) {
41 office 597 free(line->string);
598 free(line);
599 line = NULL;
600  
33 office 601 PROGRAM_RUN = FALSE;
23 office 602 continue;
1 office 603 }
604 #endif
605  
22 office 606 #if defined ___AsyncIO___
41 office 607 WriteAsync(tp, line->string, (LONG)line->length);
608 WriteAsync(tp, "\n", 1);
22 office 609 #else
41 office 610 fprintf(tp, "%s\n", line->string);
22 office 611 #endif
1 office 612  
41 office 613 ++write;
614  
615 if(PROGRAM_VERBOSE) {
616 fprintf(stdout, "Writing to temporary files: %d%%.\r", (int)(((float)write / total) * 100.0));
617 }
618  
619 // Switch to the next temporary file.
620 if(++lines >= tmpLines) {
621 // If there are no temporary files left then run till the end.
622 if(files - 1 < 0) {
623 free(line->string);
624 free(line);
625 line = NULL;
626 continue;
627 }
628  
629 // Close the previous temporary file and write to the next temporary file.
22 office 630 #if defined ___AsyncIO___
41 office 631 CloseAsync(tp);
632 if((tp = OpenAsync(tmpNames->array[--files], MODE_WRITE, ASYNC_BUF)) == NULL) {
22 office 633 #else
41 office 634 fclose(tp);
635 if((tp = fopen(tmpNames->array[--files], "w")) == NULL) {
22 office 636 #endif
41 office 637 fprintf(stderr, "Could not open '%s' for writing.\n", (char *)tmpNames->array[files]);
22 office 638 #if defined ___AsyncIO___
41 office 639 CloseAsync(fp);
22 office 640 #else
41 office 641 fclose(fp);
22 office 642 #endif
41 office 643 free(line->string);
644 free(line);
645 line = NULL;
646 return;
647 }
648 lines = 0;
1 office 649 }
41 office 650  
651 free(line->string);
652 free(line);
653 line = NULL;
1 office 654 }
655  
41 office 656 if(line != NULL) {
657 free(line->string);
658 free(line);
659 line = NULL;
660 }
661  
33 office 662 if(PROGRAM_VERBOSE) {
24 office 663 fprintf(stdout, "\n");
664 }
1 office 665  
22 office 666 #if defined ___AsyncIO___
667 CloseAsync(tp);
668 CloseAsync(fp);
669 #else
1 office 670 fclose(tp);
671 fclose(fp);
22 office 672 #endif
1 office 673 }
674  
675 /*
676 *
677 * Merges temporary files "tmpNames" into a database "dbFile".
678 */
38 office 679 void MergeTemporaryFiles(char *dbFile, VECTOR *tmpNames, int lines) {
22 office 680 #if defined ___AsyncIO___
681 struct AsyncFile *fp;
682 struct AsyncFile **tp;
683 #else
1 office 684 FILE *fp;
685 FILE **tp;
22 office 686 #endif
33 office 687 int i;
688 int j;
38 office 689 dbLine *tmp;
26 office 690 char *rem;
14 office 691 char *min;
1 office 692 int count;
693  
22 office 694 #if defined ___AsyncIO___
23 office 695 if((fp = OpenAsync(dbFile, MODE_WRITE, ASYNC_BUF)) == NULL) {
22 office 696 #else
23 office 697 if((fp = fopen(dbFile, "w")) == NULL) {
22 office 698 #endif
33 office 699 fprintf(stderr, "Could not open file '%s' for writing.\n", dbFile);
1 office 700 return;
701 }
702  
703 // Allocate as many file pointers as temporary files.
38 office 704 if((tp = malloc(tmpNames->length * sizeof(*tp))) == NULL) {
26 office 705 fprintf(stderr, "Memory allocation failure.\n");
706 #if defined ___AsyncIO___
707 CloseAsync(fp);
708 #else
709 fclose(fp);
710 #endif
711 return;
712 }
1 office 713  
714 // Open all temporary files for reading.
38 office 715 for(i = 0; i < tmpNames->length; ++i) {
22 office 716 #if defined ___AsyncIO___
38 office 717 if((tp[i] = OpenAsync(tmpNames->array[i], MODE_READ, ASYNC_BUF)) == NULL) {
22 office 718 #else
38 office 719 if((tp[i] = fopen(tmpNames->array[i], "r")) == NULL) {
22 office 720 #endif
39 office 721 fprintf(stderr, "Could not open file '%s' for reading.\n", (char *)tmpNames->array[i]);
1 office 722 // Close all temporary files.
24 office 723 while(--i > -1) {
22 office 724 #if defined ___AsyncIO___
725 CloseAsync(tp[i]);
726 #else
1 office 727 fclose(tp[i]);
22 office 728 #endif
1 office 729 }
24 office 730 #if defined ___AsyncIO___
731 CloseAsync(fp);
732 #else
733 fclose(fp);
734 #endif
1 office 735 return;
736 }
737 }
738  
33 office 739 if(PROGRAM_VERBOSE) {
24 office 740 fprintf(stdout, "Merging all files...\r");
1 office 741 }
742  
26 office 743 rem = NULL;
1 office 744 count = lines;
14 office 745 j = 0;
33 office 746 while(PROGRAM_RUN && --count > -1) {
1 office 747 #if defined ___AmigaOS___
748 // Check if CTRL+C was pressed and abort the program.
749 if(SetSignal(0L, SIGBREAKF_CTRL_C) & SIGBREAKF_CTRL_C) {
33 office 750 PROGRAM_RUN = FALSE;
23 office 751 continue;
1 office 752 }
753 #endif
754 // Find the smallest line in all temporary files.
33 office 755 if(PROGRAM_VERBOSE) {
24 office 756 fprintf(stdout, "Merging all files: %d%%.\r", 100 - (int)(((float)count / lines) * 100.0));
1 office 757 }
758  
14 office 759 min = NULL;
38 office 760 for(i = 0; i < tmpNames->length; ++i) {
26 office 761 tmp = PeekLine(tp[i]);
1 office 762 if(tmp == NULL) {
763 continue;
764 }
26 office 765 #if defined ___AmigaOS___
38 office 766 if(min == NULL || StrnCmp(locale, tmp->string, min, -1, SC_ASCII) < 0) {
26 office 767 #else
38 office 768 if(min == NULL || strcmp(tmp->string, min) < 0) {
26 office 769 #endif
14 office 770 if(min != NULL) {
2 office 771 // Free previous instance.
14 office 772 free(min);
38 office 773 min = NULL;
2 office 774 }
38 office 775 if((min = malloc((strlen(tmp->string) + 1) * sizeof(*min))) == NULL) {
33 office 776 fprintf(stderr, "Memory allocation failure.\n");
38 office 777  
778 free(tmp->string);
26 office 779 free(tmp);
38 office 780 tmp = NULL;
781  
26 office 782 if(min != NULL) {
783 free(min);
38 office 784 min = NULL;
26 office 785 }
786 if(rem != NULL) {
787 free(rem);
38 office 788 rem = NULL;
26 office 789 }
790 #if defined ___AsyncIO___
791 CloseAsync(fp);
792 #else
793 fclose(fp);
794 #endif
795 return;
796 }
38 office 797 sprintf(min, "%s", tmp->string);
1 office 798 // Remember the index of the file where the smallest entry has been found.
14 office 799 j = i;
1 office 800 }
38 office 801 free(tmp->string);
1 office 802 free(tmp);
38 office 803 tmp = NULL;
1 office 804 }
805  
806 // Forward the file where the smallest line was found.
23 office 807 SkipLine(tp[j]);
1 office 808  
809 // Write the smallest line.
14 office 810 if(min != NULL) {
26 office 811 // If current minimum line is identical to previous minimum line then skip to remove duplicates.
812 if(rem != NULL) {
813 #if defined ___AmigaOS___
814 if(StrnCmp(locale, min, rem, -1, SC_ASCII) == 0) {
815 #else
28 office 816 if(strcmp(min, rem) == 0) {
26 office 817 #endif
818 free(min);
38 office 819 min = NULL;
26 office 820 continue;
821 }
822 }
823  
22 office 824 #if defined ___AsyncIO___
26 office 825 WriteAsync(fp, min, (LONG)strlen(min));
826 WriteAsync(fp, "\n", 1);
22 office 827 #else
14 office 828 fprintf(fp, "%s\n", min);
22 office 829 #endif
26 office 830  
831 if(rem != NULL) {
832 free(rem);
38 office 833 rem = NULL;
26 office 834 }
835  
836 if((rem = malloc((strlen(min) + 1) * sizeof(*rem))) == NULL) {
837 fprintf(stderr, "Memory allocation failure.\n");
38 office 838  
26 office 839 free(min);
38 office 840 min = NULL;
841  
26 office 842 #if defined ___AsyncIO___
843 CloseAsync(fp);
844 #else
845 fclose(fp);
846 #endif
847 return;
848 }
849  
39 office 850 // Remember the last minimal line.
26 office 851 sprintf(rem, "%s", min);
38 office 852  
14 office 853 free(min);
38 office 854 min = NULL;
1 office 855 }
856 }
857  
26 office 858 if(rem != NULL) {
859 free(rem);
38 office 860 rem = NULL;
26 office 861 }
862  
1 office 863 // Write out any remaining contents from the temporary files.
38 office 864 for(i = 0; PROGRAM_RUN && i < tmpNames->length; ++i) {
24 office 865 #if defined ___AmigaOS___
866 // Check if CTRL+C was pressed and abort the program.
867 if(SetSignal(0L, SIGBREAKF_CTRL_C) & SIGBREAKF_CTRL_C) {
33 office 868 PROGRAM_RUN = FALSE;
24 office 869 continue;
870 }
871 #endif
23 office 872 tmp = ReadLine(tp[i]);
1 office 873 if(tmp == NULL) {
874 continue;
875 }
22 office 876 #if defined ___AsyncIO___
38 office 877 WriteAsync(fp, tmp->string, (LONG)strlen(tmp->string));
26 office 878 WriteAsync(fp, "\n", 1);
22 office 879 #else
38 office 880 fprintf(fp, "%s\n", tmp->string);
22 office 881 #endif
38 office 882 free(tmp->string);
14 office 883 free(tmp);
38 office 884 tmp = NULL;
1 office 885 }
886  
24 office 887 // Close all temporary files.
38 office 888 for(i = 0; i < tmpNames->length; ++i) {
22 office 889 #if defined ___AsyncIO___
890 CloseAsync(tp[i]);
891 #else
1 office 892 fclose(tp[i]);
22 office 893 #endif
1 office 894 }
895  
22 office 896 #if defined ___AsyncIO___
897 CloseAsync(fp);
898 #else
1 office 899 fclose(fp);
22 office 900 #endif
33 office 901  
902 if(PROGRAM_VERBOSE) {
903 fprintf(stdout, "\n");
904 }
1 office 905 }
906  
907 /*
26 office 908 *
909 * Filter the paths inside the database with provided paths.
910 */
38 office 911 void FilterDatabasePaths(char *dbFile, char *tmpName, VECTOR *paths) {
26 office 912 #if defined ___AsyncIO___
913 struct AsyncFile *fp;
914 struct AsyncFile *tp;
915 #else
916 FILE *fp;
917 FILE *tp;
918 #endif
38 office 919 dbLine *line;
920 dbEntry *entry;
33 office 921 int lines;
26 office 922 int i;
24 office 923  
26 office 924 // Open database file for reading.
925 #if defined ___AsyncIO___
926 if((fp = OpenAsync(dbFile, MODE_READ, ASYNC_BUF)) == NULL) {
927 #else
928 if((fp = fopen(dbFile, "r")) == NULL) {
929 #endif
33 office 930 fprintf(stderr, "Could not open file '%s' for reading.\n", dbFile);
26 office 931 return;
24 office 932 }
933  
26 office 934 // Open temporary file for writing.
935 #if defined ___AsyncIO___
936 if((tp = OpenAsync(tmpName, MODE_WRITE, ASYNC_BUF)) == NULL) {
937 #else
938 if((tp = fopen(tmpName, "w")) == NULL) {
939 #endif
53 office 940 fprintf(stderr, "Could not open file '%s' for writing.\n", tmpName);
24 office 941  
26 office 942 // Close database file.
943 #if defined ___AsyncIO___
944 CloseAsync(fp);
945 #else
946 fclose(fp);
947 #endif
948  
949 return;
24 office 950 }
26 office 951  
33 office 952 if(PROGRAM_VERBOSE) {
31 office 953 fprintf(stdout, "Removing lines...\r");
954 }
955  
956 lines = 0;
33 office 957 while(PROGRAM_RUN && (line = ReadLine(fp)) != NULL) {
26 office 958 #if defined ___AmigaOS___
959 // Check if CTRL+C was pressed and abort the program.
960 if(SetSignal(0L, SIGBREAKF_CTRL_C) & SIGBREAKF_CTRL_C) {
33 office 961 PROGRAM_RUN = FALSE;
26 office 962 continue;
963 }
964 #endif
38 office 965  
31 office 966 if((entry = CreateDatabaseEntry(line)) == NULL) {
26 office 967 fprintf(stderr, "Unable to create database entry.\n");
38 office 968 free(line->string);
31 office 969 free(line);
38 office 970 line = NULL;
26 office 971 continue;
972 }
31 office 973  
38 office 974 for(i = 0; i < paths->length; ++i) {
975 if(PathCompare(entry->path, paths->array[i]) == TRUE) {
31 office 976 ++lines;
33 office 977 if(PROGRAM_VERBOSE) {
978 fprintf(stdout, "Removing lines: %d.\r", lines);
31 office 979 }
26 office 980 continue;
981 }
982 #if defined ___AsyncIO___
38 office 983 WriteAsync(tp, line->string, (LONG)strlen(line->string));
26 office 984 WriteAsync(tp, "\n", 1);
985 #else
38 office 986 fprintf(tp, "%s\n", line->string);
26 office 987 #endif
988 break;
989 }
990  
38 office 991 // Free up database entry.
31 office 992 free(entry->name);
993 free(entry->path);
26 office 994 free(entry);
38 office 995 entry = NULL;
31 office 996  
38 office 997 // Free up line.
998 free(line->string);
26 office 999 free(line);
38 office 1000 line = NULL;
26 office 1001 }
1002  
1003 #if defined ___AsyncIO___
1004 CloseAsync(fp);
1005 CloseAsync(tp);
1006 #else
1007 fclose(fp);
1008 fclose(tp);
1009 #endif
33 office 1010  
1011 if(PROGRAM_VERBOSE) {
1012 fprintf(stdout, "\n");
1013 }
24 office 1014 }
1015  
1016 /*
1017 *
41 office 1018 * Indexes paths and adds to a database file.
1 office 1019 */
38 office 1020 void GatherDatabaseFiles(char *dbFile, VECTOR *paths) {
33 office 1021 dbStats *stats;
38 office 1022 VECTOR *tmpNames;
26 office 1023 int tmpFiles;
1024 int tmpLines;
1 office 1025 int i;
41 office 1026 int line;
1027 int size;
1 office 1028  
26 office 1029 // Generate the database file from the supplied paths.
38 office 1030 if((stats = CollectFiles(dbFile, paths)) == NULL) {
26 office 1031 fprintf(stderr, "Collecting files failed.\n");
1032 return;
1033 }
1 office 1034  
42 office 1035 // The size and amount of lines are not necessarily what has been gathered now.
41 office 1036 size = GetFileSize(dbFile);
1037 line = CountFileLines(dbFile);
1038  
33 office 1039 // Calculate the total number of temporary files required.
41 office 1040 tmpFiles = size / maxmem;
1041  
1042 /* In case no temporary files are required,
1043 * just sort the database and terminate.
1044 */
1045 if(tmpFiles < 2) {
1046 SortDatabase(dbFile, line);
1047 return;
1048 }
1049  
1050 // Calculate the number of lines per temporary file.
1051 tmpLines = ceil(((double)line) / ((double)tmpFiles));
1052  
1053 // Create temporary files.
1054 if((tmpNames = CreateTemporaryFiles(tmpFiles)) == NULL) {
1055 fprintf(stderr, "Unable to create temporary files.\n");
1056 return;
1057 }
1058  
1059 // Write "tmpLines" to temporary files in "tmpNames" from "dbFile".
1060 WriteTemporaryFiles(dbFile, tmpNames, tmpLines, line);
1061  
1062 // Sort the temporary files.
1063 for(i = 0; i < tmpNames->length; ++i) {
1064 SortDatabase(tmpNames->array[i], tmpLines);
1065 }
1066  
1067 // Merge all the temporary files to the database file.
1068 MergeTemporaryFiles(dbFile, tmpNames, line);
1069  
1070 // Remove all temporary files.
1071 RemoveFiles(tmpNames);
1072  
1073 // Free temporary file names.
1074 free(tmpNames);
1075 tmpNames = NULL;
1076  
1077 // Free statistics.
1078 free(stats);
1079 stats = NULL;
1080 }
1081  
1082 /*
1083 *
1084 * Indexes paths and creates a daabase file.
1085 */
1086 void CreateDatabaseFiles(char *dbFile, VECTOR *paths) {
1087 dbStats *stats;
1088 VECTOR *tmpNames;
1089 int tmpFiles;
1090 int tmpLines;
1091 int i;
1092  
1093 // Generate the database file from the supplied paths.
1094 if((stats = CollectFiles(dbFile, paths)) == NULL) {
1095 fprintf(stderr, "Collecting files failed.\n");
1096 return;
1097 }
1098  
1099 // Calculate the total number of temporary files required.
33 office 1100 tmpFiles = stats->size / maxmem;
1 office 1101  
26 office 1102 /* In case no temporary files are required,
1103 * just sort the database and terminate.
1104 */
33 office 1105 if(tmpFiles < 2) {
1106 SortDatabase(dbFile, stats->lines);
1 office 1107 return;
1108 }
1109  
33 office 1110 // Calculate the number of lines per temporary file.
38 office 1111 tmpLines = ceil(((double)stats->lines) / ((double)tmpFiles));
1 office 1112  
1113 // Create temporary files.
23 office 1114 if((tmpNames = CreateTemporaryFiles(tmpFiles)) == NULL) {
1 office 1115 fprintf(stderr, "Unable to create temporary files.\n");
1116 return;
1117 }
1118  
24 office 1119 // Write "tmpLines" to temporary files in "tmpNames" from "dbFile".
38 office 1120 WriteTemporaryFiles(dbFile, tmpNames, tmpLines, stats->lines);
1 office 1121  
1122 // Sort the temporary files.
38 office 1123 for(i = 0; i < tmpNames->length; ++i) {
1124 SortDatabase(tmpNames->array[i], tmpLines);
1 office 1125 }
1126  
24 office 1127 // Merge all the temporary files to the database file.
38 office 1128 MergeTemporaryFiles(dbFile, tmpNames, stats->lines);
24 office 1129  
1130 // Remove all temporary files.
39 office 1131 RemoveFiles(tmpNames);
26 office 1132  
31 office 1133 // Free temporary file names.
26 office 1134 free(tmpNames);
38 office 1135 tmpNames = NULL;
33 office 1136  
1137 // Free statistics.
1138 free(stats);
38 office 1139 stats = NULL;
1 office 1140 }
1141  
38 office 1142 void RemoveDatabaseFiles(char *dbFile, VECTOR *paths) {
26 office 1143 char *tmpName;
1144  
1145 // Create a temporary file to hold the changes.
1146 if((tmpName = CreateTemporaryFile()) == NULL) {
1147 fprintf(stderr, "Unable to create temporary file.\n");
1148 return;
1149 }
1150  
1151 // Filter the database of the provided paths.
38 office 1152 FilterDatabasePaths(dbFile, tmpName, paths);
26 office 1153  
1154 // Overwrite the database file with the filtered paths.
43 office 1155 CopyLines(tmpName, dbFile);
26 office 1156  
1157 // Remove temporary file.
31 office 1158 if(RemoveFile(tmpName) == FALSE) {
1159 fprintf(stderr, "Temporary file could not be removed.\n");
1160 return;
1161 }
26 office 1162 }
1163  
11 office 1164 void usage(char *name) {
1165 fprintf(stdout, "Hunt & Gather - %s, a file index generating tool. \n", name);
19 office 1166 fprintf(stdout, "Version: %s \n", PROGRAM_VERSION);
11 office 1167 fprintf(stdout, " \n");
26 office 1168 fprintf(stdout, "SYNTAX: %s [-q] <-a|-r|-c> <PATH PATH PATH...> \n", name);
1169 fprintf(stdout, " \n");
1170 fprintf(stdout, "Required: \n");
27 office 1171 fprintf(stdout, " -a [PATH...] Add files. \n");
1172 fprintf(stdout, " -c [PATH...] Create from scratch. \n");
26 office 1173 fprintf(stdout, " -r [PATH...] Remove files. \n");
1174 fprintf(stdout, " \n");
1175 fprintf(stdout, "Optional: \n");
1176 fprintf(stdout, " -d [FIILE] Where to store the database. \n");
1177 fprintf(stdout, " -m BYTES Memory to use (default: %d). \n", maxmem);
11 office 1178 fprintf(stdout, " -q Do not print out any messages. \n");
26 office 1179 fprintf(stdout, " \n");
11 office 1180 fprintf(stdout, "DATABASE is a path to where the indexed results will be \n");
1181 fprintf(stdout, "stored for searching with the Hunt tool. \n");
1182 fprintf(stdout, " \n");
1183 fprintf(stdout, "(c) 2021 Wizardry and Steamworks, MIT. \n");
1184 }
1185  
1 office 1186 /*
1187 *
1188 * Main entry point.
1189 */
1190 int main(int argc, char **argv) {
1191 int option;
33 office 1192 int i;
2 office 1193 char *dbFile;
19 office 1194 char *path;
38 office 1195 VECTOR *paths;
33 office 1196 OPERATION operation = NONE;
1 office 1197  
1198 // Bind handler to SIGINT.
26 office 1199 #if !defined ___AmigaOS___
1 office 1200 signal(SIGINT, SignalHandler);
26 office 1201 #endif
1 office 1202  
2 office 1203 dbFile = DEFAULT_DATABASE_FILE;
26 office 1204 while((option = getopt(argc, argv, "hqdm:arc")) != -1) {
1 office 1205 switch(option) {
26 office 1206 case 'a':
1207 operation = GATHER;
1208 break;
1209 case 'r':
1210 operation = REMOVE;
1211 break;
1212 case 'c':
1213 operation = CREATE;
1214 break;
1215 case 'm':
1216 maxmem = strtoul(optarg, NULL, 10);
1217 break;
2 office 1218 case 'd':
1219 dbFile = optarg;
1220 break;
1 office 1221 case 'q':
33 office 1222 PROGRAM_VERBOSE = FALSE;
1 office 1223 break;
1224 case 'h':
11 office 1225 usage(argv[0]);
2 office 1226 return 0;
1 office 1227 case '?':
1228 fprintf(stderr, "Invalid option %ct.\n", optopt);
37 office 1229 return 5;
1 office 1230 }
1231 }
1232  
26 office 1233 if(operation == NONE) {
1234 usage(argv[0]);
37 office 1235 return 5;
26 office 1236 }
10 office 1237  
1238 if(optind >= argc) {
11 office 1239 usage(argv[0]);
37 office 1240 return 5;
1 office 1241 }
1242  
38 office 1243 // Build the path vector.
1244 if((paths = malloc(1 * sizeof(*paths))) == NULL) {
1245 fprintf(stderr, "Memory allocation failure.\n");
1246 return 20;
1247 }
1248  
26 office 1249 // Go through all supplied arguments and add paths to search.
38 office 1250 if((paths->array = malloc((argc - optind) * sizeof(*paths))) == NULL) {
26 office 1251 fprintf(stderr, "Memory allocation failure.\n");
37 office 1252 return 20;
1 office 1253 }
31 office 1254  
38 office 1255 for(i = optind, paths->length = 0; i < argc; ++i) {
28 office 1256 if((path = PathToAbsolute(argv[i])) == NULL) {
26 office 1257 fprintf(stderr, "Absolute path for '%s' failed to resolve.\n", argv[optind]);
1258 continue;
1259 }
1 office 1260  
33 office 1261 switch(GetFsType(path)) {
1262 case UNKNOWN:
1263 case REGULAR:
1264 fprintf(stderr, "Path '%s' is not a directory.\n", path);
1265 free(path);
38 office 1266 path = NULL;
33 office 1267 continue;
1268 case DIRECTORY:
1269 break;
28 office 1270 }
1271  
33 office 1272 if(PROGRAM_VERBOSE) {
26 office 1273 fprintf(stdout, "Will process path: '%s'\n", path);
1274 }
1275  
1276 // Add the path to the array of paths.
38 office 1277 if((paths->array[paths->length] = malloc((strlen(path) + 1) * sizeof(*paths->array[paths->length]))) == NULL) {
26 office 1278 fprintf(stderr, "Memory allocation failure.");
37 office 1279 return 20;
26 office 1280 }
28 office 1281  
38 office 1282 sprintf(paths->array[paths->length], "%s", path);
1283 ++paths->length;
28 office 1284  
26 office 1285 free(path);
38 office 1286 path = NULL;
28 office 1287  
26 office 1288 }
1289  
38 office 1290 if(paths->length == 0) {
31 office 1291 fprintf(stderr, "No valid paths are available.\n");
38 office 1292 free(paths->array);
31 office 1293 free(paths);
38 office 1294 paths = NULL;
37 office 1295 return 5;
31 office 1296 }
1297  
33 office 1298 if(PROGRAM_VERBOSE) {
26 office 1299 fprintf(stdout, "Gathering to: '%s'\n", dbFile);
2 office 1300 }
1301  
26 office 1302 #if defined ___AmigaOS___
1303 locale = OpenLocale(NULL);
1304 #endif
1305  
1306 switch(operation) {
1307 case CREATE:
33 office 1308 if(PROGRAM_VERBOSE) {
26 office 1309 fprintf(stdout, "Removing '%s' and creating a new database.\n", dbFile);
1310 }
37 office 1311 RemoveFile(dbFile);
41 office 1312 if(PROGRAM_VERBOSE) {
1313 fprintf(stdout, "Gathering files to database...\n");
1314 }
1315 CreateDatabaseFiles(dbFile, paths);
1316 break;
26 office 1317 case GATHER:
33 office 1318 if(PROGRAM_VERBOSE) {
26 office 1319 fprintf(stdout, "Gathering files to database...\n");
1320 }
38 office 1321 GatherDatabaseFiles(dbFile, paths);
26 office 1322 break;
1323 case REMOVE:
33 office 1324 if(PROGRAM_VERBOSE) {
26 office 1325 fprintf(stdout, "Removing files from database...\n");
1326 }
38 office 1327 RemoveDatabaseFiles(dbFile, paths);
26 office 1328 break;
1329 default:
31 office 1330 fprintf(stderr, "Unknown operation.\n");
37 office 1331 #if defined ___AmigaOS___
1332 CloseLocale(locale);
1333 #endif
1334  
41 office 1335 free(paths->array);
37 office 1336 free(paths);
38 office 1337 paths = NULL;
37 office 1338 return 5;
26 office 1339 }
1 office 1340  
26 office 1341 #if defined ___AmigaOS___
1342 CloseLocale(locale);
1343 #endif
19 office 1344  
41 office 1345 if(paths != NULL) {
1346 free(paths->array);
1347 free(paths);
1348 paths = NULL;
1349 }
1350  
26 office 1351 return 0;
1 office 1352 }