HuntnGather – Blame information for rev 33

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