HuntnGather – Blame information for rev 43

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  
31 office 33 #include "StringStack.h"
33 office 34 #include "/shared/utilities.h"
31 office 35  
26 office 36 #define PROGRAM_VERSION "1.7.4"
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
284 stringStack *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.
38 office 320 stack = stringStackCreate((unsigned int)paths->length);
321 for(i = 0; PROGRAM_RUN && i < paths->length; ++i) {
322 stringStackPush(stack, paths->array[i]);
26 office 323 }
324  
33 office 325 while(PROGRAM_RUN && !stringStackIsEmpty(stack)) {
1 office 326 #if defined ___AmigaOS___
327 // Check if CTRL+C was pressed and abort the program.
328 if(SetSignal(0L, SIGBREAKF_CTRL_C) & SIGBREAKF_CTRL_C) {
33 office 329 PROGRAM_RUN = FALSE;
23 office 330 continue;
1 office 331 }
332 #endif
26 office 333 if((path = stringStackPop(stack)) == NULL) {
334 break;
335 }
1 office 336  
28 office 337 #if defined ___AmigaOS___
33 office 338 if((lock = Lock(path, ACCESS_READ)) == NULL) {
28 office 339 fprintf(stderr, "Could not lock path '%s' for reading.\n", path);
340 free(path);
38 office 341 path = NULL;
28 office 342 continue;
26 office 343 }
24 office 344  
33 office 345 if((FIB = AllocDosObject(DOS_FIB, NULL)) == NULL) {
346 fprintf(stderr, "File information block for path '%s' could not be allocated.\n", path);
347 UnLock(lock);
28 office 348 free(path);
38 office 349 path = NULL;
28 office 350 continue;
351 }
352  
33 office 353 if(Examine(lock, FIB) == FALSE) {
28 office 354 fprintf(stderr, "Path '%s' could not be examined.\n", path);
33 office 355 FreeDosObject(DOS_FIB, FIB);
38 office 356 FIB = NULL;
33 office 357 UnLock(lock);
28 office 358 free(path);
38 office 359 path = NULL;
28 office 360 continue;
361 }
362  
33 office 363 while(PROGRAM_RUN && ExNext(lock, FIB)) {
26 office 364 // Check if CTRL+C was pressed and abort the program.
365 if(SetSignal(0L, SIGBREAKF_CTRL_C) & SIGBREAKF_CTRL_C) {
33 office 366 PROGRAM_RUN = FALSE;
31 office 367 continue;
26 office 368 }
28 office 369 #else
370  
371 if((dir = opendir(path)) == NULL) {
33 office 372 fprintf(stderr, "Directory '%s' could not be opened.\n", path);
28 office 373 free(path);
38 office 374 path = NULL;
28 office 375 continue;
376 }
377  
33 office 378 while(PROGRAM_RUN && (entry = readdir(dir)) != NULL) {
26 office 379 #endif
380 switch(path[strlen(path) - 1]) {
381 case '/':
382 case ':': // This is a drive path.
28 office 383 #if defined ___AmigaOS___
33 office 384 if((sub = malloc(strlen(path) + strlen(FIB->fib_FileName) + 1)) == NULL) {
28 office 385 #else
30 office 386 if((sub = malloc(strlen(path) + strlen(entry->d_name) + 1)) == NULL) {
28 office 387 #endif
26 office 388 fprintf(stderr, "Memory allocation failure.\n");
28 office 389 #if defined ___AmigaOS___
33 office 390 FreeDosObject(DOS_FIB, FIB);
38 office 391 FIB = NULL;
33 office 392 UnLock(lock);
28 office 393 #else
26 office 394 closedir(dir);
28 office 395 #endif
38 office 396  
26 office 397 free(path);
38 office 398 path = NULL;
399  
26 office 400 stringStackDestroy(stack);
401 #if defined ___AsyncIO___
402 CloseAsync(fp);
403 #else
404 fclose(fp);
405 #endif
406 return NULL;
407 }
28 office 408 #if defined ___AmigaOS___
33 office 409 sprintf(sub, "%s%s", path, FIB->fib_FileName);
28 office 410 #else
411 sprintf(sub, "%s%s", path, entry->d_name);
412 #endif
26 office 413 break;
414 default:
28 office 415 #if defined ___AmigaOS___
33 office 416 if((sub = malloc(strlen(path) + strlen(FIB->fib_FileName) + 1 + 1)) == NULL) {
28 office 417 #else
30 office 418 if((sub = malloc(strlen(path) + strlen(entry->d_name) + 1 + 1)) == NULL) {
28 office 419 #endif
26 office 420 fprintf(stderr, "Memory allocation failure.\n");
28 office 421 #if defined ___AmigaOS___
33 office 422 FreeDosObject(DOS_FIB, FIB);
38 office 423 FIB = NULL;
33 office 424 UnLock(lock);
28 office 425 #else
26 office 426 closedir(dir);
28 office 427 #endif
38 office 428  
26 office 429 free(path);
38 office 430 path = NULL;
431  
26 office 432 stringStackDestroy(stack);
433 #if defined ___AsyncIO___
434 CloseAsync(fp);
435 #else
436 fclose(fp);
437 #endif
438 return NULL;
439 }
28 office 440 #if defined ___AmigaOS___
33 office 441 sprintf(sub, "%s/%s", path, FIB->fib_FileName);
28 office 442 #else
443 sprintf(sub, "%s/%s", path, entry->d_name);
444 #endif
26 office 445 break;
446 }
28 office 447  
33 office 448 switch(GetFsType(sub)) {
449 case UNKNOWN:
450 free(sub);
38 office 451 sub = NULL;
33 office 452 continue;
453 case REGULAR:
454 ++stats->files;
30 office 455  
33 office 456 if(PROGRAM_VERBOSE) {
457 fprintf(stdout,
38 office 458 "Gathered '%d' directories and '%d' files.\r",
26 office 459 stats->dirs,
460 stats->files);
33 office 461 }
462 break;
463 case DIRECTORY:
464 stringStackPush(stack, sub);
26 office 465  
33 office 466 ++stats->dirs;
26 office 467  
33 office 468 if(PROGRAM_VERBOSE) {
469 fprintf(stdout,
38 office 470 "Gathered '%d' directories and '%d' files.\r",
33 office 471 stats->dirs,
472 stats->files);
473 }
28 office 474  
33 office 475 free(sub);
38 office 476 sub = NULL;
33 office 477 continue;
478 }
30 office 479  
26 office 480 #if defined ___NOCASE_FS___
28 office 481 #if defined ___AmigaOS___
33 office 482 StrUpr(FIB->fib_FileName);
28 office 483 #else
33 office 484 StrUpr(entry->d_name);
26 office 485 #endif
28 office 486 #endif
30 office 487  
26 office 488 // Write to database file.
489 #if defined ___AsyncIO___
28 office 490 #if defined ___AmigaOS___
33 office 491 WriteAsync(fp, FIB->fib_FileName, (LONG)strlen(FIB->fib_FileName));
492 stats->size = stats->size + strlen(FIB->fib_FileName);
28 office 493 #else
26 office 494 WriteAsync(fp, entry->d_name, (LONG)strlen(entry->d_name));
33 office 495 stats->size = stats->size + strlen(entry->d_name);
28 office 496 #endif
26 office 497 WriteAsync(fp, "\t", 1);
33 office 498 ++stats->size;
28 office 499 WriteAsync(fp, sub, (LONG)strlen(sub));
33 office 500 stats->size = stats->size + strlen(sub);
26 office 501 WriteAsync(fp, "\n", 1);
33 office 502 ++stats->size;
26 office 503 #else
28 office 504 #if defined ___AmigaOS___
33 office 505 fprintf(fp, "%s\t%s\n", FIB->fib_FileName, sub);
506 stats->size = stats->size + strlen(FIB->fib_FileName) + strlen(sub) + 1 + 1 + 1;
28 office 507 #else
508 fprintf(fp, "%s\t%s\n", entry->d_name, sub);
33 office 509 stats->size = stats->size + strlen(entry->d_name) + strlen(sub) + 1 + 1 + 1;
26 office 510 #endif
28 office 511 #endif
33 office 512  
513 ++stats->lines;
514  
28 office 515 free(sub);
38 office 516 sub = NULL;
26 office 517 }
518  
28 office 519 #if defined ___AmigaOS___
33 office 520 FreeDosObject(DOS_FIB, FIB);
38 office 521 FIB = NULL;
33 office 522 UnLock(lock);
28 office 523 #else
26 office 524 closedir(dir);
28 office 525 #endif
26 office 526 free(path);
38 office 527 path = NULL;
1 office 528 }
529  
33 office 530 if(PROGRAM_VERBOSE) {
1 office 531 fprintf(stdout, "\n");
532 }
533  
26 office 534 stringStackDestroy(stack);
535  
536 #if defined ___AsyncIO___
537 CloseAsync(fp);
538 #else
539 fclose(fp);
540 #endif
541  
542 return stats;
543  
1 office 544 }
545  
546 /*
547 *
548 * Writes lines from the database "dbFile" to temporary filenames "tmpNames".
549 */
38 office 550 void WriteTemporaryFiles(char *dbFile, VECTOR *tmpNames, int tmpLines, int total) {
22 office 551 #if defined ___AsyncIO___
552 struct AsyncFile *fp, *tp;
553 #else
1 office 554 FILE *fp, *tp;
22 office 555 #endif
1 office 556 int lines;
26 office 557 int write;
38 office 558 int files;
41 office 559 dbLine *line = NULL;
1 office 560  
22 office 561 #if defined ___AsyncIO___
562 if((fp = OpenAsync(dbFile, MODE_READ, ASYNC_BUF)) == NULL) {
563 #else
1 office 564 if((fp = fopen(dbFile, "r")) == NULL) {
22 office 565 #endif
33 office 566 fprintf(stderr, "Could not open file '%s' for reading.\n", dbFile);
1 office 567 return;
568 }
569  
38 office 570 files = tmpNames->length;
22 office 571 #if defined ___AsyncIO___
38 office 572 if((tp = OpenAsync(tmpNames->array[--files], MODE_WRITE, ASYNC_BUF)) == NULL) {
22 office 573 #else
38 office 574 if((tp = fopen(tmpNames->array[--files], "w")) == NULL) {
22 office 575 #endif
38 office 576 fprintf(stderr, "Could not open file '%s' for writing.\n", (char *)tmpNames->array[files]);
24 office 577 #if defined ___AsyncIO___
578 CloseAsync(fp);
579 #else
580 fclose(fp);
581 #endif
1 office 582 return;
583 }
584  
33 office 585 if(PROGRAM_VERBOSE) {
24 office 586 fprintf(stdout, "Writing to temporary files...\r");
1 office 587 }
588  
26 office 589 write = 0;
1 office 590 lines = 0;
41 office 591  
592 while(PROGRAM_RUN && (line = ReadLine(fp)) != NULL) {
1 office 593 #if defined ___AmigaOS___
594 // Check if CTRL+C was pressed and abort the program.
595 if(SetSignal(0L, SIGBREAKF_CTRL_C) & SIGBREAKF_CTRL_C) {
41 office 596 free(line->string);
597 free(line);
598 line = NULL;
599  
33 office 600 PROGRAM_RUN = FALSE;
23 office 601 continue;
1 office 602 }
603 #endif
604  
22 office 605 #if defined ___AsyncIO___
41 office 606 WriteAsync(tp, line->string, (LONG)line->length);
607 WriteAsync(tp, "\n", 1);
22 office 608 #else
41 office 609 fprintf(tp, "%s\n", line->string);
22 office 610 #endif
1 office 611  
41 office 612 ++write;
613  
614 if(PROGRAM_VERBOSE) {
615 fprintf(stdout, "Writing to temporary files: %d%%.\r", (int)(((float)write / total) * 100.0));
616 }
617  
618 // Switch to the next temporary file.
619 if(++lines >= tmpLines) {
620 // If there are no temporary files left then run till the end.
621 if(files - 1 < 0) {
622 free(line->string);
623 free(line);
624 line = NULL;
625 continue;
626 }
627  
628 // Close the previous temporary file and write to the next temporary file.
22 office 629 #if defined ___AsyncIO___
41 office 630 CloseAsync(tp);
631 if((tp = OpenAsync(tmpNames->array[--files], MODE_WRITE, ASYNC_BUF)) == NULL) {
22 office 632 #else
41 office 633 fclose(tp);
634 if((tp = fopen(tmpNames->array[--files], "w")) == NULL) {
22 office 635 #endif
41 office 636 fprintf(stderr, "Could not open '%s' for writing.\n", (char *)tmpNames->array[files]);
22 office 637 #if defined ___AsyncIO___
41 office 638 CloseAsync(fp);
22 office 639 #else
41 office 640 fclose(fp);
22 office 641 #endif
41 office 642 free(line->string);
643 free(line);
644 line = NULL;
645 return;
646 }
647 lines = 0;
1 office 648 }
41 office 649  
650 free(line->string);
651 free(line);
652 line = NULL;
1 office 653 }
654  
41 office 655 if(line != NULL) {
656 free(line->string);
657 free(line);
658 line = NULL;
659 }
660  
33 office 661 if(PROGRAM_VERBOSE) {
24 office 662 fprintf(stdout, "\n");
663 }
1 office 664  
22 office 665 #if defined ___AsyncIO___
666 CloseAsync(tp);
667 CloseAsync(fp);
668 #else
1 office 669 fclose(tp);
670 fclose(fp);
22 office 671 #endif
1 office 672 }
673  
674 /*
675 *
676 * Merges temporary files "tmpNames" into a database "dbFile".
677 */
38 office 678 void MergeTemporaryFiles(char *dbFile, VECTOR *tmpNames, int lines) {
22 office 679 #if defined ___AsyncIO___
680 struct AsyncFile *fp;
681 struct AsyncFile **tp;
682 #else
1 office 683 FILE *fp;
684 FILE **tp;
22 office 685 #endif
33 office 686 int i;
687 int j;
38 office 688 dbLine *tmp;
26 office 689 char *rem;
14 office 690 char *min;
1 office 691 int count;
692  
22 office 693 #if defined ___AsyncIO___
23 office 694 if((fp = OpenAsync(dbFile, MODE_WRITE, ASYNC_BUF)) == NULL) {
22 office 695 #else
23 office 696 if((fp = fopen(dbFile, "w")) == NULL) {
22 office 697 #endif
33 office 698 fprintf(stderr, "Could not open file '%s' for writing.\n", dbFile);
1 office 699 return;
700 }
701  
702 // Allocate as many file pointers as temporary files.
38 office 703 if((tp = malloc(tmpNames->length * sizeof(*tp))) == NULL) {
26 office 704 fprintf(stderr, "Memory allocation failure.\n");
705 #if defined ___AsyncIO___
706 CloseAsync(fp);
707 #else
708 fclose(fp);
709 #endif
710 return;
711 }
1 office 712  
713 // Open all temporary files for reading.
38 office 714 for(i = 0; i < tmpNames->length; ++i) {
22 office 715 #if defined ___AsyncIO___
38 office 716 if((tp[i] = OpenAsync(tmpNames->array[i], MODE_READ, ASYNC_BUF)) == NULL) {
22 office 717 #else
38 office 718 if((tp[i] = fopen(tmpNames->array[i], "r")) == NULL) {
22 office 719 #endif
39 office 720 fprintf(stderr, "Could not open file '%s' for reading.\n", (char *)tmpNames->array[i]);
1 office 721 // Close all temporary files.
24 office 722 while(--i > -1) {
22 office 723 #if defined ___AsyncIO___
724 CloseAsync(tp[i]);
725 #else
1 office 726 fclose(tp[i]);
22 office 727 #endif
1 office 728 }
24 office 729 #if defined ___AsyncIO___
730 CloseAsync(fp);
731 #else
732 fclose(fp);
733 #endif
1 office 734 return;
735 }
736 }
737  
33 office 738 if(PROGRAM_VERBOSE) {
24 office 739 fprintf(stdout, "Merging all files...\r");
1 office 740 }
741  
26 office 742 rem = NULL;
1 office 743 count = lines;
14 office 744 j = 0;
33 office 745 while(PROGRAM_RUN && --count > -1) {
1 office 746 #if defined ___AmigaOS___
747 // Check if CTRL+C was pressed and abort the program.
748 if(SetSignal(0L, SIGBREAKF_CTRL_C) & SIGBREAKF_CTRL_C) {
33 office 749 PROGRAM_RUN = FALSE;
23 office 750 continue;
1 office 751 }
752 #endif
753 // Find the smallest line in all temporary files.
33 office 754 if(PROGRAM_VERBOSE) {
24 office 755 fprintf(stdout, "Merging all files: %d%%.\r", 100 - (int)(((float)count / lines) * 100.0));
1 office 756 }
757  
14 office 758 min = NULL;
38 office 759 for(i = 0; i < tmpNames->length; ++i) {
26 office 760 tmp = PeekLine(tp[i]);
1 office 761 if(tmp == NULL) {
762 continue;
763 }
26 office 764 #if defined ___AmigaOS___
38 office 765 if(min == NULL || StrnCmp(locale, tmp->string, min, -1, SC_ASCII) < 0) {
26 office 766 #else
38 office 767 if(min == NULL || strcmp(tmp->string, min) < 0) {
26 office 768 #endif
14 office 769 if(min != NULL) {
2 office 770 // Free previous instance.
14 office 771 free(min);
38 office 772 min = NULL;
2 office 773 }
38 office 774 if((min = malloc((strlen(tmp->string) + 1) * sizeof(*min))) == NULL) {
33 office 775 fprintf(stderr, "Memory allocation failure.\n");
38 office 776  
777 free(tmp->string);
26 office 778 free(tmp);
38 office 779 tmp = NULL;
780  
26 office 781 if(min != NULL) {
782 free(min);
38 office 783 min = NULL;
26 office 784 }
785 if(rem != NULL) {
786 free(rem);
38 office 787 rem = NULL;
26 office 788 }
789 #if defined ___AsyncIO___
790 CloseAsync(fp);
791 #else
792 fclose(fp);
793 #endif
794 return;
795 }
38 office 796 sprintf(min, "%s", tmp->string);
1 office 797 // Remember the index of the file where the smallest entry has been found.
14 office 798 j = i;
1 office 799 }
38 office 800 free(tmp->string);
1 office 801 free(tmp);
38 office 802 tmp = NULL;
1 office 803 }
804  
805 // Forward the file where the smallest line was found.
23 office 806 SkipLine(tp[j]);
1 office 807  
808 // Write the smallest line.
14 office 809 if(min != NULL) {
26 office 810 // If current minimum line is identical to previous minimum line then skip to remove duplicates.
811 if(rem != NULL) {
812 #if defined ___AmigaOS___
813 if(StrnCmp(locale, min, rem, -1, SC_ASCII) == 0) {
814 #else
28 office 815 if(strcmp(min, rem) == 0) {
26 office 816 #endif
817 free(min);
38 office 818 min = NULL;
26 office 819 continue;
820 }
821 }
822  
22 office 823 #if defined ___AsyncIO___
26 office 824 WriteAsync(fp, min, (LONG)strlen(min));
825 WriteAsync(fp, "\n", 1);
22 office 826 #else
14 office 827 fprintf(fp, "%s\n", min);
22 office 828 #endif
26 office 829  
830 if(rem != NULL) {
831 free(rem);
38 office 832 rem = NULL;
26 office 833 }
834  
835 if((rem = malloc((strlen(min) + 1) * sizeof(*rem))) == NULL) {
836 fprintf(stderr, "Memory allocation failure.\n");
38 office 837  
26 office 838 free(min);
38 office 839 min = NULL;
840  
26 office 841 #if defined ___AsyncIO___
842 CloseAsync(fp);
843 #else
844 fclose(fp);
845 #endif
846 return;
847 }
848  
39 office 849 // Remember the last minimal line.
26 office 850 sprintf(rem, "%s", min);
38 office 851  
14 office 852 free(min);
38 office 853 min = NULL;
1 office 854 }
855 }
856  
26 office 857 if(rem != NULL) {
858 free(rem);
38 office 859 rem = NULL;
26 office 860 }
861  
1 office 862 // Write out any remaining contents from the temporary files.
38 office 863 for(i = 0; PROGRAM_RUN && i < tmpNames->length; ++i) {
24 office 864 #if defined ___AmigaOS___
865 // Check if CTRL+C was pressed and abort the program.
866 if(SetSignal(0L, SIGBREAKF_CTRL_C) & SIGBREAKF_CTRL_C) {
33 office 867 PROGRAM_RUN = FALSE;
24 office 868 continue;
869 }
870 #endif
23 office 871 tmp = ReadLine(tp[i]);
1 office 872 if(tmp == NULL) {
873 continue;
874 }
22 office 875 #if defined ___AsyncIO___
38 office 876 WriteAsync(fp, tmp->string, (LONG)strlen(tmp->string));
26 office 877 WriteAsync(fp, "\n", 1);
22 office 878 #else
38 office 879 fprintf(fp, "%s\n", tmp->string);
22 office 880 #endif
38 office 881 free(tmp->string);
14 office 882 free(tmp);
38 office 883 tmp = NULL;
1 office 884 }
885  
24 office 886 // Close all temporary files.
38 office 887 for(i = 0; i < tmpNames->length; ++i) {
22 office 888 #if defined ___AsyncIO___
889 CloseAsync(tp[i]);
890 #else
1 office 891 fclose(tp[i]);
22 office 892 #endif
1 office 893 }
894  
22 office 895 #if defined ___AsyncIO___
896 CloseAsync(fp);
897 #else
1 office 898 fclose(fp);
22 office 899 #endif
33 office 900  
901 if(PROGRAM_VERBOSE) {
902 fprintf(stdout, "\n");
903 }
1 office 904 }
905  
906 /*
26 office 907 *
908 * Filter the paths inside the database with provided paths.
909 */
38 office 910 void FilterDatabasePaths(char *dbFile, char *tmpName, VECTOR *paths) {
26 office 911 #if defined ___AsyncIO___
912 struct AsyncFile *fp;
913 struct AsyncFile *tp;
914 #else
915 FILE *fp;
916 FILE *tp;
917 #endif
38 office 918 dbLine *line;
919 dbEntry *entry;
33 office 920 int lines;
26 office 921 int i;
24 office 922  
26 office 923 // Open database file for reading.
924 #if defined ___AsyncIO___
925 if((fp = OpenAsync(dbFile, MODE_READ, ASYNC_BUF)) == NULL) {
926 #else
927 if((fp = fopen(dbFile, "r")) == NULL) {
928 #endif
33 office 929 fprintf(stderr, "Could not open file '%s' for reading.\n", dbFile);
26 office 930 return;
24 office 931 }
932  
26 office 933 // Open temporary file for writing.
934 #if defined ___AsyncIO___
935 if((tp = OpenAsync(tmpName, MODE_WRITE, ASYNC_BUF)) == NULL) {
936 #else
937 if((tp = fopen(tmpName, "w")) == NULL) {
938 #endif
33 office 939 fprintf(stderr, "Copuld not open file '%s' for writing.\n", tmpName);
24 office 940  
26 office 941 // Close database file.
942 #if defined ___AsyncIO___
943 CloseAsync(fp);
944 #else
945 fclose(fp);
946 #endif
947  
948 return;
24 office 949 }
26 office 950  
33 office 951 if(PROGRAM_VERBOSE) {
31 office 952 fprintf(stdout, "Removing lines...\r");
953 }
954  
955 lines = 0;
33 office 956 while(PROGRAM_RUN && (line = ReadLine(fp)) != NULL) {
26 office 957 #if defined ___AmigaOS___
958 // Check if CTRL+C was pressed and abort the program.
959 if(SetSignal(0L, SIGBREAKF_CTRL_C) & SIGBREAKF_CTRL_C) {
33 office 960 PROGRAM_RUN = FALSE;
26 office 961 continue;
962 }
963 #endif
38 office 964  
31 office 965 if((entry = CreateDatabaseEntry(line)) == NULL) {
26 office 966 fprintf(stderr, "Unable to create database entry.\n");
38 office 967 free(line->string);
31 office 968 free(line);
38 office 969 line = NULL;
26 office 970 continue;
971 }
31 office 972  
38 office 973 for(i = 0; i < paths->length; ++i) {
974 if(PathCompare(entry->path, paths->array[i]) == TRUE) {
31 office 975 ++lines;
33 office 976 if(PROGRAM_VERBOSE) {
977 fprintf(stdout, "Removing lines: %d.\r", lines);
31 office 978 }
26 office 979 continue;
980 }
981 #if defined ___AsyncIO___
38 office 982 WriteAsync(tp, line->string, (LONG)strlen(line->string));
26 office 983 WriteAsync(tp, "\n", 1);
984 #else
38 office 985 fprintf(tp, "%s\n", line->string);
26 office 986 #endif
987 break;
988 }
989  
38 office 990 // Free up database entry.
31 office 991 free(entry->name);
992 free(entry->path);
26 office 993 free(entry);
38 office 994 entry = NULL;
31 office 995  
38 office 996 // Free up line.
997 free(line->string);
26 office 998 free(line);
38 office 999 line = NULL;
26 office 1000 }
1001  
1002 #if defined ___AsyncIO___
1003 CloseAsync(fp);
1004 CloseAsync(tp);
1005 #else
1006 fclose(fp);
1007 fclose(tp);
1008 #endif
33 office 1009  
1010 if(PROGRAM_VERBOSE) {
1011 fprintf(stdout, "\n");
1012 }
24 office 1013 }
1014  
1015 /*
1016 *
41 office 1017 * Indexes paths and adds to a database file.
1 office 1018 */
38 office 1019 void GatherDatabaseFiles(char *dbFile, VECTOR *paths) {
33 office 1020 dbStats *stats;
38 office 1021 VECTOR *tmpNames;
26 office 1022 int tmpFiles;
1023 int tmpLines;
1 office 1024 int i;
41 office 1025 int line;
1026 int size;
1 office 1027  
26 office 1028 // Generate the database file from the supplied paths.
38 office 1029 if((stats = CollectFiles(dbFile, paths)) == NULL) {
26 office 1030 fprintf(stderr, "Collecting files failed.\n");
1031 return;
1032 }
1 office 1033  
42 office 1034 // The size and amount of lines are not necessarily what has been gathered now.
41 office 1035 size = GetFileSize(dbFile);
1036 line = CountFileLines(dbFile);
1037  
33 office 1038 // Calculate the total number of temporary files required.
41 office 1039 tmpFiles = size / maxmem;
1040  
1041 /* In case no temporary files are required,
1042 * just sort the database and terminate.
1043 */
1044 if(tmpFiles < 2) {
1045 SortDatabase(dbFile, line);
1046 return;
1047 }
1048  
1049 // Calculate the number of lines per temporary file.
1050 tmpLines = ceil(((double)line) / ((double)tmpFiles));
1051  
1052 // Create temporary files.
1053 if((tmpNames = CreateTemporaryFiles(tmpFiles)) == NULL) {
1054 fprintf(stderr, "Unable to create temporary files.\n");
1055 return;
1056 }
1057  
1058 // Write "tmpLines" to temporary files in "tmpNames" from "dbFile".
1059 WriteTemporaryFiles(dbFile, tmpNames, tmpLines, line);
1060  
1061 // Sort the temporary files.
1062 for(i = 0; i < tmpNames->length; ++i) {
1063 SortDatabase(tmpNames->array[i], tmpLines);
1064 }
1065  
1066 // Merge all the temporary files to the database file.
1067 MergeTemporaryFiles(dbFile, tmpNames, line);
1068  
1069 // Remove all temporary files.
1070 RemoveFiles(tmpNames);
1071  
1072 // Free temporary file names.
1073 free(tmpNames);
1074 tmpNames = NULL;
1075  
1076 // Free statistics.
1077 free(stats);
1078 stats = NULL;
1079 }
1080  
1081 /*
1082 *
1083 * Indexes paths and creates a daabase file.
1084 */
1085 void CreateDatabaseFiles(char *dbFile, VECTOR *paths) {
1086 dbStats *stats;
1087 VECTOR *tmpNames;
1088 int tmpFiles;
1089 int tmpLines;
1090 int i;
1091  
1092 // Generate the database file from the supplied paths.
1093 if((stats = CollectFiles(dbFile, paths)) == NULL) {
1094 fprintf(stderr, "Collecting files failed.\n");
1095 return;
1096 }
1097  
1098 // Calculate the total number of temporary files required.
33 office 1099 tmpFiles = stats->size / maxmem;
1 office 1100  
26 office 1101 /* In case no temporary files are required,
1102 * just sort the database and terminate.
1103 */
33 office 1104 if(tmpFiles < 2) {
1105 SortDatabase(dbFile, stats->lines);
1 office 1106 return;
1107 }
1108  
33 office 1109 // Calculate the number of lines per temporary file.
38 office 1110 tmpLines = ceil(((double)stats->lines) / ((double)tmpFiles));
1 office 1111  
1112 // Create temporary files.
23 office 1113 if((tmpNames = CreateTemporaryFiles(tmpFiles)) == NULL) {
1 office 1114 fprintf(stderr, "Unable to create temporary files.\n");
1115 return;
1116 }
1117  
24 office 1118 // Write "tmpLines" to temporary files in "tmpNames" from "dbFile".
38 office 1119 WriteTemporaryFiles(dbFile, tmpNames, tmpLines, stats->lines);
1 office 1120  
1121 // Sort the temporary files.
38 office 1122 for(i = 0; i < tmpNames->length; ++i) {
1123 SortDatabase(tmpNames->array[i], tmpLines);
1 office 1124 }
1125  
24 office 1126 // Merge all the temporary files to the database file.
38 office 1127 MergeTemporaryFiles(dbFile, tmpNames, stats->lines);
24 office 1128  
1129 // Remove all temporary files.
39 office 1130 RemoveFiles(tmpNames);
26 office 1131  
31 office 1132 // Free temporary file names.
26 office 1133 free(tmpNames);
38 office 1134 tmpNames = NULL;
33 office 1135  
1136 // Free statistics.
1137 free(stats);
38 office 1138 stats = NULL;
1 office 1139 }
1140  
38 office 1141 void RemoveDatabaseFiles(char *dbFile, VECTOR *paths) {
26 office 1142 char *tmpName;
1143  
1144 // Create a temporary file to hold the changes.
1145 if((tmpName = CreateTemporaryFile()) == NULL) {
1146 fprintf(stderr, "Unable to create temporary file.\n");
1147 return;
1148 }
1149  
1150 // Filter the database of the provided paths.
38 office 1151 FilterDatabasePaths(dbFile, tmpName, paths);
26 office 1152  
1153 // Overwrite the database file with the filtered paths.
43 office 1154 CopyLines(tmpName, dbFile);
26 office 1155  
1156 // Remove temporary file.
31 office 1157 if(RemoveFile(tmpName) == FALSE) {
1158 fprintf(stderr, "Temporary file could not be removed.\n");
1159 return;
1160 }
26 office 1161 }
1162  
11 office 1163 void usage(char *name) {
1164 fprintf(stdout, "Hunt & Gather - %s, a file index generating tool. \n", name);
19 office 1165 fprintf(stdout, "Version: %s \n", PROGRAM_VERSION);
11 office 1166 fprintf(stdout, " \n");
26 office 1167 fprintf(stdout, "SYNTAX: %s [-q] <-a|-r|-c> <PATH PATH PATH...> \n", name);
1168 fprintf(stdout, " \n");
1169 fprintf(stdout, "Required: \n");
27 office 1170 fprintf(stdout, " -a [PATH...] Add files. \n");
1171 fprintf(stdout, " -c [PATH...] Create from scratch. \n");
26 office 1172 fprintf(stdout, " -r [PATH...] Remove files. \n");
1173 fprintf(stdout, " \n");
1174 fprintf(stdout, "Optional: \n");
1175 fprintf(stdout, " -d [FIILE] Where to store the database. \n");
1176 fprintf(stdout, " -m BYTES Memory to use (default: %d). \n", maxmem);
11 office 1177 fprintf(stdout, " -q Do not print out any messages. \n");
26 office 1178 fprintf(stdout, " \n");
11 office 1179 fprintf(stdout, "DATABASE is a path to where the indexed results will be \n");
1180 fprintf(stdout, "stored for searching with the Hunt tool. \n");
1181 fprintf(stdout, " \n");
1182 fprintf(stdout, "(c) 2021 Wizardry and Steamworks, MIT. \n");
1183 }
1184  
1 office 1185 /*
1186 *
1187 * Main entry point.
1188 */
1189 int main(int argc, char **argv) {
1190 int option;
33 office 1191 int i;
2 office 1192 char *dbFile;
19 office 1193 char *path;
38 office 1194 VECTOR *paths;
33 office 1195 OPERATION operation = NONE;
1 office 1196  
1197 // Bind handler to SIGINT.
26 office 1198 #if !defined ___AmigaOS___
1 office 1199 signal(SIGINT, SignalHandler);
26 office 1200 #endif
1 office 1201  
2 office 1202 dbFile = DEFAULT_DATABASE_FILE;
26 office 1203 while((option = getopt(argc, argv, "hqdm:arc")) != -1) {
1 office 1204 switch(option) {
26 office 1205 case 'a':
1206 operation = GATHER;
1207 break;
1208 case 'r':
1209 operation = REMOVE;
1210 break;
1211 case 'c':
1212 operation = CREATE;
1213 break;
1214 case 'm':
1215 maxmem = strtoul(optarg, NULL, 10);
1216 break;
2 office 1217 case 'd':
1218 dbFile = optarg;
1219 break;
1 office 1220 case 'q':
33 office 1221 PROGRAM_VERBOSE = FALSE;
1 office 1222 break;
1223 case 'h':
11 office 1224 usage(argv[0]);
2 office 1225 return 0;
1 office 1226 case '?':
1227 fprintf(stderr, "Invalid option %ct.\n", optopt);
37 office 1228 return 5;
1 office 1229 }
1230 }
1231  
26 office 1232 if(operation == NONE) {
1233 usage(argv[0]);
37 office 1234 return 5;
26 office 1235 }
10 office 1236  
1237 if(optind >= argc) {
11 office 1238 usage(argv[0]);
37 office 1239 return 5;
1 office 1240 }
1241  
38 office 1242 // Build the path vector.
1243 if((paths = malloc(1 * sizeof(*paths))) == NULL) {
1244 fprintf(stderr, "Memory allocation failure.\n");
1245 return 20;
1246 }
1247  
26 office 1248 // Go through all supplied arguments and add paths to search.
38 office 1249 if((paths->array = malloc((argc - optind) * sizeof(*paths))) == NULL) {
26 office 1250 fprintf(stderr, "Memory allocation failure.\n");
37 office 1251 return 20;
1 office 1252 }
31 office 1253  
38 office 1254 for(i = optind, paths->length = 0; i < argc; ++i) {
28 office 1255 if((path = PathToAbsolute(argv[i])) == NULL) {
26 office 1256 fprintf(stderr, "Absolute path for '%s' failed to resolve.\n", argv[optind]);
1257 continue;
1258 }
1 office 1259  
33 office 1260 switch(GetFsType(path)) {
1261 case UNKNOWN:
1262 case REGULAR:
1263 fprintf(stderr, "Path '%s' is not a directory.\n", path);
1264 free(path);
38 office 1265 path = NULL;
33 office 1266 continue;
1267 case DIRECTORY:
1268 break;
28 office 1269 }
1270  
33 office 1271 if(PROGRAM_VERBOSE) {
26 office 1272 fprintf(stdout, "Will process path: '%s'\n", path);
1273 }
1274  
1275 // Add the path to the array of paths.
38 office 1276 if((paths->array[paths->length] = malloc((strlen(path) + 1) * sizeof(*paths->array[paths->length]))) == NULL) {
26 office 1277 fprintf(stderr, "Memory allocation failure.");
37 office 1278 return 20;
26 office 1279 }
28 office 1280  
38 office 1281 sprintf(paths->array[paths->length], "%s", path);
1282 ++paths->length;
28 office 1283  
26 office 1284 free(path);
38 office 1285 path = NULL;
28 office 1286  
26 office 1287 }
1288  
38 office 1289 if(paths->length == 0) {
31 office 1290 fprintf(stderr, "No valid paths are available.\n");
38 office 1291 free(paths->array);
31 office 1292 free(paths);
38 office 1293 paths = NULL;
37 office 1294 return 5;
31 office 1295 }
1296  
33 office 1297 if(PROGRAM_VERBOSE) {
26 office 1298 fprintf(stdout, "Gathering to: '%s'\n", dbFile);
2 office 1299 }
1300  
26 office 1301 #if defined ___AmigaOS___
1302 locale = OpenLocale(NULL);
1303 #endif
1304  
1305 switch(operation) {
1306 case CREATE:
33 office 1307 if(PROGRAM_VERBOSE) {
26 office 1308 fprintf(stdout, "Removing '%s' and creating a new database.\n", dbFile);
1309 }
37 office 1310 RemoveFile(dbFile);
41 office 1311 if(PROGRAM_VERBOSE) {
1312 fprintf(stdout, "Gathering files to database...\n");
1313 }
1314 CreateDatabaseFiles(dbFile, paths);
1315 break;
26 office 1316 case GATHER:
33 office 1317 if(PROGRAM_VERBOSE) {
26 office 1318 fprintf(stdout, "Gathering files to database...\n");
1319 }
38 office 1320 GatherDatabaseFiles(dbFile, paths);
26 office 1321 break;
1322 case REMOVE:
33 office 1323 if(PROGRAM_VERBOSE) {
26 office 1324 fprintf(stdout, "Removing files from database...\n");
1325 }
38 office 1326 RemoveDatabaseFiles(dbFile, paths);
26 office 1327 break;
1328 default:
31 office 1329 fprintf(stderr, "Unknown operation.\n");
37 office 1330 #if defined ___AmigaOS___
1331 CloseLocale(locale);
1332 #endif
1333  
41 office 1334 free(paths->array);
37 office 1335 free(paths);
38 office 1336 paths = NULL;
37 office 1337 return 5;
26 office 1338 }
1 office 1339  
26 office 1340 #if defined ___AmigaOS___
1341 CloseLocale(locale);
1342 #endif
19 office 1343  
41 office 1344 if(paths != NULL) {
1345 free(paths->array);
1346 free(paths);
1347 paths = NULL;
1348 }
1349  
26 office 1350 return 0;
1 office 1351 }