HuntnGather – Blame information for rev 31

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>
21 #endif
1 office 22  
22 office 23 #if defined ___AsyncIO___
24 #include <asyncio.h>
25 #endif
26  
5 office 27 #if !defined ___HAVE_GETOPT___
1 office 28 #include "getopt.h"
29 #endif
30  
31 office 31 #include "StringStack.h"
32  
33 // MemLib memory debugging.
34 #if defined MWDEBUG
35 #include "memwatch.h"
36 #endif
37  
26 office 38 #define PROGRAM_VERSION "1.7.4"
19 office 39  
5 office 40 #if defined ___AmigaOS___
41 /*************************************************************************/
42 /* Version string used for querrying the program version. */
43 /*************************************************************************/
44 TEXT version_string[] =
19 office 45 "\0$VER: Gather " PROGRAM_VERSION " "__DATE__" by Wizardry and Steamworks";
5 office 46 #endif
1 office 47  
48 #if !defined TRUE
49 #define TRUE 1;
50 #endif
51  
52 #if !defined FALSE
53 #define FALSE 0;
54 #endif
55  
22 office 56 #define ASYNC_BUF 8192
2 office 57 #define MAX_MEM 262144
11 office 58 #define LINE_BUF 256
2 office 59 #define DEFAULT_DATABASE_FILE "S:gather.db"
1 office 60  
61 typedef struct {
62 unsigned int dirs;
63 unsigned int files;
64 } stats;
65  
26 office 66 typedef struct {
67 char *name;
68 char *path;
69 } dbEntry;
1 office 70  
26 office 71 typedef struct {
72 char **database;
73 unsigned int count;
74 } dbArray;
75  
76 enum MODE {
77 NONE,
78 GATHER,
79 REMOVE,
80 CREATE
81 } operation;
82  
83 unsigned int run = TRUE;
84 unsigned int verbose = TRUE;
85 unsigned int maxmem = MAX_MEM;
86 // Define global locale for string compare.
87 #if defined ___AmigaOS___
88 struct Locale *locale;
89 #endif
90  
1 office 91 void SignalHandler(int sig) {
92 // Toggle the run flag to stop execution.
93 run = FALSE;
94 }
95  
26 office 96 /*
97 *
98 * Used for sorting database lines.
99 */
100 int QsortCompare(const void *a, const void *b) {
101 #if defined ___AmigaOS___
31 office 102 return StrnCmp(
103 locale,
104 (STRPTR)(*(const char **)a),
105 (STRPTR)*((const char **)b),
106 -1,
107 SC_ASCII
108 );
26 office 109 #else
31 office 110 return strcmp(*(const char **)a, *(const char **)b);
26 office 111 #endif
1 office 112 }
113  
114 /*
115 *
26 office 116 * Gets the absolute path to file by name.
1 office 117 */
26 office 118 char *PathToAbsolute(char *path) {
119 char *abs;
120 #if defined ___AmigaOS___
121 BPTR lock;
122 #endif
123  
124 #if defined ___AmigaOS___
125 if((abs = malloc(PATH_MAX * sizeof(*abs))) == NULL) {
126 fprintf(stderr, "Memory allocation failure.\n");
127 return NULL;
128 }
129 if((lock = Lock(path, SHARED_LOCK)) == 0) {
130 fprintf(stderr, "Lock on %s failed.\n", path);
131 return NULL;
132 }
133 if(NameFromLock(lock, abs, PATH_MAX) == FALSE) {
134 fprintf(stderr, "Lock on %s failed.\n", path);
135 UnLock(lock);
136 return NULL;
137 }
138 UnLock(lock);
139 #else
30 office 140 //abs = realpath(path, NULL);
141 if((abs = malloc((strlen(path) + 1) * sizeof(*abs))) == NULL) {
142 fprintf(stderr, "Memory allocation failure.\n");
143 return NULL;
144 }
145 sprintf(abs, "%s", path);
26 office 146 #endif
147  
148 return abs;
149 }
150  
151 /*
152 *
153 * Compares path parts for equality.
154 */
155 #if defined ___AmigaOS___
156 BOOL PathCompare(char *path, char *look) {
157 #else
158 int PathCompare(char *path, char *look) {
159 #endif
160 char *a;
161 char *b;
162  
163 for(a = path, b = look; *a != '\0' && *b != '\0'; ++a, ++b) {
164 if(*b != '\0' && *a != *b) {
165 return FALSE;
166 }
167 }
168  
169 return *b == '\0';
170 }
171  
172 /*
173 *
174 * Gets the size of a file by name.
175 */
176 int GetFileSize(char *dbFile) {
22 office 177 #if defined ___AsyncIO___
178 struct AsyncFile *fp;
26 office 179 LONG size;
22 office 180 #else
1 office 181 FILE *fp;
26 office 182 int size;
22 office 183 #endif
1 office 184  
22 office 185 #if defined ___AsyncIO___
186 if((fp = OpenAsync(dbFile, MODE_READ, ASYNC_BUF)) == NULL) {
187 #else
1 office 188 if((fp = fopen(dbFile, "r")) == NULL) {
22 office 189 #endif
24 office 190 fprintf(stderr, "Unable to open '%s' for reading.\n", dbFile);
26 office 191 return -1;
1 office 192 }
193  
26 office 194 #if defined ___AsyncIO___
195 if(SeekAsync(fp, 0, MODE_END) == -1) {
196 #else
30 office 197 if(fseek(fp, 0L, SEEK_END) != 0) {
26 office 198 #endif
199 fprintf(stderr, "Seek in file %s failed.\n", dbFile);
200 #if defined ___AsyncIO___
201 CloseAsync(fp);
202 #else
203 fclose(fp);
204 #endif
205 return -1;
206 }
207 #if defined ___AsyncIO___
208 if((size = SeekAsync(fp, 0, MODE_CURRENT)) == -1) {
209 fprintf(stderr, "Seek in file %s failed.\n", dbFile);
210 CloseAsync(fp);
211 return -1;
212 }
213 #else
214 size = ftell(fp);
215 #endif
22 office 216  
26 office 217 #if defined ___AsyncIO___
218 CloseAsync(fp);
219 #else
220 fclose(fp);
221 #endif
1 office 222  
26 office 223 return size;
224 }
225  
226 /*
227 *
228 * Counts the lines of a file.
229 */
230 int CountFileLines(char *dbFile) {
231 #if defined ___AsyncIO___
232 struct AsyncFile *fp;
233 LONG c;
234 #else
235 FILE *fp;
236 char c;
237 #endif
238 int lines;
239  
240 #if defined ___AsyncIO___
241 if((fp = OpenAsync(dbFile, MODE_READ, ASYNC_BUF)) == NULL) {
242 #else
243 if((fp = fopen(dbFile, "r")) == NULL) {
244 #endif
245 fprintf(stderr, "Unable to open '%s' for reading.\n", dbFile);
246 return -1;
1 office 247 }
24 office 248  
26 office 249 lines = 0;
30 office 250 if(verbose) {
251 fprintf(stdout, "Lines in '%s' so far: %d\r", dbFile, lines);
252 }
253  
22 office 254 #if defined ___AsyncIO___
255 while(run && (c = ReadCharAsync(fp)) != -1) {
256 #else
1 office 257 while(run && fscanf(fp, "%c", &c) == 1) {
22 office 258 #endif
1 office 259 #if defined ___AmigaOS___
260 // Check if CTRL+C was pressed and abort the program.
26 office 261 if(SetSignal(0L, SIGBREAKF_CTRL_C) & SIGBREAKF_CTRL_C) {
1 office 262 run = FALSE;
263 continue;
264 }
265 #endif
266 switch(c) {
267 case '\n':
26 office 268 ++lines;
30 office 269  
270 if(verbose) {
271 fprintf(stdout, "Lines in '%s' so far: %d\r", dbFile, lines);
272 }
1 office 273 break;
274 }
275 }
276  
22 office 277 #if defined ___AsyncIO___
278 CloseAsync(fp);
279 #else
1 office 280 fclose(fp);
22 office 281 #endif
1 office 282  
30 office 283 if(verbose) {
284 fprintf(stdout, "\n");
285 }
286  
26 office 287 return lines;
288 }
1 office 289  
26 office 290 /*
291 *
292 * Creates a temporary file and returns its name.
293 */
294 char *CreateTemporaryFile(void) {
295 char *name;
296  
297 name = tmpnam(NULL);
298  
299 return name;
300 }
301  
302 /*
303 *
304 * Create multiple temporary files and return their names.
305 */
306 char **CreateTemporaryFiles(int files) {
307 char **tmpNames;
308 int count;
309  
310 if((tmpNames = malloc(files * sizeof(*tmpNames))) == NULL) {
311 fprintf(stderr, "Memory allocation failure.\n");
312 return NULL;
313 }
314  
315 if(verbose) {
316 fprintf(stdout, "Creating temporary files...\r");
317 }
318  
319 count = files;
320 while(run && --count > -1) {
321 #if defined ___AmigaOS___
322 // Check if CTRL+C was pressed and abort the program.
323 if(SetSignal(0L, SIGBREAKF_CTRL_C) & SIGBREAKF_CTRL_C) {
324 run = FALSE;
325 continue;
326 }
327 #endif
328 tmpNames[count] = CreateTemporaryFile();
329  
330 if(verbose) {
331 fprintf(stdout, "Creating temporary files: %d%%\r", 100 - (int)(((float)count / files) * 100.0));
332 }
333 }
334  
335 if(verbose) {
336 fprintf(stdout, "\n");
337 }
338  
339 return tmpNames;
340 }
341  
342  
343 /*
344 *
345 * Skips a line in a file.
346 */
22 office 347 #if defined ___AsyncIO___
26 office 348 void SkipLine(struct AsyncFile *fp) {
349 LONG c;
350 while(run && (c = ReadCharAsync(fp)) != -1) {
22 office 351 #else
26 office 352 void SkipLine(FILE *fp) {
353 char c;
354 while(run && fscanf(fp, "%c", &c) == 1) {
22 office 355 #endif
23 office 356 #if defined ___AmigaOS___
357 // Check if CTRL+C was pressed and abort the program.
26 office 358 if(SetSignal(0L, SIGBREAKF_CTRL_C) & SIGBREAKF_CTRL_C) {
23 office 359 run = FALSE;
360 continue;
361 }
362 #endif
26 office 363 switch(c) {
364 case '\n':
365 return;
366 }
367 }
368 }
369  
370 /*
371 *
372 * Peeks at a line from a file.
373 */
22 office 374 #if defined ___AsyncIO___
26 office 375 char *PeekLine(struct AsyncFile *fp) {
376 LONG c;
22 office 377 #else
26 office 378 char *PeekLine(FILE *fp) {
379 char c;
22 office 380 #endif
31 office 381 char *line = NULL;
382 char *real = NULL;
26 office 383 unsigned int size;
384 int i;
385  
386 size = LINE_BUF;
387 if((line = malloc(size * sizeof(*line))) == NULL) {
388 fprintf(stderr, "Memory allocation failure.\n");
389 return NULL;
1 office 390 }
391  
26 office 392 i = 0;
22 office 393 #if defined ___AsyncIO___
26 office 394 while(run && (c = ReadCharAsync(fp)) != -1) {
22 office 395 #else
26 office 396 while(run && fscanf(fp, "%c", &c) == 1) {
22 office 397 #endif
26 office 398 #if defined ___AmigaOS___
399 // Check if CTRL+C was pressed and abort the program.
400 if(SetSignal(0L, SIGBREAKF_CTRL_C) & SIGBREAKF_CTRL_C) {
401 run = FALSE;
402 continue;
403 }
404 #endif
405 switch(c) {
406 case '\n':
407 // Rewind the file by the number of read characters.
408 #if defined ___AsyncIO___
409 if(SeekAsync(fp, -(i + 1), MODE_CURRENT) == -1) {
410 fprintf(stderr, "Could not seek in file.\n");
411 free(line);
412 return NULL;
413 }
414 #else
30 office 415 if(fseek(fp, -(i + 1), SEEK_CUR) != 0) {
416 fprintf(stderr, "Could not seek in file.\n");
417 free(line);
418 return NULL;
419 }
26 office 420 #endif
421 return line;
422 default:
423 if(strlen(line) == size) {
424 size = size * 1.5;
31 office 425 real = realloc(line, size * sizeof(*line));
426 if(real == NULL) {
427 fprintf(stderr, "Memory reallocation failure.\n");
428 free(line);
429 return NULL;
430 }
431 line = real;
26 office 432 }
433 line[i] = c;
434 line[i + 1] = '\0';
435 break;
436 }
437 ++i;
438 }
22 office 439  
31 office 440 if(line != NULL) {
441 free(line);
442 }
443  
26 office 444 return NULL;
1 office 445 }
446  
447 /*
448 *
26 office 449 * Read a line from a file.
1 office 450 */
22 office 451 #if defined ___AsyncIO___
26 office 452 char *ReadLine(struct AsyncFile *fp) {
453 LONG c;
22 office 454 #else
26 office 455 char *ReadLine(FILE *fp) {
456 char c;
22 office 457 #endif
31 office 458 char *line = NULL;
459 char *real = NULL;
1 office 460 unsigned int size;
26 office 461 unsigned int i;
1 office 462  
26 office 463 size = LINE_BUF;
464 if((line = malloc(size * sizeof(*line))) == NULL) {
465 fprintf(stderr, "Memory allication failure.\n");
466 return NULL;
467 }
468  
469 i = 0;
22 office 470 #if defined ___AsyncIO___
26 office 471 while(run && (c = ReadCharAsync(fp)) != -1) {
22 office 472 #else
26 office 473 while(run && fscanf(fp, "%c", &c) == 1) {
22 office 474 #endif
1 office 475 #if defined ___AmigaOS___
476 // Check if CTRL+C was pressed and abort the program.
477 if(SetSignal(0L, SIGBREAKF_CTRL_C) & SIGBREAKF_CTRL_C) {
478 run = FALSE;
23 office 479 continue;
1 office 480 }
481 #endif
26 office 482 switch(c) {
483 case '\n':
484 return line;
485 default:
486 if(strlen(line) == size) {
487 size = size * 1.5;
31 office 488 real = realloc(line, size * sizeof(*line));
489 if(real == NULL) {
490 fprintf(stderr, "Memory reallocation failure.\n");
491 free(line);
492 return NULL;
493 }
494 line = real;
26 office 495 }
496 line[i] = c;
497 line[i + 1] = '\0';
498 break;
1 office 499 }
26 office 500 ++i;
501 }
1 office 502  
31 office 503 if(line != NULL) {
504 free(line);
505 }
506  
26 office 507 return NULL;
508 }
1 office 509  
26 office 510 /*
511 *
512 * Delete a file.
513 */
1 office 514 #if defined ___AmigaOS___
26 office 515 BOOL RemoveFile(char *name) {
516 return DeleteFile(name);
517 #else
518 int RemoveFile(char *name) {
519 return remove(name) == 0;
1 office 520 #endif
26 office 521 }
1 office 522  
26 office 523 /*
524 *
525 * Deletes files.
526 */
527 void RemoveFiles(char **names, int count) {
528 unsigned int i;
529 for(i = 0; i < count; ++i) {
530 if(RemoveFile(names[i]) == FALSE) {
531 fprintf(stderr, "Unable to remove %s...\n", names[i]);
532 continue;
533 }
534 fprintf(stderr, "Removing file: %s\n", names[i]);
535 }
536 }
1 office 537  
26 office 538 /*
539 *
540 * Copies a file to another file by name.
541 */
542 void CopyFile(char *a, char *b) {
543 #if defined ___AsyncIO___
544 struct AsyncFile *ap;
545 struct AsyncFile *bp;
546 LONG c;
547 #else
548 FILE *ap;
549 FILE *bp;
550 char c;
551 #endif
1 office 552  
26 office 553 // Open database file for writing.
554 #if defined ___AsyncIO___
555 if((ap = OpenAsync(a, MODE_READ, ASYNC_BUF)) == NULL) {
556 #else
557 if((ap = fopen(a, "r")) == NULL) {
558 #endif
559 fprintf(stderr, "Unable to open '%s' for reading.\n", a);
560 return;
561 }
15 office 562  
26 office 563 // Open temporary file for reading.
564 #if defined ___AsyncIO___
565 if((bp = OpenAsync(b, MODE_WRITE, ASYNC_BUF)) == NULL) {
566 #else
30 office 567 if((bp = fopen(b, "w")) == NULL) {
15 office 568 #endif
26 office 569 fprintf(stderr, "Unable to open file '%s' for writing.\n", b);
15 office 570  
26 office 571 // Close database file.
22 office 572 #if defined ___AsyncIO___
26 office 573 CloseAsync(ap);
22 office 574 #else
26 office 575 fclose(ap);
22 office 576 #endif
1 office 577  
26 office 578 return;
1 office 579 }
580  
26 office 581 #if defined ___AsyncIO___
582 while(run && (c = ReadCharAsync(ap)) != -1) {
583 #else
584 while(run && fscanf(ap, "%c", &c) == 1) {
585 #endif
586 #if defined ___AmigaOS___
587 // Check if CTRL+C was pressed and abort the program.
30 office 588 if(SetSignal(0L, SIGBREAKF_CTRL_C) & SIGBREAKF_CTRL_C) {
26 office 589 run = FALSE;
590 continue;
591 }
592 #endif
593 #if defined ___AsyncIO___
594 if(WriteCharAsync(bp, (UBYTE)c) != 1) {
595 #else
596 if(fprintf(bp, "%c", c) != 1) {
597 #endif
598 fprintf(stderr, "Unable to write to '%s'.\n", b);
599 break;
600 }
1 office 601 }
602  
22 office 603 #if defined ___AsyncIO___
26 office 604 CloseAsync(ap);
605 CloseAsync(bp);
22 office 606 #else
26 office 607 fclose(ap);
608 fclose(bp);
22 office 609 #endif
1 office 610 }
611  
612 /*
613 *
26 office 614 * Write lines to a file.
1 office 615 */
26 office 616 void WriteLinesToFile(char *dbFile, char **lines, unsigned int count) {
23 office 617 #if defined ___AsyncIO___
618 struct AsyncFile *fp;
619 #else
1 office 620 FILE *fp;
23 office 621 #endif
30 office 622 unsigned int i;
26 office 623 char *rem;
1 office 624  
26 office 625 // Write the database lines back to the database.
23 office 626 #if defined ___AsyncIO___
26 office 627 if((fp = OpenAsync(dbFile, MODE_WRITE, ASYNC_BUF)) == NULL) {
23 office 628 #else
26 office 629 if((fp = fopen(dbFile, "w")) == NULL) {
23 office 630 #endif
26 office 631 fprintf(stderr, "Unable to open '%s' for writing.\n", dbFile);
632 return;
1 office 633 }
634  
26 office 635 rem = NULL;
636 for(i = 0; i < count; ++i) {
637 #if defined ___AmigaOS___
638 // Check if CTRL+C was pressed and abort the program.
30 office 639 if(SetSignal(0L, SIGBREAKF_CTRL_C) & SIGBREAKF_CTRL_C) {
26 office 640 run = FALSE;
641 continue;
642 }
643 #endif
644  
645 if(rem != NULL) {
646 #if defined ___AmigaOS___
647 if(StrnCmp(locale, lines[i], rem, -1, SC_ASCII) == 0) {
648 #else
27 office 649 if(strcmp(lines[i], rem) == 0) {
26 office 650 #endif
651 continue;
652 }
653 }
654  
23 office 655 #if defined ___AsyncIO___
26 office 656 WriteAsync(fp, lines[i], (LONG)strlen(lines[i]));
657 WriteAsync(fp, "\n", 1);
23 office 658 #else
26 office 659 fprintf(fp, "%s\n", lines[i]);
23 office 660 #endif
1 office 661  
26 office 662 if(rem != NULL) {
663 free(rem);
664 }
665  
666 rem = malloc(strlen(lines[i]) + 1);
667 sprintf(rem, "%s", lines[i]);
668 }
669  
31 office 670 if(rem != NULL) {
671 free(rem);
672 }
673  
23 office 674 #if defined ___AsyncIO___
26 office 675 CloseAsync(fp);
23 office 676 #else
1 office 677 fclose(fp);
23 office 678 #endif
26 office 679 }
23 office 680  
26 office 681 /*
682 *
683 * Create a database entry from a line of text.
684 */
31 office 685 dbEntry* CreateDatabaseEntry(char *line) {
26 office 686 dbEntry *entry;
687 char *ptr;
688 unsigned int side;
689 unsigned int i;
690 unsigned int j;
691  
692 if((entry = malloc(1 * sizeof(*entry))) == NULL) {
693 fprintf(stderr, "Memory allocation failure.\n");
694 return NULL;
695 }
696  
31 office 697 if((entry->name = malloc((strlen(line) + 1) * sizeof(*entry->name))) == NULL) {
26 office 698 fprintf(stderr, "Memory allocation failure.\n");
699 return NULL;
700 }
701  
31 office 702 if((entry->path = malloc((strlen(line) + 1) * sizeof(*entry->path))) == NULL) {
26 office 703 fprintf(stderr, "Memory allocation failure.\n");
704 return NULL;
705 }
706  
707 for(ptr = line, side = 0, i = 0, j = 0; run && *ptr != '\0'; ++ptr) {
708 #if defined ___AmigaOS___
709 // Check if CTRL+C was pressed and abort the program.
31 office 710 if(SetSignal(0L, SIGBREAKF_CTRL_C) & SIGBREAKF_CTRL_C) {
26 office 711 run = FALSE;
712 continue;
713 }
714 #endif
715 switch(*ptr) {
716 case '\t':
717 entry->name[i] = '\0';
718 ++side;
719 break;
720 case '\n':
721 entry->path[j] = '\0';
722 return entry;
723 default:
724 switch(side) {
725 case 0:
726 entry->name[i++] = *ptr;
727 break;
728 case 1:
729 entry->path[j++] = *ptr;
730 break;
731 }
732 break;
733 }
734 }
735  
736 return entry;
1 office 737 }
738  
739 /*
740 *
26 office 741 *
1 office 742 */
26 office 743 dbArray *GetDatabaseArray(char *dbFile) {
22 office 744 #if defined ___AsyncIO___
745 struct AsyncFile *fp;
746 #else
1 office 747 FILE *fp;
22 office 748 #endif
26 office 749 dbArray *array;
750 dbEntry *entry;
31 office 751 char *line = NULL;
752 char *real = NULL;
26 office 753 unsigned int count;
1 office 754  
26 office 755 // Open database file for reading.
22 office 756 #if defined ___AsyncIO___
757 if((fp = OpenAsync(dbFile, MODE_READ, ASYNC_BUF)) == NULL) {
758 #else
1 office 759 if((fp = fopen(dbFile, "r")) == NULL) {
22 office 760 #endif
24 office 761 fprintf(stderr, "Unable to open '%s' for reading.\n", dbFile);
26 office 762 return NULL;
1 office 763 }
764  
31 office 765 if((array = malloc(1 * sizeof(*array))) == NULL) {
766 fprintf(stderr, "Memory allocation failure.\n");
767 #if defined ___AsyncIO___
768 CloseAsync(fp);
769 #else
770 fclose(fp);
771 #endif
772 return NULL;
773 }
774  
26 office 775 count = 0;
776 while(run && (line = ReadLine(fp)) != NULL) {
23 office 777 #if defined ___AmigaOS___
778 // Check if CTRL+C was pressed and abort the program.
26 office 779 if(SetSignal(0L, SIGBREAKF_CTRL_C) & SIGBREAKF_CTRL_C) {
23 office 780 run = FALSE;
781 continue;
782 }
783 #endif
31 office 784 if((entry = CreateDatabaseEntry(line)) == NULL) {
26 office 785 fprintf(stderr, "Unable to create database entry.\n");
31 office 786 free(line);
26 office 787 #if defined ___AsyncIO___
788 CloseAsync(fp);
789 #else
790 fclose(fp);
791 #endif
792 return NULL;
1 office 793 }
26 office 794  
795 // Load up the name and path into the database variable.
31 office 796 real = realloc(array->database, (count + 1) * sizeof(*array->database));
797 if(real == NULL) {
798 fprintf(stderr, "Memory reallocation failure.\n");
799 free(entry->name);
800 free(entry->path);
801 free(entry);
802 free(line);
803 #if defined ___AsyncIO___
804 CloseAsync(fp);
805 #else
806 fclose(fp);
807 #endif
808 return NULL;
809 }
810 array->database = real;
811  
26 office 812 if((array->database[count] = malloc((strlen(entry->name) + strlen(entry->path) + 1 + 1) * sizeof(*array->database[count]))) == NULL) {
813 fprintf(stderr, "Memory allocation failure.\n");
31 office 814 free(entry->name);
815 free(entry->path);
26 office 816 free(entry);
817 free(line);
818 #if defined ___AsyncIO___
819 CloseAsync(fp);
820 #else
821 fclose(fp);
822 #endif
823 return NULL;
824 }
825 sprintf(array->database[count], "%s\t%s", entry->name, entry->path);
826 ++count;
827  
30 office 828 // Free the database entry.
829 free(entry->name);
830 free(entry->path);
26 office 831 free(entry);
30 office 832  
26 office 833 free(line);
1 office 834 }
835  
31 office 836 if(line != NULL) {
837 free(line);
838 }
839  
22 office 840 #if defined ___AsyncIO___
841 CloseAsync(fp);
842 #else
1 office 843 fclose(fp);
22 office 844 #endif
1 office 845  
26 office 846 array->count = count;
847 return array;
1 office 848 }
849  
850 /*
851 *
26 office 852 * Sorts a database file lexicographically.
1 office 853 */
26 office 854 void SortDatabase(char *dbFile) {
855 dbArray *array;
30 office 856 int i;
1 office 857  
26 office 858 if(verbose) {
859 fprintf(stdout, "Sorting '%s'...\n", dbFile);
860 }
1 office 861  
26 office 862 // Retrieve the database as an array.
863 if((array = GetDatabaseArray(dbFile)) == NULL) {
864 fprintf(stderr, "Unable to read '%s' as a database file.\n", dbFile);
865 return;
866 }
867  
868 // Sort the database.
869 qsort(array->database, array->count, sizeof(char *), QsortCompare);
870  
871 // Write back the database to the database file.
872 WriteLinesToFile(dbFile, array->database, array->count);
873  
30 office 874 // Deallocate all the lines.
875 for(i = 0; i < array->count; ++i) {
876 free(array->database[i]);
877 }
878  
26 office 879 free(array);
880 }
881  
882 /*
883 *
884 * Updates a database file "dbFile".
885 */
886 stats *CollectFiles(char *dbFile, char **paths, unsigned int count) {
887 #if defined ___AsyncIO___
888 struct AsyncFile *fp;
889 #else
890 FILE *fp;
891 #endif
28 office 892 #if defined ___AmigaOS___
893 struct FileInfoBlock *FIBp, *FIBq;
894 BPTR lockp, lockq;
895 #else
26 office 896 DIR *dir;
897 struct dirent *entry;
29 office 898 struct stat dirStat;
28 office 899 #endif
900 stringStack *stack;
31 office 901 stats *stats = NULL;
26 office 902 int i;
903 char *path;
28 office 904 char *sub;
26 office 905  
906 #if defined ___AsyncIO___
907 if((fp = OpenAsync(dbFile, MODE_APPEND, ASYNC_BUF)) == NULL) {
908 #else
30 office 909 if((fp = fopen(dbFile, "a")) == NULL) {
26 office 910 #endif
911 fprintf(stderr, "Unable to open '%s' for writing.\n", dbFile);
31 office 912 return NULL;
26 office 913 }
914  
1 office 915 if(verbose) {
26 office 916 fprintf(stdout, "Collecting files...\r");
1 office 917 }
918  
31 office 919 // Initialize metrics.
920 if((stats = malloc(sizeof(*stats))) == NULL) {
921 fprintf(stderr, "Memory allocation failure.\n");
922 #if defined ___AsyncIO___
923 CloseAsync(fp);
924 #else
925 fclose(fp);
926 #endif
927 return NULL;
928 }
929  
930 stats->dirs = 0;
931 stats->files = 0;
932  
26 office 933 // Push the first path onto the stack.
934 stack = stringStackCreate(count);
935 for(i = 0; run && i < count; ++i) {
936 stringStackPush(stack, paths[i]);
937 }
938  
939 while(run && !stringStackIsEmpty(stack)) {
1 office 940 #if defined ___AmigaOS___
941 // Check if CTRL+C was pressed and abort the program.
942 if(SetSignal(0L, SIGBREAKF_CTRL_C) & SIGBREAKF_CTRL_C) {
943 run = FALSE;
23 office 944 continue;
1 office 945 }
946 #endif
26 office 947 if((path = stringStackPop(stack)) == NULL) {
948 break;
949 }
1 office 950  
28 office 951 #if defined ___AmigaOS___
952 if((lockp = Lock(path, ACCESS_READ)) == NULL) {
953 fprintf(stderr, "Could not lock path '%s' for reading.\n", path);
954 free(path);
955 continue;
26 office 956 }
24 office 957  
30 office 958 if((FIBp = AllocDosObject(DOS_FIB, NULL)) == NULL) {
28 office 959 fprintf(stderr, "Path '%s' info block allocation failure.\n", path);
960 UnLock(lockp);
961 free(path);
962 continue;
963 }
964  
965 if(Examine(lockp, FIBp) == FALSE) {
966 fprintf(stderr, "Path '%s' could not be examined.\n", path);
967 FreeDosObject(DOS_FIB, FIBp);
968 UnLock(lockp);
969 free(path);
970 continue;
971 }
972  
973 while(run && ExNext(lockp, FIBp)) {
26 office 974 // Check if CTRL+C was pressed and abort the program.
975 if(SetSignal(0L, SIGBREAKF_CTRL_C) & SIGBREAKF_CTRL_C) {
976 run = FALSE;
31 office 977 continue;
26 office 978 }
28 office 979 #else
980  
981 if((dir = opendir(path)) == NULL) {
982 fprintf(stderr, "Unable to open '%s' for reading.\n", path);
983 free(path);
984 continue;
985 }
986  
987 while(run && (entry = readdir(dir)) != NULL) {
26 office 988 #endif
989 switch(path[strlen(path) - 1]) {
990 case '/':
991 case ':': // This is a drive path.
28 office 992 #if defined ___AmigaOS___
30 office 993 if((sub = malloc(strlen(path) + strlen(FIBp->fib_FileName) + 1)) == NULL) {
28 office 994 #else
30 office 995 if((sub = malloc(strlen(path) + strlen(entry->d_name) + 1)) == NULL) {
28 office 996 #endif
26 office 997 fprintf(stderr, "Memory allocation failure.\n");
28 office 998 #if defined ___AmigaOS___
999 FreeDosObject(DOS_FIB, FIBp);
1000 UnLock(lockp);
1001 #else
26 office 1002 closedir(dir);
28 office 1003 #endif
26 office 1004 free(path);
1005 stringStackDestroy(stack);
1006 #if defined ___AsyncIO___
1007 CloseAsync(fp);
1008 #else
1009 fclose(fp);
1010 #endif
1011 return NULL;
1012 }
28 office 1013 #if defined ___AmigaOS___
1014 sprintf(sub, "%s%s", path, FIBp->fib_FileName);
1015 #else
1016 sprintf(sub, "%s%s", path, entry->d_name);
1017 #endif
26 office 1018 break;
1019 default:
28 office 1020 #if defined ___AmigaOS___
30 office 1021 if((sub = malloc(strlen(path) + strlen(FIBp->fib_FileName) + 1 + 1)) == NULL) {
28 office 1022 #else
30 office 1023 if((sub = malloc(strlen(path) + strlen(entry->d_name) + 1 + 1)) == NULL) {
28 office 1024 #endif
26 office 1025 fprintf(stderr, "Memory allocation failure.\n");
28 office 1026 #if defined ___AmigaOS___
1027 FreeDosObject(DOS_FIB, FIBp);
1028 UnLock(lockp);
1029 #else
26 office 1030 closedir(dir);
28 office 1031 #endif
26 office 1032 free(path);
1033 stringStackDestroy(stack);
1034 #if defined ___AsyncIO___
1035 CloseAsync(fp);
1036 #else
1037 fclose(fp);
1038 #endif
1039 return NULL;
1040 }
28 office 1041 #if defined ___AmigaOS___
1042 sprintf(sub, "%s/%s", path, FIBp->fib_FileName);
1043 #else
1044 sprintf(sub, "%s/%s", path, entry->d_name);
1045 #endif
26 office 1046 break;
1047 }
28 office 1048  
30 office 1049  
28 office 1050 #if defined ___AmigaOS___
1051 if((lockq = Lock(sub, ACCESS_READ)) == NULL) {
1052 fprintf(stderr, "Could not lock path '%s' for reading.\n", sub);
1053 free(sub);
1054 continue;
1055 }
1056  
30 office 1057 if((FIBq = AllocDosObject(DOS_FIB, NULL)) == NULL) {
28 office 1058 fprintf(stderr, "Path '%s' info block allocation failure.\n", sub);
1059 UnLock(lockq);
1060 free(sub);
1061 continue;
1062 }
1063  
1064 if(Examine(lockq, FIBq) == FALSE) {
1065 fprintf(stderr, "Path '%s' could not be examined.\n", sub);
1066 FreeDosObject(DOS_FIB, FIBq);
1067 UnLock(lockq);
1068 free(sub);
1069 continue;
1070 }
1071  
1072 if(FIBq->fib_DirEntryType > 0) {
1073 #else
1074 stat(sub, &dirStat);
26 office 1075 if(S_ISDIR(dirStat.st_mode)) {
28 office 1076 #endif
1077 stringStackPush(stack, sub);
26 office 1078  
1079 ++stats->dirs;
1080  
1081 if(verbose) {
1082 fprintf(stdout,
1083 "Gathered %d directories and %d files.\r",
1084 stats->dirs,
1085 stats->files);
1086 }
1087  
28 office 1088 #if defined ___AmigaOS___
1089 FreeDosObject(DOS_FIB, FIBq);
1090 UnLock(lockq);
1091 #endif
1092 free(sub);
26 office 1093 continue;
1094 }
1095  
28 office 1096 #if defined ___AmigaOS___
1097 FreeDosObject(DOS_FIB, FIBq);
1098 UnLock(lockq);
1099 #endif
1100  
30 office 1101 ++stats->files;
1102  
1103 if(verbose) {
1104 fprintf(stdout,
1105 "Gathered %d directories and %d files.\r",
1106 stats->dirs,
1107 stats->files);
1108 }
1109  
26 office 1110 #if defined ___NOCASE_FS___
28 office 1111 #if defined ___AmigaOS___
1112 strupr(FIBp->fib_FileName);
1113 #else
26 office 1114 strupr(entry->d_name);
1115 #endif
28 office 1116 #endif
30 office 1117  
1118  
26 office 1119 // Write to database file.
1120 #if defined ___AsyncIO___
28 office 1121 #if defined ___AmigaOS___
1122 WriteAsync(fp, FIBp->fib_FileName, (LONG)strlen(FIBp->fib_FileName));
1123 #else
26 office 1124 WriteAsync(fp, entry->d_name, (LONG)strlen(entry->d_name));
28 office 1125 #endif
26 office 1126 WriteAsync(fp, "\t", 1);
28 office 1127 WriteAsync(fp, sub, (LONG)strlen(sub));
26 office 1128 WriteAsync(fp, "\n", 1);
1129 #else
28 office 1130 #if defined ___AmigaOS___
1131 fprintf(fp, "%s\t%s\n", FIBp->fib_FileName, sub);
1132 #else
1133 fprintf(fp, "%s\t%s\n", entry->d_name, sub);
26 office 1134 #endif
28 office 1135 #endif
1136 free(sub);
26 office 1137 }
1138  
28 office 1139 #if defined ___AmigaOS___
1140 FreeDosObject(DOS_FIB, FIBp);
1141 UnLock(lockp);
1142 #else
26 office 1143 closedir(dir);
28 office 1144 #endif
26 office 1145 free(path);
1 office 1146 }
1147  
1148 if(verbose) {
1149 fprintf(stdout, "\n");
1150 }
1151  
26 office 1152 stringStackDestroy(stack);
1153  
1154 #if defined ___AsyncIO___
1155 CloseAsync(fp);
1156 #else
1157 fclose(fp);
1158 #endif
1159  
1160 return stats;
1161  
1 office 1162 }
1163  
1164 /*
1165 *
1166 * Writes lines from the database "dbFile" to temporary filenames "tmpNames".
1167 */
23 office 1168 void WriteTemporaryFiles(char *dbFile, char **tmpNames, int tmpFiles, int tmpLines, int total) {
22 office 1169 #if defined ___AsyncIO___
1170 struct AsyncFile *fp, *tp;
1171 LONG c;
1172 #else
1 office 1173 FILE *fp, *tp;
1174 char c;
22 office 1175 #endif
1 office 1176 int lines;
26 office 1177 int write;
1 office 1178  
22 office 1179 #if defined ___AsyncIO___
1180 if((fp = OpenAsync(dbFile, MODE_READ, ASYNC_BUF)) == NULL) {
1181 #else
1 office 1182 if((fp = fopen(dbFile, "r")) == NULL) {
22 office 1183 #endif
24 office 1184 fprintf(stderr, "Unable to open '%s' for reading.\n", dbFile);
1 office 1185 return;
1186 }
1187  
22 office 1188 #if defined ___AsyncIO___
23 office 1189 if((tp = OpenAsync(tmpNames[--tmpFiles], MODE_WRITE, ASYNC_BUF)) == NULL) {
22 office 1190 #else
23 office 1191 if((tp = fopen(tmpNames[--tmpFiles], "w")) == NULL) {
22 office 1192 #endif
24 office 1193 fprintf(stderr, "Unable to open '%s' for writing.\n", tmpNames[tmpFiles]);
1194 #if defined ___AsyncIO___
1195 CloseAsync(fp);
1196 #else
1197 fclose(fp);
1198 #endif
1 office 1199 return;
1200 }
1201  
1202 if(verbose) {
24 office 1203 fprintf(stdout, "Writing to temporary files...\r");
1 office 1204 }
1205  
26 office 1206 write = 0;
1 office 1207 lines = 0;
22 office 1208 #if defined ___AsyncIO___
1209 while(run && (c = ReadCharAsync(fp)) != -1) {
1210 #else
1 office 1211 while(run && fscanf(fp, "%c", &c) == 1) {
22 office 1212 #endif
1 office 1213 #if defined ___AmigaOS___
1214 // Check if CTRL+C was pressed and abort the program.
1215 if(SetSignal(0L, SIGBREAKF_CTRL_C) & SIGBREAKF_CTRL_C) {
1216 run = FALSE;
23 office 1217 continue;
1 office 1218 }
1219 #endif
1220 switch(c) {
1221 case '\n':
1222 // Increment the total written lines.
26 office 1223 ++write;
1 office 1224  
1225 if(verbose) {
26 office 1226 fprintf(stdout, "Writing to temporary files: %d%%.\r", (int)(((float)write / total) * 100.0));
1 office 1227 }
1228  
1229 // Write the newline character back.
22 office 1230 #if defined ___AsyncIO___
1231 if(WriteCharAsync(tp, (UBYTE)c) != 1) {
1232 #else
1 office 1233 if(fprintf(tp, "%c", c) != 1) {
22 office 1234 #endif
24 office 1235 fprintf(stderr, "Unable to write to '%s'.\n", tmpNames[tmpFiles]);
22 office 1236 #if defined ___AsyncIO___
1237 CloseAsync(tp);
1238 CloseAsync(fp);
1239 #else
19 office 1240 fclose(tp);
1 office 1241 fclose(fp);
22 office 1242 #endif
1 office 1243 return;
1244 }
1245 // Switch to the next temporary file.
1246 if(++lines >= tmpLines) {
1247 // If there are no temporary files left then run till the end.
1248 if(tmpFiles - 1 < 0) {
1249 break;
1250 }
1251  
1252 // Close the previous temporary file and write to the next temporary file.
22 office 1253 #if defined ___AsyncIO___
1254 CloseAsync(tp);
23 office 1255 if((tp = OpenAsync(tmpNames[--tmpFiles], MODE_WRITE, ASYNC_BUF)) == NULL) {
22 office 1256 #else
1 office 1257 fclose(tp);
23 office 1258 if((tp = fopen(tmpNames[--tmpFiles], "w")) == NULL) {
22 office 1259 #endif
24 office 1260 fprintf(stderr, "Unable to open '%s' for writing.\n", tmpNames[tmpFiles]);
22 office 1261 #if defined ___AsyncIO___
1262 CloseAsync(fp);
1263 #else
1 office 1264 fclose(fp);
22 office 1265 #endif
24 office 1266 return;
1 office 1267 }
1268 lines = 0;
1269 break;
1270 }
1271 break;
1272 default:
22 office 1273 #if defined ___AsyncIO___
1274 if(WriteCharAsync(tp, (UBYTE)c) != 1) {
1275 #else
1 office 1276 if(fprintf(tp, "%c", c) != 1) {
22 office 1277 #endif
24 office 1278 fprintf(stderr, "Unable to write to '%s'.\n", tmpNames[tmpFiles]);
22 office 1279 #if defined ___AsyncIO___
1280 CloseAsync(tp);
1281 CloseAsync(fp);
1282 #else
1 office 1283 fclose(tp);
1284 fclose(fp);
22 office 1285 #endif
1 office 1286 return;
1287 }
1288 break;
1289 }
1290 }
1291  
24 office 1292 if(verbose) {
1293 fprintf(stdout, "\n");
1294 }
1 office 1295  
22 office 1296 #if defined ___AsyncIO___
1297 CloseAsync(tp);
1298 CloseAsync(fp);
1299 #else
1 office 1300 fclose(tp);
1301 fclose(fp);
22 office 1302 #endif
1 office 1303 }
1304  
1305 /*
1306 *
1307 * Merges temporary files "tmpNames" into a database "dbFile".
1308 */
24 office 1309 void MergeTemporaryFiles(char *dbFile, char **tmpNames, int files, int lines) {
22 office 1310 #if defined ___AsyncIO___
1311 struct AsyncFile *fp;
1312 struct AsyncFile **tp;
1313 #else
1 office 1314 FILE *fp;
1315 FILE **tp;
22 office 1316 #endif
31 office 1317 unsigned int i;
1318 unsigned int j;
1 office 1319 char *tmp;
26 office 1320 char *rem;
14 office 1321 char *min;
1 office 1322 int count;
1323  
22 office 1324 #if defined ___AsyncIO___
23 office 1325 if((fp = OpenAsync(dbFile, MODE_WRITE, ASYNC_BUF)) == NULL) {
22 office 1326 #else
23 office 1327 if((fp = fopen(dbFile, "w")) == NULL) {
22 office 1328 #endif
24 office 1329 fprintf(stderr, "Unable to open '%s' for writing.\n", dbFile);
1 office 1330 return;
1331 }
1332  
1333 // Allocate as many file pointers as temporary files.
26 office 1334 if((tp = malloc(files * sizeof(*tp))) == NULL) {
1335 fprintf(stderr, "Memory allocation failure.\n");
1336 #if defined ___AsyncIO___
1337 CloseAsync(fp);
1338 #else
1339 fclose(fp);
1340 #endif
1341 return;
1342 }
1 office 1343  
1344 // Open all temporary files for reading.
1345 for(i = 0; i < files; ++i) {
22 office 1346 #if defined ___AsyncIO___
1347 if((tp[i] = OpenAsync(tmpNames[i], MODE_READ, ASYNC_BUF)) == NULL) {
1348 #else
1 office 1349 if((tp[i] = fopen(tmpNames[i], "r")) == NULL) {
22 office 1350 #endif
24 office 1351 fprintf(stderr, "Unable to open '%s' for reading.\n", tmpNames[i]);
1 office 1352 // Close all temporary files.
24 office 1353 while(--i > -1) {
22 office 1354 #if defined ___AsyncIO___
1355 CloseAsync(tp[i]);
1356 #else
1 office 1357 fclose(tp[i]);
22 office 1358 #endif
1 office 1359 }
24 office 1360 #if defined ___AsyncIO___
1361 CloseAsync(fp);
1362 #else
1363 fclose(fp);
1364 #endif
1 office 1365 return;
1366 }
1367 }
1368  
1369 if(verbose) {
24 office 1370 fprintf(stdout, "Merging all files...\r");
1 office 1371 }
1372  
26 office 1373 rem = NULL;
1 office 1374 count = lines;
14 office 1375 j = 0;
1 office 1376 while(run && --count > -1) {
1377 #if defined ___AmigaOS___
1378 // Check if CTRL+C was pressed and abort the program.
1379 if(SetSignal(0L, SIGBREAKF_CTRL_C) & SIGBREAKF_CTRL_C) {
1380 run = FALSE;
23 office 1381 continue;
1 office 1382 }
1383 #endif
1384 // Find the smallest line in all temporary files.
1385 if(verbose) {
24 office 1386 fprintf(stdout, "Merging all files: %d%%.\r", 100 - (int)(((float)count / lines) * 100.0));
1 office 1387 }
1388  
14 office 1389 min = NULL;
1 office 1390 for(i = 0; i < files; ++i) {
26 office 1391 tmp = PeekLine(tp[i]);
1 office 1392 if(tmp == NULL) {
1393 continue;
1394 }
26 office 1395 #if defined ___AmigaOS___
1396 if(min == NULL || StrnCmp(locale, tmp, min, -1, SC_ASCII) < 0) {
1397 #else
27 office 1398 if(min == NULL || strcmp(tmp, min) < 0) {
26 office 1399 #endif
14 office 1400 if(min != NULL) {
2 office 1401 // Free previous instance.
14 office 1402 free(min);
2 office 1403 }
26 office 1404 if((min = malloc((strlen(tmp) + 1) * sizeof(*min))) == NULL) {
1405 fprintf(stderr, "Memory allication failure.\n");
1406 free(tmp);
1407 if(min != NULL) {
1408 free(min);
1409 }
1410 if(rem != NULL) {
1411 free(rem);
1412 }
1413 #if defined ___AsyncIO___
1414 CloseAsync(fp);
1415 #else
1416 fclose(fp);
1417 #endif
1418 return;
1419 }
14 office 1420 sprintf(min, "%s", tmp);
1 office 1421 // Remember the index of the file where the smallest entry has been found.
14 office 1422 j = i;
1 office 1423 }
1424 free(tmp);
1425 }
1426  
1427 // Forward the file where the smallest line was found.
23 office 1428 SkipLine(tp[j]);
1 office 1429  
1430 // Write the smallest line.
14 office 1431 if(min != NULL) {
26 office 1432 // If current minimum line is identical to previous minimum line then skip to remove duplicates.
1433 if(rem != NULL) {
1434 #if defined ___AmigaOS___
1435 if(StrnCmp(locale, min, rem, -1, SC_ASCII) == 0) {
1436 #else
28 office 1437 if(strcmp(min, rem) == 0) {
26 office 1438 #endif
1439 free(min);
1440 continue;
1441 }
1442 }
1443  
22 office 1444 #if defined ___AsyncIO___
26 office 1445 WriteAsync(fp, min, (LONG)strlen(min));
1446 WriteAsync(fp, "\n", 1);
22 office 1447 #else
14 office 1448 fprintf(fp, "%s\n", min);
22 office 1449 #endif
26 office 1450  
1451 if(rem != NULL) {
1452 free(rem);
1453 }
1454  
1455 if((rem = malloc((strlen(min) + 1) * sizeof(*rem))) == NULL) {
1456 fprintf(stderr, "Memory allocation failure.\n");
1457 free(min);
1458 #if defined ___AsyncIO___
1459 CloseAsync(fp);
1460 #else
1461 fclose(fp);
1462 #endif
1463 return;
1464 }
1465  
1466 sprintf(rem, "%s", min);
14 office 1467 free(min);
1 office 1468 }
1469 }
1470  
26 office 1471 if(rem != NULL) {
1472 free(rem);
1473 }
1474  
1 office 1475 // Write out any remaining contents from the temporary files.
24 office 1476 for(i = 0; run && i < files; ++i) {
1477 #if defined ___AmigaOS___
1478 // Check if CTRL+C was pressed and abort the program.
1479 if(SetSignal(0L, SIGBREAKF_CTRL_C) & SIGBREAKF_CTRL_C) {
1480 run = FALSE;
1481 continue;
1482 }
1483 #endif
23 office 1484 tmp = ReadLine(tp[i]);
1 office 1485 if(tmp == NULL) {
1486 continue;
1487 }
22 office 1488 #if defined ___AsyncIO___
26 office 1489 WriteAsync(fp, tmp, (LONG)strlen(tmp));
1490 WriteAsync(fp, "\n", 1);
22 office 1491 #else
1 office 1492 fprintf(fp, "%s\n", tmp);
22 office 1493 #endif
14 office 1494 free(tmp);
1 office 1495 }
1496  
24 office 1497 // Close all temporary files.
1 office 1498 for(i = 0; i < files; ++i) {
22 office 1499 #if defined ___AsyncIO___
1500 CloseAsync(tp[i]);
1501 #else
1 office 1502 fclose(tp[i]);
22 office 1503 #endif
1 office 1504 }
1505  
1506 if(verbose) {
1507 fprintf(stdout, "\n");
1508 }
1509  
22 office 1510 #if defined ___AsyncIO___
1511 CloseAsync(fp);
1512 #else
1 office 1513 fclose(fp);
22 office 1514 #endif
1 office 1515 }
1516  
1517 /*
26 office 1518 *
1519 * Filter the paths inside the database with provided paths.
1520 */
1521 void FilterDatabasePaths(char *dbFile, char *tmpName, char **paths, unsigned int count) {
1522 #if defined ___AsyncIO___
1523 struct AsyncFile *fp;
1524 struct AsyncFile *tp;
1525 #else
1526 FILE *fp;
1527 FILE *tp;
1528 #endif
1529 char *line;
31 office 1530 unsigned int lines;
26 office 1531 int i;
1532 dbEntry *entry;
24 office 1533  
26 office 1534 // Open database file for reading.
1535 #if defined ___AsyncIO___
1536 if((fp = OpenAsync(dbFile, MODE_READ, ASYNC_BUF)) == NULL) {
1537 #else
1538 if((fp = fopen(dbFile, "r")) == NULL) {
1539 #endif
1540 fprintf(stderr, "Unable to open '%s' for reading.\n", dbFile);
1541 return;
24 office 1542 }
1543  
26 office 1544 // Open temporary file for writing.
1545 #if defined ___AsyncIO___
1546 if((tp = OpenAsync(tmpName, MODE_WRITE, ASYNC_BUF)) == NULL) {
1547 #else
1548 if((tp = fopen(tmpName, "w")) == NULL) {
1549 #endif
1550 fprintf(stderr, "Unable to open '%s' for writing.\n", tmpName);
24 office 1551  
26 office 1552 // Close database file.
1553 #if defined ___AsyncIO___
1554 CloseAsync(fp);
1555 #else
1556 fclose(fp);
1557 #endif
1558  
1559 return;
24 office 1560 }
26 office 1561  
31 office 1562 if(verbose) {
1563 fprintf(stdout, "Removing lines...\r");
1564 }
1565  
1566 lines = 0;
26 office 1567 while(run && (line = ReadLine(fp)) != NULL) {
1568 #if defined ___AmigaOS___
1569 // Check if CTRL+C was pressed and abort the program.
1570 if(SetSignal(0L, SIGBREAKF_CTRL_C) & SIGBREAKF_CTRL_C) {
1571 run = FALSE;
1572 continue;
1573 }
1574 #endif
31 office 1575 if((entry = CreateDatabaseEntry(line)) == NULL) {
26 office 1576 fprintf(stderr, "Unable to create database entry.\n");
31 office 1577 free(line);
26 office 1578 continue;
1579 }
31 office 1580  
26 office 1581 for(i = 0; i < count; ++i) {
1582 if(PathCompare(entry->path, paths[i]) == TRUE) {
31 office 1583 ++lines;
1584 if(verbose) {
1585 fprintf(stdout, "Removing lines: %d\r", lines);
1586 }
26 office 1587 continue;
1588 }
1589 #if defined ___AsyncIO___
1590 WriteAsync(tp, line, (LONG)strlen(line));
1591 WriteAsync(tp, "\n", 1);
1592 #else
1593 fprintf(tp, "%s\n", line);
1594 #endif
1595 break;
1596 }
1597  
31 office 1598  
1599 free(entry->name);
1600 free(entry->path);
26 office 1601 free(entry);
31 office 1602  
26 office 1603 free(line);
1604 }
1605  
31 office 1606 if(verbose) {
1607 fprintf(stdout, "\n");
1608 }
26 office 1609  
31 office 1610  
26 office 1611 #if defined ___AsyncIO___
1612 CloseAsync(fp);
1613 CloseAsync(tp);
1614 #else
1615 fclose(fp);
1616 fclose(tp);
1617 #endif
24 office 1618 }
1619  
1620 /*
1621 *
1 office 1622 * Indexes a "path" by creating a database "dbFile".
1623 */
26 office 1624 void GatherDatabaseFiles(char *dbFile, char **paths, unsigned int count) {
1625 stats *stats;
1 office 1626 char **tmpNames;
26 office 1627 int dbSize;
1628 int dbLines;
1629 int tmpFiles;
1630 int tmpLines;
1 office 1631 int i;
1632  
26 office 1633 // Generate the database file from the supplied paths.
1634 if((stats = CollectFiles(dbFile, paths, count)) == NULL) {
1635 fprintf(stderr, "Collecting files failed.\n");
1636 return;
1637 }
1638 free(stats);
1 office 1639  
26 office 1640 // Compute the amount of temporary files needed.
23 office 1641 dbSize = GetFileSize(dbFile);
26 office 1642 if(dbSize == -1) {
1643 fprintf(stderr, "File size for '%s' failed.\n", dbFile);
1644 return;
1645 }
1646 tmpFiles = dbSize / maxmem;
1 office 1647  
26 office 1648 /* In case no temporary files are required,
1649 * just sort the database and terminate.
1650 */
2 office 1651 if(tmpFiles <= 1) {
1 office 1652 SortDatabase(dbFile);
1653 return;
1654 }
1655  
26 office 1656 // Get the database metrics.
1657 dbLines = CountFileLines(dbFile);
1658 if(dbLines == -1) {
1659 fprintf(stderr, "Counting lines of '%s' failed.\n", dbFile);
1660 }
1 office 1661 tmpLines = dbLines / tmpFiles;
1662  
1663 // Create temporary files.
23 office 1664 if((tmpNames = CreateTemporaryFiles(tmpFiles)) == NULL) {
1 office 1665 fprintf(stderr, "Unable to create temporary files.\n");
1666 return;
1667 }
1668  
24 office 1669 // Write "tmpLines" to temporary files in "tmpNames" from "dbFile".
23 office 1670 WriteTemporaryFiles(dbFile, tmpNames, tmpFiles, tmpLines, dbLines);
1 office 1671  
1672 // Sort the temporary files.
31 office 1673 for(i = 0; i < tmpFiles; ++i) {
1 office 1674 SortDatabase(tmpNames[i]);
1675 }
1676  
24 office 1677 // Merge all the temporary files to the database file.
1678 MergeTemporaryFiles(dbFile, tmpNames, tmpFiles, dbLines);
1679  
1680 // Remove all temporary files.
26 office 1681 RemoveFiles(tmpNames, tmpFiles);
1682  
31 office 1683 // Free temporary file names.
26 office 1684 free(tmpNames);
1 office 1685 }
1686  
26 office 1687 void RemoveDatabaseFiles(char *dbFile, char **paths, unsigned int count) {
1688 char *tmpName;
1689  
1690 // Create a temporary file to hold the changes.
1691 if((tmpName = CreateTemporaryFile()) == NULL) {
1692 fprintf(stderr, "Unable to create temporary file.\n");
1693 return;
1694 }
1695  
1696 // Filter the database of the provided paths.
1697 FilterDatabasePaths(dbFile, tmpName, paths, count);
1698  
1699 // Overwrite the database file with the filtered paths.
1700 CopyFile(tmpName, dbFile);
1701  
1702 // Remove temporary file.
31 office 1703 if(RemoveFile(tmpName) == FALSE) {
1704 fprintf(stderr, "Temporary file could not be removed.\n");
1705 return;
1706 }
26 office 1707 }
1708  
11 office 1709 void usage(char *name) {
1710 fprintf(stdout, "Hunt & Gather - %s, a file index generating tool. \n", name);
19 office 1711 fprintf(stdout, "Version: %s \n", PROGRAM_VERSION);
11 office 1712 fprintf(stdout, " \n");
26 office 1713 fprintf(stdout, "SYNTAX: %s [-q] <-a|-r|-c> <PATH PATH PATH...> \n", name);
1714 fprintf(stdout, " \n");
1715 fprintf(stdout, "Required: \n");
27 office 1716 fprintf(stdout, " -a [PATH...] Add files. \n");
1717 fprintf(stdout, " -c [PATH...] Create from scratch. \n");
26 office 1718 fprintf(stdout, " -r [PATH...] Remove files. \n");
1719 fprintf(stdout, " \n");
1720 fprintf(stdout, "Optional: \n");
1721 fprintf(stdout, " -d [FIILE] Where to store the database. \n");
1722 fprintf(stdout, " -m BYTES Memory to use (default: %d). \n", maxmem);
11 office 1723 fprintf(stdout, " -q Do not print out any messages. \n");
26 office 1724 fprintf(stdout, " \n");
11 office 1725 fprintf(stdout, "DATABASE is a path to where the indexed results will be \n");
1726 fprintf(stdout, "stored for searching with the Hunt tool. \n");
1727 fprintf(stdout, " \n");
1728 fprintf(stdout, "(c) 2021 Wizardry and Steamworks, MIT. \n");
1729 }
1730  
1 office 1731 /*
1732 *
1733 * Main entry point.
1734 */
1735 int main(int argc, char **argv) {
28 office 1736 #if defined ___AmigaOS___
1737 struct FileInfoBlock *FIB;
1738 BPTR lock;
1739 #else
1740 struct stat dirStat;
1741 #endif
1 office 1742 int option;
26 office 1743 unsigned int i;
1744 unsigned int count;
2 office 1745 char *dbFile;
19 office 1746 char *path;
26 office 1747 char **paths;
1 office 1748  
1749 // Bind handler to SIGINT.
26 office 1750 #if !defined ___AmigaOS___
1 office 1751 signal(SIGINT, SignalHandler);
26 office 1752 #endif
1 office 1753  
2 office 1754 dbFile = DEFAULT_DATABASE_FILE;
26 office 1755 while((option = getopt(argc, argv, "hqdm:arc")) != -1) {
1 office 1756 switch(option) {
26 office 1757 case 'a':
1758 operation = GATHER;
1759 break;
1760 case 'r':
1761 operation = REMOVE;
1762 break;
1763 case 'c':
1764 operation = CREATE;
1765 break;
1766 case 'm':
1767 maxmem = strtoul(optarg, NULL, 10);
1768 break;
2 office 1769 case 'd':
1770 dbFile = optarg;
1771 break;
1 office 1772 case 'q':
1773 verbose = FALSE;
1774 break;
1775 case 'h':
11 office 1776 usage(argv[0]);
2 office 1777 return 0;
1 office 1778 case '?':
1779 fprintf(stderr, "Invalid option %ct.\n", optopt);
1780 return 1;
1781 }
1782 }
1783  
26 office 1784 if(operation == NONE) {
1785 usage(argv[0]);
1786 return 1;
1787 }
10 office 1788  
1789 if(optind >= argc) {
11 office 1790 usage(argv[0]);
1 office 1791 return 1;
1792 }
1793  
26 office 1794 // Go through all supplied arguments and add paths to search.
1795 if((paths = malloc((argc - optind) * sizeof(*paths))) == NULL) {
1796 fprintf(stderr, "Memory allocation failure.\n");
1 office 1797 return 1;
1798 }
31 office 1799  
1800 count = 0;
1801 for(i = optind, count = 0; i < argc; ++i) {
28 office 1802 if((path = PathToAbsolute(argv[i])) == NULL) {
26 office 1803 fprintf(stderr, "Absolute path for '%s' failed to resolve.\n", argv[optind]);
1804 continue;
1805 }
1 office 1806  
26 office 1807 // Check that the path is a directory.
28 office 1808 #if defined ___AmigaOS___
1809 if((lock = Lock(path, ACCESS_READ)) == NULL) {
1810 fprintf(stderr, "Path '%s' is not accessible.\n", path);
1811 free(path);
1812 continue;
1813 }
1814  
1815 if((FIB = AllocDosObject(DOS_FIB, NULL)) == NULL) {
1816 fprintf(stderr, "Path '%s' file information block not accessible.\n", path);
1817 UnLock(lock);
1818 free(path);
1819 continue;
1820 }
1821  
1822 if(Examine(lock, FIB) == FALSE) {
1823 fprintf(stderr, "Path '%s' information unexaminable.\n", path);
1824 UnLock(lock);
1825 FreeDosObject(DOS_FIB, FIB);
1826 free(path);
1827 continue;
1828 }
1829  
1830 if(FIB->fib_DirEntryType < 0) {
1831 #else
26 office 1832 stat(path, &dirStat);
1833 if(!S_ISDIR(dirStat.st_mode)) {
28 office 1834 #endif
26 office 1835 fprintf(stderr, "Path '%s' is not a directory.\n", argv[optind]);
28 office 1836 #if defined ___AmigaOS___
1837 UnLock(lock);
1838 FreeDosObject(DOS_FIB, FIB);
1839 #endif
26 office 1840 free(path);
1841 return 1;
1842 }
1843  
1844 if(verbose) {
1845 fprintf(stdout, "Will process path: '%s'\n", path);
1846 }
1847  
1848 // Add the path to the array of paths.
30 office 1849 if((paths[count] = malloc((strlen(path) + 1) * sizeof(*paths[count]))) == NULL) {
26 office 1850 fprintf(stderr, "Memory allocation failure.");
1851 return 1;
1852 }
28 office 1853  
26 office 1854 sprintf(paths[count], "%s", path);
31 office 1855 ++count;
28 office 1856  
1857 #if defined ___AmigaOS___
1858 UnLock(lock);
1859 FreeDosObject(DOS_FIB, FIB);
1860 #endif
26 office 1861 free(path);
28 office 1862  
26 office 1863 }
1864  
31 office 1865 if(count == 0) {
1866 fprintf(stderr, "No valid paths are available.\n");
1867 free(paths);
1868 return 1;
1869 }
1870  
2 office 1871 if(verbose) {
26 office 1872 fprintf(stdout, "Gathering to: '%s'\n", dbFile);
2 office 1873 }
1874  
26 office 1875 #if defined ___AmigaOS___
1876 locale = OpenLocale(NULL);
1877 #endif
1878  
1879 switch(operation) {
1880 case CREATE:
1881 if(verbose) {
1882 fprintf(stdout, "Removing '%s' and creating a new database.\n", dbFile);
1883 }
31 office 1884 if(RemoveFile(dbFile) == FALSE) {
1885 fprintf(stderr, "File '%s' could not be removed.\n", dbFile);
1886 break;
1887 }
26 office 1888 case GATHER:
1889 if(verbose) {
1890 fprintf(stdout, "Gathering files to database...\n");
1891 }
1892 GatherDatabaseFiles(dbFile, paths, count);
1893 break;
1894 case REMOVE:
1895 if(verbose) {
1896 fprintf(stdout, "Removing files from database...\n");
1897 }
1898 RemoveDatabaseFiles(dbFile, paths, count);
1899 break;
1900 default:
31 office 1901 fprintf(stderr, "Unknown operation.\n");
26 office 1902 break;
1903 }
1 office 1904  
26 office 1905 #if defined ___AmigaOS___
1906 CloseLocale(locale);
1907 #endif
19 office 1908  
26 office 1909 free(paths);
1910  
31 office 1911 #if defined MWDEBUG
1912 /* Generate a memory usage report */
1913 MWReport("At end of main()", MWR_FULL);
1914 #endif
1915  
26 office 1916 return 0;
1 office 1917 }