HuntnGather – Blame information for rev 38

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 LONG c;
554 #else
1 office 555 FILE *fp, *tp;
556 char c;
22 office 557 #endif
1 office 558 int lines;
26 office 559 int write;
38 office 560 int files;
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;
22 office 592 #if defined ___AsyncIO___
33 office 593 while(PROGRAM_RUN && (c = ReadCharAsync(fp)) != -1) {
22 office 594 #else
33 office 595 while(PROGRAM_RUN && fscanf(fp, "%c", &c) == 1) {
22 office 596 #endif
1 office 597 #if defined ___AmigaOS___
598 // Check if CTRL+C was pressed and abort the program.
599 if(SetSignal(0L, SIGBREAKF_CTRL_C) & SIGBREAKF_CTRL_C) {
33 office 600 PROGRAM_RUN = FALSE;
23 office 601 continue;
1 office 602 }
603 #endif
604 switch(c) {
605 case '\n':
606 // Increment the total written lines.
26 office 607 ++write;
1 office 608  
33 office 609 if(PROGRAM_VERBOSE) {
26 office 610 fprintf(stdout, "Writing to temporary files: %d%%.\r", (int)(((float)write / total) * 100.0));
1 office 611 }
612  
613 // Write the newline character back.
22 office 614 #if defined ___AsyncIO___
615 if(WriteCharAsync(tp, (UBYTE)c) != 1) {
616 #else
1 office 617 if(fprintf(tp, "%c", c) != 1) {
22 office 618 #endif
38 office 619 fprintf(stderr, "Unable to write to '%s'.\n", (char *)tmpNames->array[files]);
22 office 620 #if defined ___AsyncIO___
621 CloseAsync(tp);
622 CloseAsync(fp);
623 #else
19 office 624 fclose(tp);
1 office 625 fclose(fp);
22 office 626 #endif
1 office 627 return;
628 }
629 // Switch to the next temporary file.
630 if(++lines >= tmpLines) {
631 // If there are no temporary files left then run till the end.
38 office 632 if(files - 1 < 0) {
1 office 633 break;
634 }
635  
636 // Close the previous temporary file and write to the next temporary file.
22 office 637 #if defined ___AsyncIO___
638 CloseAsync(tp);
38 office 639 if((tp = OpenAsync(tmpNames->array[--files], MODE_WRITE, ASYNC_BUF)) == NULL) {
22 office 640 #else
1 office 641 fclose(tp);
38 office 642 if((tp = fopen(tmpNames->array[--files], "w")) == NULL) {
22 office 643 #endif
38 office 644 fprintf(stderr, "Could not open '%s' for writing.\n", (char *)tmpNames->array[files]);
22 office 645 #if defined ___AsyncIO___
646 CloseAsync(fp);
647 #else
1 office 648 fclose(fp);
22 office 649 #endif
24 office 650 return;
1 office 651 }
652 lines = 0;
653 break;
654 }
655 break;
656 default:
22 office 657 #if defined ___AsyncIO___
658 if(WriteCharAsync(tp, (UBYTE)c) != 1) {
659 #else
1 office 660 if(fprintf(tp, "%c", c) != 1) {
22 office 661 #endif
38 office 662 fprintf(stderr, "Could not write to file '%s'.\n", (char *)tmpNames->array[files]);
22 office 663 #if defined ___AsyncIO___
664 CloseAsync(tp);
665 CloseAsync(fp);
666 #else
1 office 667 fclose(tp);
668 fclose(fp);
22 office 669 #endif
1 office 670 return;
671 }
672 break;
673 }
674 }
675  
33 office 676 if(PROGRAM_VERBOSE) {
24 office 677 fprintf(stdout, "\n");
678 }
1 office 679  
22 office 680 #if defined ___AsyncIO___
681 CloseAsync(tp);
682 CloseAsync(fp);
683 #else
1 office 684 fclose(tp);
685 fclose(fp);
22 office 686 #endif
1 office 687 }
688  
689 /*
690 *
691 * Merges temporary files "tmpNames" into a database "dbFile".
692 */
38 office 693 void MergeTemporaryFiles(char *dbFile, VECTOR *tmpNames, int lines) {
22 office 694 #if defined ___AsyncIO___
695 struct AsyncFile *fp;
696 struct AsyncFile **tp;
697 #else
1 office 698 FILE *fp;
699 FILE **tp;
22 office 700 #endif
33 office 701 int i;
702 int j;
38 office 703 dbLine *tmp;
26 office 704 char *rem;
14 office 705 char *min;
1 office 706 int count;
707  
22 office 708 #if defined ___AsyncIO___
23 office 709 if((fp = OpenAsync(dbFile, MODE_WRITE, ASYNC_BUF)) == NULL) {
22 office 710 #else
23 office 711 if((fp = fopen(dbFile, "w")) == NULL) {
22 office 712 #endif
33 office 713 fprintf(stderr, "Could not open file '%s' for writing.\n", dbFile);
1 office 714 return;
715 }
716  
717 // Allocate as many file pointers as temporary files.
38 office 718 if((tp = malloc(tmpNames->length * sizeof(*tp))) == NULL) {
26 office 719 fprintf(stderr, "Memory allocation failure.\n");
720 #if defined ___AsyncIO___
721 CloseAsync(fp);
722 #else
723 fclose(fp);
724 #endif
725 return;
726 }
1 office 727  
728 // Open all temporary files for reading.
38 office 729 for(i = 0; i < tmpNames->length; ++i) {
22 office 730 #if defined ___AsyncIO___
38 office 731 if((tp[i] = OpenAsync(tmpNames->array[i], MODE_READ, ASYNC_BUF)) == NULL) {
22 office 732 #else
38 office 733 if((tp[i] = fopen(tmpNames->array[i], "r")) == NULL) {
22 office 734 #endif
38 office 735 fprintf(stderr, "Could not open file '%s' for reading.\n", tmpNames->array[i]);
1 office 736 // Close all temporary files.
24 office 737 while(--i > -1) {
22 office 738 #if defined ___AsyncIO___
739 CloseAsync(tp[i]);
740 #else
1 office 741 fclose(tp[i]);
22 office 742 #endif
1 office 743 }
24 office 744 #if defined ___AsyncIO___
745 CloseAsync(fp);
746 #else
747 fclose(fp);
748 #endif
1 office 749 return;
750 }
751 }
752  
33 office 753 if(PROGRAM_VERBOSE) {
24 office 754 fprintf(stdout, "Merging all files...\r");
1 office 755 }
756  
26 office 757 rem = NULL;
1 office 758 count = lines;
14 office 759 j = 0;
33 office 760 while(PROGRAM_RUN && --count > -1) {
1 office 761 #if defined ___AmigaOS___
762 // Check if CTRL+C was pressed and abort the program.
763 if(SetSignal(0L, SIGBREAKF_CTRL_C) & SIGBREAKF_CTRL_C) {
33 office 764 PROGRAM_RUN = FALSE;
23 office 765 continue;
1 office 766 }
767 #endif
768 // Find the smallest line in all temporary files.
33 office 769 if(PROGRAM_VERBOSE) {
24 office 770 fprintf(stdout, "Merging all files: %d%%.\r", 100 - (int)(((float)count / lines) * 100.0));
1 office 771 }
772  
14 office 773 min = NULL;
38 office 774 for(i = 0; i < tmpNames->length; ++i) {
26 office 775 tmp = PeekLine(tp[i]);
1 office 776 if(tmp == NULL) {
777 continue;
778 }
26 office 779 #if defined ___AmigaOS___
38 office 780 if(min == NULL || StrnCmp(locale, tmp->string, min, -1, SC_ASCII) < 0) {
26 office 781 #else
38 office 782 if(min == NULL || strcmp(tmp->string, min) < 0) {
26 office 783 #endif
14 office 784 if(min != NULL) {
2 office 785 // Free previous instance.
14 office 786 free(min);
38 office 787 min = NULL;
2 office 788 }
38 office 789 if((min = malloc((strlen(tmp->string) + 1) * sizeof(*min))) == NULL) {
33 office 790 fprintf(stderr, "Memory allocation failure.\n");
38 office 791  
792 free(tmp->string);
26 office 793 free(tmp);
38 office 794 tmp = NULL;
795  
26 office 796 if(min != NULL) {
797 free(min);
38 office 798 min = NULL;
26 office 799 }
800 if(rem != NULL) {
801 free(rem);
38 office 802 rem = NULL;
26 office 803 }
804 #if defined ___AsyncIO___
805 CloseAsync(fp);
806 #else
807 fclose(fp);
808 #endif
809 return;
810 }
38 office 811 sprintf(min, "%s", tmp->string);
1 office 812 // Remember the index of the file where the smallest entry has been found.
14 office 813 j = i;
1 office 814 }
38 office 815 free(tmp->string);
1 office 816 free(tmp);
38 office 817 tmp = NULL;
1 office 818 }
819  
820 // Forward the file where the smallest line was found.
23 office 821 SkipLine(tp[j]);
1 office 822  
823 // Write the smallest line.
14 office 824 if(min != NULL) {
26 office 825 // If current minimum line is identical to previous minimum line then skip to remove duplicates.
826 if(rem != NULL) {
827 #if defined ___AmigaOS___
828 if(StrnCmp(locale, min, rem, -1, SC_ASCII) == 0) {
829 #else
28 office 830 if(strcmp(min, rem) == 0) {
26 office 831 #endif
832 free(min);
38 office 833 min = NULL;
26 office 834 continue;
835 }
836 }
837  
22 office 838 #if defined ___AsyncIO___
26 office 839 WriteAsync(fp, min, (LONG)strlen(min));
840 WriteAsync(fp, "\n", 1);
22 office 841 #else
14 office 842 fprintf(fp, "%s\n", min);
22 office 843 #endif
26 office 844  
845 if(rem != NULL) {
846 free(rem);
38 office 847 rem = NULL;
26 office 848 }
849  
850 if((rem = malloc((strlen(min) + 1) * sizeof(*rem))) == NULL) {
851 fprintf(stderr, "Memory allocation failure.\n");
38 office 852  
26 office 853 free(min);
38 office 854 min = NULL;
855  
26 office 856 #if defined ___AsyncIO___
857 CloseAsync(fp);
858 #else
859 fclose(fp);
860 #endif
861 return;
862 }
863  
38 office 864 // Rememb er the last minimal line.
26 office 865 sprintf(rem, "%s", min);
38 office 866  
14 office 867 free(min);
38 office 868 min = NULL;
1 office 869 }
870 }
871  
26 office 872 if(rem != NULL) {
873 free(rem);
38 office 874 rem = NULL;
26 office 875 }
876  
1 office 877 // Write out any remaining contents from the temporary files.
38 office 878 for(i = 0; PROGRAM_RUN && i < tmpNames->length; ++i) {
24 office 879 #if defined ___AmigaOS___
880 // Check if CTRL+C was pressed and abort the program.
881 if(SetSignal(0L, SIGBREAKF_CTRL_C) & SIGBREAKF_CTRL_C) {
33 office 882 PROGRAM_RUN = FALSE;
24 office 883 continue;
884 }
885 #endif
23 office 886 tmp = ReadLine(tp[i]);
1 office 887 if(tmp == NULL) {
888 continue;
889 }
22 office 890 #if defined ___AsyncIO___
38 office 891 WriteAsync(fp, tmp->string, (LONG)strlen(tmp->string));
26 office 892 WriteAsync(fp, "\n", 1);
22 office 893 #else
38 office 894 fprintf(fp, "%s\n", tmp->string);
22 office 895 #endif
38 office 896 free(tmp->string);
14 office 897 free(tmp);
38 office 898 tmp = NULL;
1 office 899 }
900  
24 office 901 // Close all temporary files.
38 office 902 for(i = 0; i < tmpNames->length; ++i) {
22 office 903 #if defined ___AsyncIO___
904 CloseAsync(tp[i]);
905 #else
1 office 906 fclose(tp[i]);
22 office 907 #endif
1 office 908 }
909  
22 office 910 #if defined ___AsyncIO___
911 CloseAsync(fp);
912 #else
1 office 913 fclose(fp);
22 office 914 #endif
33 office 915  
916 if(PROGRAM_VERBOSE) {
917 fprintf(stdout, "\n");
918 }
1 office 919 }
920  
921 /*
26 office 922 *
923 * Filter the paths inside the database with provided paths.
924 */
38 office 925 void FilterDatabasePaths(char *dbFile, char *tmpName, VECTOR *paths) {
26 office 926 #if defined ___AsyncIO___
927 struct AsyncFile *fp;
928 struct AsyncFile *tp;
929 #else
930 FILE *fp;
931 FILE *tp;
932 #endif
38 office 933 dbLine *line;
934 dbEntry *entry;
33 office 935 int lines;
26 office 936 int i;
24 office 937  
26 office 938 // Open database file for reading.
939 #if defined ___AsyncIO___
940 if((fp = OpenAsync(dbFile, MODE_READ, ASYNC_BUF)) == NULL) {
941 #else
942 if((fp = fopen(dbFile, "r")) == NULL) {
943 #endif
33 office 944 fprintf(stderr, "Could not open file '%s' for reading.\n", dbFile);
26 office 945 return;
24 office 946 }
947  
26 office 948 // Open temporary file for writing.
949 #if defined ___AsyncIO___
950 if((tp = OpenAsync(tmpName, MODE_WRITE, ASYNC_BUF)) == NULL) {
951 #else
952 if((tp = fopen(tmpName, "w")) == NULL) {
953 #endif
33 office 954 fprintf(stderr, "Copuld not open file '%s' for writing.\n", tmpName);
24 office 955  
26 office 956 // Close database file.
957 #if defined ___AsyncIO___
958 CloseAsync(fp);
959 #else
960 fclose(fp);
961 #endif
962  
963 return;
24 office 964 }
26 office 965  
33 office 966 if(PROGRAM_VERBOSE) {
31 office 967 fprintf(stdout, "Removing lines...\r");
968 }
969  
970 lines = 0;
33 office 971 while(PROGRAM_RUN && (line = ReadLine(fp)) != NULL) {
26 office 972 #if defined ___AmigaOS___
973 // Check if CTRL+C was pressed and abort the program.
974 if(SetSignal(0L, SIGBREAKF_CTRL_C) & SIGBREAKF_CTRL_C) {
33 office 975 PROGRAM_RUN = FALSE;
26 office 976 continue;
977 }
978 #endif
38 office 979  
31 office 980 if((entry = CreateDatabaseEntry(line)) == NULL) {
26 office 981 fprintf(stderr, "Unable to create database entry.\n");
38 office 982 free(line->string);
31 office 983 free(line);
38 office 984 line = NULL;
26 office 985 continue;
986 }
31 office 987  
38 office 988 for(i = 0; i < paths->length; ++i) {
989 if(PathCompare(entry->path, paths->array[i]) == TRUE) {
31 office 990 ++lines;
33 office 991 if(PROGRAM_VERBOSE) {
992 fprintf(stdout, "Removing lines: %d.\r", lines);
31 office 993 }
26 office 994 continue;
995 }
996 #if defined ___AsyncIO___
38 office 997 WriteAsync(tp, line->string, (LONG)strlen(line->string));
26 office 998 WriteAsync(tp, "\n", 1);
999 #else
38 office 1000 fprintf(tp, "%s\n", line->string);
26 office 1001 #endif
1002 break;
1003 }
1004  
38 office 1005 // Free up database entry.
31 office 1006 free(entry->name);
1007 free(entry->path);
26 office 1008 free(entry);
38 office 1009 entry = NULL;
31 office 1010  
38 office 1011 // Free up line.
1012 free(line->string);
26 office 1013 free(line);
38 office 1014 line = NULL;
26 office 1015 }
1016  
1017 #if defined ___AsyncIO___
1018 CloseAsync(fp);
1019 CloseAsync(tp);
1020 #else
1021 fclose(fp);
1022 fclose(tp);
1023 #endif
33 office 1024  
1025 if(PROGRAM_VERBOSE) {
1026 fprintf(stdout, "\n");
1027 }
24 office 1028 }
1029  
1030 /*
1031 *
1 office 1032 * Indexes a "path" by creating a database "dbFile".
1033 */
38 office 1034 void GatherDatabaseFiles(char *dbFile, VECTOR *paths) {
33 office 1035 dbStats *stats;
38 office 1036 VECTOR *tmpNames;
26 office 1037 int tmpFiles;
1038 int tmpLines;
1 office 1039 int i;
1040  
26 office 1041 // Generate the database file from the supplied paths.
38 office 1042 if((stats = CollectFiles(dbFile, paths)) == NULL) {
26 office 1043 fprintf(stderr, "Collecting files failed.\n");
1044 return;
1045 }
1 office 1046  
33 office 1047 // Calculate the total number of temporary files required.
1048 tmpFiles = stats->size / maxmem;
1 office 1049  
26 office 1050 /* In case no temporary files are required,
1051 * just sort the database and terminate.
1052 */
33 office 1053 if(tmpFiles < 2) {
1054 SortDatabase(dbFile, stats->lines);
1 office 1055 return;
1056 }
1057  
33 office 1058 // Calculate the number of lines per temporary file.
38 office 1059 tmpLines = ceil(((double)stats->lines) / ((double)tmpFiles));
1 office 1060  
1061 // Create temporary files.
23 office 1062 if((tmpNames = CreateTemporaryFiles(tmpFiles)) == NULL) {
1 office 1063 fprintf(stderr, "Unable to create temporary files.\n");
1064 return;
1065 }
1066  
24 office 1067 // Write "tmpLines" to temporary files in "tmpNames" from "dbFile".
38 office 1068 WriteTemporaryFiles(dbFile, tmpNames, tmpLines, stats->lines);
1 office 1069  
1070 // Sort the temporary files.
38 office 1071 for(i = 0; i < tmpNames->length; ++i) {
1072 SortDatabase(tmpNames->array[i], tmpLines);
1 office 1073 }
1074  
24 office 1075 // Merge all the temporary files to the database file.
38 office 1076 MergeTemporaryFiles(dbFile, tmpNames, stats->lines);
24 office 1077  
1078 // Remove all temporary files.
26 office 1079 RemoveFiles(tmpNames, tmpFiles);
1080  
31 office 1081 // Free temporary file names.
26 office 1082 free(tmpNames);
38 office 1083 tmpNames = NULL;
33 office 1084  
1085 // Free statistics.
1086 free(stats);
38 office 1087 stats = NULL;
1 office 1088 }
1089  
38 office 1090 void RemoveDatabaseFiles(char *dbFile, VECTOR *paths) {
26 office 1091 char *tmpName;
1092  
1093 // Create a temporary file to hold the changes.
1094 if((tmpName = CreateTemporaryFile()) == NULL) {
1095 fprintf(stderr, "Unable to create temporary file.\n");
1096 return;
1097 }
1098  
1099 // Filter the database of the provided paths.
38 office 1100 FilterDatabasePaths(dbFile, tmpName, paths);
26 office 1101  
1102 // Overwrite the database file with the filtered paths.
1103 CopyFile(tmpName, dbFile);
1104  
1105 // Remove temporary file.
31 office 1106 if(RemoveFile(tmpName) == FALSE) {
1107 fprintf(stderr, "Temporary file could not be removed.\n");
1108 return;
1109 }
26 office 1110 }
1111  
11 office 1112 void usage(char *name) {
1113 fprintf(stdout, "Hunt & Gather - %s, a file index generating tool. \n", name);
19 office 1114 fprintf(stdout, "Version: %s \n", PROGRAM_VERSION);
11 office 1115 fprintf(stdout, " \n");
26 office 1116 fprintf(stdout, "SYNTAX: %s [-q] <-a|-r|-c> <PATH PATH PATH...> \n", name);
1117 fprintf(stdout, " \n");
1118 fprintf(stdout, "Required: \n");
27 office 1119 fprintf(stdout, " -a [PATH...] Add files. \n");
1120 fprintf(stdout, " -c [PATH...] Create from scratch. \n");
26 office 1121 fprintf(stdout, " -r [PATH...] Remove files. \n");
1122 fprintf(stdout, " \n");
1123 fprintf(stdout, "Optional: \n");
1124 fprintf(stdout, " -d [FIILE] Where to store the database. \n");
1125 fprintf(stdout, " -m BYTES Memory to use (default: %d). \n", maxmem);
11 office 1126 fprintf(stdout, " -q Do not print out any messages. \n");
26 office 1127 fprintf(stdout, " \n");
11 office 1128 fprintf(stdout, "DATABASE is a path to where the indexed results will be \n");
1129 fprintf(stdout, "stored for searching with the Hunt tool. \n");
1130 fprintf(stdout, " \n");
1131 fprintf(stdout, "(c) 2021 Wizardry and Steamworks, MIT. \n");
1132 }
1133  
1 office 1134 /*
1135 *
1136 * Main entry point.
1137 */
1138 int main(int argc, char **argv) {
1139 int option;
33 office 1140 int i;
2 office 1141 char *dbFile;
19 office 1142 char *path;
38 office 1143 VECTOR *paths;
33 office 1144 OPERATION operation = NONE;
1 office 1145  
1146 // Bind handler to SIGINT.
26 office 1147 #if !defined ___AmigaOS___
1 office 1148 signal(SIGINT, SignalHandler);
26 office 1149 #endif
1 office 1150  
2 office 1151 dbFile = DEFAULT_DATABASE_FILE;
26 office 1152 while((option = getopt(argc, argv, "hqdm:arc")) != -1) {
1 office 1153 switch(option) {
26 office 1154 case 'a':
1155 operation = GATHER;
1156 break;
1157 case 'r':
1158 operation = REMOVE;
1159 break;
1160 case 'c':
1161 operation = CREATE;
1162 break;
1163 case 'm':
1164 maxmem = strtoul(optarg, NULL, 10);
1165 break;
2 office 1166 case 'd':
1167 dbFile = optarg;
1168 break;
1 office 1169 case 'q':
33 office 1170 PROGRAM_VERBOSE = FALSE;
1 office 1171 break;
1172 case 'h':
11 office 1173 usage(argv[0]);
2 office 1174 return 0;
1 office 1175 case '?':
1176 fprintf(stderr, "Invalid option %ct.\n", optopt);
37 office 1177 return 5;
1 office 1178 }
1179 }
1180  
26 office 1181 if(operation == NONE) {
1182 usage(argv[0]);
37 office 1183 return 5;
26 office 1184 }
10 office 1185  
1186 if(optind >= argc) {
11 office 1187 usage(argv[0]);
37 office 1188 return 5;
1 office 1189 }
1190  
38 office 1191 // Build the path vector.
1192 if((paths = malloc(1 * sizeof(*paths))) == NULL) {
1193 fprintf(stderr, "Memory allocation failure.\n");
1194 return 20;
1195 }
1196  
26 office 1197 // Go through all supplied arguments and add paths to search.
38 office 1198 if((paths->array = malloc((argc - optind) * sizeof(*paths))) == NULL) {
26 office 1199 fprintf(stderr, "Memory allocation failure.\n");
37 office 1200 return 20;
1 office 1201 }
31 office 1202  
38 office 1203 for(i = optind, paths->length = 0; i < argc; ++i) {
28 office 1204 if((path = PathToAbsolute(argv[i])) == NULL) {
26 office 1205 fprintf(stderr, "Absolute path for '%s' failed to resolve.\n", argv[optind]);
1206 continue;
1207 }
1 office 1208  
33 office 1209 switch(GetFsType(path)) {
1210 case UNKNOWN:
1211 case REGULAR:
1212 fprintf(stderr, "Path '%s' is not a directory.\n", path);
1213 free(path);
38 office 1214 path = NULL;
33 office 1215 continue;
1216 case DIRECTORY:
1217 break;
28 office 1218 }
1219  
33 office 1220 if(PROGRAM_VERBOSE) {
26 office 1221 fprintf(stdout, "Will process path: '%s'\n", path);
1222 }
1223  
1224 // Add the path to the array of paths.
38 office 1225 if((paths->array[paths->length] = malloc((strlen(path) + 1) * sizeof(*paths->array[paths->length]))) == NULL) {
26 office 1226 fprintf(stderr, "Memory allocation failure.");
37 office 1227 return 20;
26 office 1228 }
28 office 1229  
38 office 1230 sprintf(paths->array[paths->length], "%s", path);
1231 ++paths->length;
28 office 1232  
26 office 1233 free(path);
38 office 1234 path = NULL;
28 office 1235  
26 office 1236 }
1237  
38 office 1238 if(paths->length == 0) {
31 office 1239 fprintf(stderr, "No valid paths are available.\n");
38 office 1240 free(paths->array);
31 office 1241 free(paths);
38 office 1242 paths = NULL;
37 office 1243 return 5;
31 office 1244 }
1245  
33 office 1246 if(PROGRAM_VERBOSE) {
26 office 1247 fprintf(stdout, "Gathering to: '%s'\n", dbFile);
2 office 1248 }
1249  
26 office 1250 #if defined ___AmigaOS___
1251 locale = OpenLocale(NULL);
1252 #endif
1253  
1254 switch(operation) {
1255 case CREATE:
33 office 1256 if(PROGRAM_VERBOSE) {
26 office 1257 fprintf(stdout, "Removing '%s' and creating a new database.\n", dbFile);
1258 }
37 office 1259 RemoveFile(dbFile);
26 office 1260 case GATHER:
33 office 1261 if(PROGRAM_VERBOSE) {
26 office 1262 fprintf(stdout, "Gathering files to database...\n");
1263 }
38 office 1264 GatherDatabaseFiles(dbFile, paths);
26 office 1265 break;
1266 case REMOVE:
33 office 1267 if(PROGRAM_VERBOSE) {
26 office 1268 fprintf(stdout, "Removing files from database...\n");
1269 }
38 office 1270 RemoveDatabaseFiles(dbFile, paths);
26 office 1271 break;
1272 default:
31 office 1273 fprintf(stderr, "Unknown operation.\n");
37 office 1274 #if defined ___AmigaOS___
1275 CloseLocale(locale);
1276 #endif
1277  
1278 free(paths);
38 office 1279 paths = NULL;
37 office 1280 return 5;
26 office 1281 }
1 office 1282  
26 office 1283 #if defined ___AmigaOS___
1284 CloseLocale(locale);
1285 #endif
19 office 1286  
26 office 1287 free(paths);
38 office 1288 paths = NULL;
26 office 1289 return 0;
1 office 1290 }