HuntnGather – Blame information for rev 46

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