HuntnGather – Blame information for rev 22

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>
8 #include <dirent.h>
9 #include <signal.h>
10  
11 #include <sys/types.h>
12 #include <sys/stat.h>
19 office 13 #include <sys/syslimits.h>
1 office 14  
15 #include <proto/dos.h>
16 #include <proto/exec.h>
17  
22 office 18 #if defined ___AsyncIO___
19 #include <asyncio.h>
20 #endif
21  
1 office 22 #include "StringStack.h"
23  
5 office 24 #if !defined ___HAVE_GETOPT___
1 office 25 #include "getopt.h"
26 #endif
27  
22 office 28 #define PROGRAM_VERSION "1.7.3"
19 office 29  
5 office 30 #if defined ___AmigaOS___
31 /*************************************************************************/
32 /* Version string used for querrying the program version. */
33 /*************************************************************************/
34 TEXT version_string[] =
19 office 35 "\0$VER: Gather " PROGRAM_VERSION " "__DATE__" by Wizardry and Steamworks";
5 office 36 #endif
1 office 37  
38 #if !defined TRUE
39 #define TRUE 1;
40 #endif
41  
42 #if !defined FALSE
43 #define FALSE 0;
44 #endif
45  
22 office 46 #define ASYNC_BUF 8192
2 office 47 #define MAX_MEM 262144
11 office 48 #define NAME_BUF 32
49 #define PATH_BUF 128
50 #define LINE_BUF 256
2 office 51 #define DEFAULT_DATABASE_FILE "S:gather.db"
1 office 52  
53 typedef struct {
54 unsigned int dirs;
55 unsigned int files;
56 } stats;
57  
58 int run = TRUE;
59 int verbose = TRUE;
60  
61 void SignalHandler(int sig) {
62 // Toggle the run flag to stop execution.
63 run = FALSE;
64 }
65  
66 int compare(const void *a, const void *b) {
67 const char **p = (const char **)a;
68 const char **q = (const char **)b;
13 office 69 return strncmp(*p, *q, strlen(*p));
1 office 70 }
71  
72 /*
73 *
74 * Sorts a database file lexicographically.
75 */
76 void SortDatabase(char *dbFile) {
22 office 77 #if defined ___AsyncIO___
78 struct AsyncFile *fp;
79 LONG c;
80 #else
1 office 81 FILE *fp;
22 office 82 char c;
83 #endif
1 office 84 char *name = NULL;
85 char *path = NULL;
86 char **database;
87 int i;
88 int side;
89 unsigned int line;
11 office 90 int name_size;
91 int path_size;
1 office 92  
93 // Open database file for reading.
22 office 94 #if defined ___AsyncIO___
95 if((fp = OpenAsync(dbFile, MODE_READ, ASYNC_BUF)) == NULL) {
96 #else
1 office 97 if((fp = fopen(dbFile, "r")) == NULL) {
22 office 98 #endif
1 office 99 fprintf(stderr, "Unable to open gather database for reading.\n");
100 return;
101 }
102  
19 office 103 database = malloc(sizeof(*database));
11 office 104 name_size = NAME_BUF;
19 office 105 name = malloc(name_size * sizeof(*name));
11 office 106 path_size = PATH_BUF;
19 office 107 path = malloc(path_size * sizeof(*path));
22 office 108  
1 office 109 line = 0;
110 side = 0;
111 i = 0;
112  
113 if(verbose) {
114 fprintf(stdout, "Sorting database: '%s'\n", dbFile);
115 }
22 office 116 #if defined ___AsyncIO___
117 while(run && (c = ReadCharAsync(fp)) != -1) {
118 #else
1 office 119 while(run && fscanf(fp, "%c", &c) == 1) {
22 office 120 #endif
1 office 121 #if defined ___AmigaOS___
122 // Check if CTRL+C was pressed and abort the program.
123 if(SetSignal(0L, SIGBREAKF_CTRL_C) & SIGBREAKF_CTRL_C) {
124 run = FALSE;
125 continue;
126 }
127 #endif
128 switch(c) {
129 case '\n':
130 // Load up the name and path into the database variable.
19 office 131 database = realloc(database, (line + 1) * sizeof(*database));
132 database[line] = malloc((strlen(name) + strlen(path) + 1 + 1) * sizeof(*database[line]));
1 office 133 sprintf(database[line], "%s\t%s", name, path);
134 ++line;
135  
136 free(name);
11 office 137 name_size = NAME_BUF;
19 office 138 name = malloc(name_size * sizeof(*name));
1 office 139 --side;
140 i = 0;
141  
142 break;
143 case '\t':
144 free(path);
11 office 145 path_size = PATH_BUF;
19 office 146 path = malloc(path_size * sizeof(*path));
1 office 147 ++side;
148 i = 0;
149 break;
150 default:
151 switch(side) {
152 case 0:
11 office 153 if(strlen(name) == name_size) {
154 name_size = name_size * 1.5;
19 office 155 name = realloc(name, name_size * sizeof(*name));
11 office 156 }
157 //name = realloc(name, (i + 1 + 1) * sizeof(char));
1 office 158 name[i] = c;
159 name[i + 1] = '\0';
160 break;
161 case 1:
11 office 162 if(strlen(path) == path_size) {
163 path_size = path_size * 1.5;
19 office 164 path = realloc(path, path_size * sizeof(*path));
11 office 165 }
166 //path = realloc(path, (i + 1 + 1) * sizeof(char));
1 office 167 path[i] = c;
168 path[i + 1] = '\0';
169 break;
170 default:
22 office 171 fprintf(stderr, "Database corrupted: %d\n", side);
1 office 172 break;
173 }
174 ++i;
175 break;
176 }
177 }
178  
22 office 179 #if defined ___AsyncIO___
180 CloseAsync(fp);
181 #else
1 office 182 fclose(fp);
22 office 183 #endif
1 office 184  
185 // Sort the database.
186 qsort(database, line, sizeof(char *), compare);
187  
188 // Write the database lines back to the database.
22 office 189 #if defined ___AsyncIO___
190 if((fp = OpenAsync(dbFile, MODE_READ|MODE_WRITE, ASYNC_BUF)) == NULL) {
191 #else
1 office 192 if((fp = fopen(dbFile, "w+")) == NULL) {
22 office 193 #endif
1 office 194 fprintf(stderr, "Unable to open gather database for writing.\n");
195 return;
196 }
197  
198 for(i = 0; i < line; ++i) {
22 office 199 #if defined ___AsyncIO___
200 WriteAsync(fp, database[i], (LONG)(strlen(database[i]) * sizeof(char)));
201 WriteAsync(fp, "\n", 1 * sizeof(char));
202 #else
1 office 203 fprintf(fp, "%s\n", database[i]);
22 office 204 #endif
1 office 205 }
206  
22 office 207 #if defined ___AsyncIO___
208 CloseAsync(fp);
209 #else
210 fclose(fp);
211 #endif
212  
1 office 213 free(database);
214 }
215  
216 /*
217 *
218 * Updates a database file "dbFile".
219 */
220 void UpdateDatabase(char *dbFile, stringStack *dirStack, stats *stats) {
22 office 221 #if defined ___AsyncIO___
222 struct AsyncFile *fp;
223 #else
1 office 224 FILE *fp;
22 office 225 #endif
1 office 226 DIR *dir;
227 struct dirent *dirEntry;
228 struct stat dirStat;
229 unsigned int size;
230 char *path;
231 char *subPath;
232  
22 office 233 #if defined ___AsyncIO___
234 if((fp = OpenAsync(dbFile, MODE_READ|MODE_WRITE, ASYNC_BUF)) == NULL) {
235 #else
1 office 236 if((fp = fopen(dbFile, "w+")) == NULL) {
22 office 237 #endif
1 office 238 fprintf(stderr, "Unable to open gather database for writing.\n");
239 return;
240 }
241  
242 while(run && !stringStackIsEmpty(dirStack)) {
243 #if defined ___AmigaOS___
244 // Check if CTRL+C was pressed and abort the program.
245 if(SetSignal(0L, SIGBREAKF_CTRL_C) & SIGBREAKF_CTRL_C) {
246 run = FALSE;
247 }
248 #endif
19 office 249 if((path = stringStackPop(dirStack)) == NULL) {
1 office 250 return;
251 }
252  
253 if((dir = opendir(path)) == NULL) {
254 return;
255 }
256  
257 while(run && (dirEntry = readdir(dir)) != NULL) {
258 #if defined ___AmigaOS___
259 // Check if CTRL+C was pressed and abort the program.
260 if(SetSignal(0L, SIGBREAKF_CTRL_C) & SIGBREAKF_CTRL_C) {
261 run = FALSE;
262 }
263 #endif
264 size = sizeof(path) + sizeof(dirEntry->d_name) + 1;
265 switch(path[strlen(path) - 1]) {
266 case '/':
267 case ':': // This is a drive path.
11 office 268 subPath = malloc(size);
1 office 269 sprintf(subPath, "%s%s", path, dirEntry->d_name);
270 break;
271 default:
11 office 272 subPath = malloc(size + 1);
1 office 273 sprintf(subPath, "%s/%s", path, dirEntry->d_name);
274 break;
275 }
276 stat(subPath, &dirStat);
277 if(S_ISDIR(dirStat.st_mode)) {
278 stringStackPush(dirStack, subPath);
279  
280 ++stats->dirs;
281  
282 if(verbose) {
283 fprintf(stdout,
284 "Gathered %d directories and %d files.\r",
285 stats->dirs,
286 stats->files);
287 }
288  
289 free(subPath);
290 continue;
291 }
292  
293 // Write to database file.
15 office 294  
295 #if defined ___NOCASE_FS___
296 strupr(dirEntry->d_name);
297 #endif
298  
22 office 299 #if defined ___AsyncIO___
300 WriteAsync(fp, dirEntry->d_name, (LONG)(strlen(dirEntry->d_name) * sizeof(char)));
301 WriteAsync(fp, "\t", 1 * sizeof(char));
302 WriteAsync(fp, subPath, (LONG)(strlen(subPath) * sizeof(char)));
303 WriteAsync(fp, "\n", 1 * sizeof(char));
304 #else
1 office 305 fprintf(fp, "%s\t%s\n", dirEntry->d_name, subPath);
22 office 306 #endif
1 office 307 ++stats->files;
308  
309 if(verbose) {
310 fprintf(stdout,
311 "Gathered %d directories and %d files.\r",
312 stats->dirs,
313 stats->files);
314 }
315  
316 free(subPath);
317 }
318  
319 closedir(dir);
320 free(path);
321 }
322  
323 if(verbose) {
324 fprintf(stdout, "\n");
325 }
326  
22 office 327 #if defined ___AsyncIO___
328 CloseAsync(fp);
329 #else
1 office 330 fclose(fp);
22 office 331 #endif
1 office 332  
333 }
334  
335 /*
336 *
337 * Gets the size of a database "dbFle".
338 */
339 int GetDatabaseSize(char *dbFile) {
340 FILE *fp;
341 int size;
342  
343 if((fp = fopen(dbFile, "r")) == NULL) {
344 fprintf(stderr, "Unable to open gather database for reading.\n");
19 office 345 fclose(fp);
1 office 346 return 0;
347 }
348  
349 fseek(fp, 0L, SEEK_END);
350 size = ftell(fp);
351  
352 fclose(fp);
353 return size;
354 }
355  
356 /*
357 *
358 * Counts the lines in a database file "dbFile".
359 */
360 int CountDatabaseLines(char *dbFile) {
22 office 361 #if defined ___AsyncIO___
362 struct AsyncFile *fp;
363 LONG c;
364 #else
1 office 365 FILE *fp;
22 office 366 char c;
367 #endif
1 office 368 int lines;
369  
22 office 370 #if defined ___AsyncIO___
371 if((fp = OpenAsync(dbFile, MODE_READ, ASYNC_BUF)) == NULL) {
372 #else
1 office 373 if((fp = fopen(dbFile, "r")) == NULL) {
22 office 374 #endif
1 office 375 fprintf(stderr, "Unable to open gather database for reading.\n");
376 return 0;
377 }
378  
379 lines = 0;
22 office 380 #if defined ___AsyncIO___
381 while((c = ReadCharAsync(fp)) != -1) {
382 #else
1 office 383 while(fscanf(fp, "%c", &c) == 1) {
22 office 384 #endif
1 office 385 switch(c) {
386 case '\n':
387 ++lines;
388 break;
389 }
390 }
391  
22 office 392 #if defined ___AsyncIO___
393 CloseAsync(fp);
394 #else
1 office 395 fclose(fp);
22 office 396 #endif
1 office 397  
398 return lines;
399 }
400  
401 /*
402 *
403 * Creates "files" temporary filenames.
404 */
405 char **CreateTempFiles(int files) {
406 char **tmpNames;
407 int count;
408  
11 office 409 tmpNames = malloc(files * sizeof(char *));
1 office 410  
411 if(verbose) {
412 fprintf(stdout, "Creating temporary files.\r");
413 }
414  
415 count = files;
416 while(--count > -1) {
417 #if defined ___AmigaOS___
418 // Check if CTRL+C was pressed and abort the program.
419 if(SetSignal(0L, SIGBREAKF_CTRL_C) & SIGBREAKF_CTRL_C) {
420 run = FALSE;
421 }
422 #endif
423 tmpNames[count] = tmpnam(NULL);
424  
425 if(verbose) {
426 fprintf(stdout, "Creating temporary files: %d%%\r", 100 - (int)(((float)count / files) * 100.0));
427 }
428 }
429  
430 if(verbose) {
431 fprintf(stdout, "\n");
432 }
433  
434 return tmpNames;
435 }
436  
437 /*
438 *
439 * Writes lines from the database "dbFile" to temporary filenames "tmpNames".
440 */
441 void WriteTempFiles(char *dbFile, char **tmpNames, int tmpFiles, int tmpLines, int total) {
22 office 442 #if defined ___AsyncIO___
443 struct AsyncFile *fp, *tp;
444 LONG c;
445 #else
1 office 446 FILE *fp, *tp;
447 char c;
22 office 448 #endif
1 office 449 int lines;
450 int linesWritten;
451  
22 office 452 #if defined ___AsyncIO___
453 if((fp = OpenAsync(dbFile, MODE_READ, ASYNC_BUF)) == NULL) {
454 #else
1 office 455 if((fp = fopen(dbFile, "r")) == NULL) {
22 office 456 #endif
1 office 457 fprintf(stderr, "Unable to open gather database for reading.\n");
458 return;
459 }
460  
22 office 461 #if defined ___AsyncIO___
462 if((tp = OpenAsync(tmpNames[--tmpFiles], MODE_READ|MODE_WRITE, ASYNC_BUF)) == NULL) {
463 #else
1 office 464 if((tp = fopen(tmpNames[--tmpFiles], "w+")) == NULL) {
22 office 465 #endif
1 office 466 fprintf(stderr, "Unable to open temporary file '%s' for writing.\n", tmpNames[tmpFiles]);
467 return;
468 }
469  
470 if(verbose) {
471 fprintf(stdout, "Writing to temporary files.\r");
472 }
473  
474 linesWritten = 0;
475 lines = 0;
22 office 476 #if defined ___AsyncIO___
477 while(run && (c = ReadCharAsync(fp)) != -1) {
478 #else
1 office 479 while(run && fscanf(fp, "%c", &c) == 1) {
22 office 480 #endif
1 office 481 #if defined ___AmigaOS___
482 // Check if CTRL+C was pressed and abort the program.
483 if(SetSignal(0L, SIGBREAKF_CTRL_C) & SIGBREAKF_CTRL_C) {
484 run = FALSE;
485 }
486 #endif
487 switch(c) {
488 case '\n':
489 // Increment the total written lines.
490 ++linesWritten;
491  
492 if(verbose) {
493 fprintf(stdout, "Writing to temporary files: %d%%.\r", (int)(((float)linesWritten / total) * 100.0));
494 }
495  
496 // Write the newline character back.
22 office 497 #if defined ___AsyncIO___
498 if(WriteCharAsync(tp, (UBYTE)c) != 1) {
499 #else
1 office 500 if(fprintf(tp, "%c", c) != 1) {
22 office 501 #endif
1 office 502 fprintf(stderr, "Unable to write to temporary file '%s'.\n", tmpNames[tmpFiles]);
22 office 503 #if defined ___AsyncIO___
504 CloseAsync(tp);
505 CloseAsync(fp);
506 #else
19 office 507 fclose(tp);
1 office 508 fclose(fp);
22 office 509 #endif
1 office 510 return;
511 }
512 // Switch to the next temporary file.
513 if(++lines >= tmpLines) {
514 // If there are no temporary files left then run till the end.
515 if(tmpFiles - 1 < 0) {
516 break;
517 }
518  
519 // Close the previous temporary file and write to the next temporary file.
22 office 520 #if defined ___AsyncIO___
521 CloseAsync(tp);
522 if((tp = OpenAsync(tmpNames[--tmpFiles], MODE_READ|MODE_WRITE, ASYNC_BUF)) == NULL) {
523 #else
1 office 524 fclose(tp);
525 if((tp = fopen(tmpNames[--tmpFiles], "w+")) == NULL) {
22 office 526 #endif
1 office 527 fprintf(stderr, "Unable to open temporary file '%s' for writing.\n", tmpNames[tmpFiles]);
22 office 528 #if defined ___AsyncIO___
529 CloseAsync(tp);
530 CloseAsync(fp);
531 #else
19 office 532 fclose(tp);
1 office 533 fclose(fp);
22 office 534 #endif
1 office 535 }
536 lines = 0;
537 break;
538 }
539 break;
540 default:
22 office 541 #if defined ___AsyncIO___
542 if(WriteCharAsync(tp, (UBYTE)c) != 1) {
543 #else
1 office 544 if(fprintf(tp, "%c", c) != 1) {
22 office 545 #endif
1 office 546 fprintf(stderr, "Unable to write to temporary file '%s'.\n", tmpNames[tmpFiles]);
22 office 547 #if defined ___AsyncIO___
548 CloseAsync(tp);
549 CloseAsync(fp);
550 #else
1 office 551 fclose(tp);
552 fclose(fp);
22 office 553 #endif
1 office 554 return;
555 }
556 break;
557 }
558 }
559  
560 fprintf(stdout, "\n");
561  
22 office 562 #if defined ___AsyncIO___
563 CloseAsync(tp);
564 CloseAsync(fp);
565 #else
1 office 566 fclose(tp);
567 fclose(fp);
22 office 568 #endif
1 office 569 }
570  
571 /*
572 *
573 * Skips a line in a database file "fp".
574 */
22 office 575  
576 #if defined ___AsyncIO___
577 void SkipDatabaseLine(struct AsyncFile *fp) {
578 LONG c;
579 while((c = ReadCharAsync(fp)) != -1) {
580 #else
1 office 581 void SkipDatabaseLine(FILE *fp) {
582 char c;
583 while(fscanf(fp, "%c", &c) == 1) {
22 office 584 #endif
585 switch(c) {
586 case '\n':
587 return;
1 office 588 }
589 }
590 }
591  
592 /*
593 *
594 * Reads a line from the database file "fp".
595 */
22 office 596 #if defined ___AsyncIO___
597 char *ReadDatabaseLine(struct AsyncFile *fp) {
598 LONG c;
599 #else
1 office 600 char *ReadDatabaseLine(FILE *fp) {
601 char c;
22 office 602 #endif
1 office 603 char *line;
11 office 604 int line_size;
605 int i;
1 office 606  
11 office 607 line_size = LINE_BUF;
19 office 608 line = malloc(line_size * sizeof(*line));
1 office 609  
11 office 610 i = 0;
22 office 611 #if defined ___AsyncIO___
612 while(run && (c = ReadCharAsync(fp)) != -1) {
613 #else
1 office 614 while(run && fscanf(fp, "%c", &c) == 1) {
22 office 615 #endif
1 office 616 #if defined ___AmigaOS___
617 // Check if CTRL+C was pressed and abort the program.
618 if(SetSignal(0L, SIGBREAKF_CTRL_C) & SIGBREAKF_CTRL_C) {
619 run = FALSE;
620 }
621 #endif
622 switch(c) {
623 case '\n':
624 // Rewind the file by the number of read characters.
22 office 625 #if defined ___AsyncIO___
626 if(SeekAsync(fp, -(i + 1), MODE_CURRENT) == -1) {
627 fprintf(stderr, "Could not seek in file.\n");
628 return NULL;
629 }
630 #else
11 office 631 fseek(fp, -(i + 1), SEEK_CUR);
22 office 632 #endif
1 office 633 return line;
634 default:
11 office 635 if(strlen(line) == line_size) {
636 line_size = line_size * 1.5;
19 office 637 line = realloc(line, line_size * sizeof(*line));
11 office 638 }
639 //line = realloc(line, (chars + 1 + 1) * sizeof(char));
640 line[i] = c;
641 line[i + 1] = '\0';
1 office 642 break;
643 }
11 office 644 ++i;
1 office 645 }
646  
647 return NULL;
648 }
649  
650 /*
651 *
652 * Merges temporary files "tmpNames" into a database "dbFile".
653 */
654 void MergeDatabase(char *dbFile, char **tmpNames, int files, int lines) {
22 office 655 #if defined ___AsyncIO___
656 struct AsyncFile *fp;
657 struct AsyncFile **tp;
658 #else
1 office 659 FILE *fp;
660 FILE **tp;
22 office 661 #endif
1 office 662 int i;
14 office 663 int j;
1 office 664 char *tmp;
14 office 665 char *min;
1 office 666 int count;
667  
22 office 668 #if defined ___AsyncIO___
669 if((fp = OpenAsync(dbFile, MODE_READ|MODE_WRITE, ASYNC_BUF)) == NULL) {
670 #else
1 office 671 if((fp = fopen(dbFile, "w+")) == NULL) {
22 office 672 #endif
1 office 673 fprintf(stderr, "Unable to open gather database for writing.\n");
674 return;
675 }
676  
677 // Allocate as many file pointers as temporary files.
19 office 678 tp = malloc(files * sizeof(*tp));
1 office 679  
680 // Open all temporary files for reading.
681 for(i = 0; i < files; ++i) {
22 office 682 #if defined ___AsyncIO___
683 if((tp[i] = OpenAsync(tmpNames[i], MODE_READ, ASYNC_BUF)) == NULL) {
684 #else
1 office 685 if((tp[i] = fopen(tmpNames[i], "r")) == NULL) {
22 office 686 #endif
1 office 687 fprintf(stderr, "Unable to open temporary file '%s' for reading.\n", tmpNames[i]);
688 // Close all temporary files.
689 --i;
690 while(i >= 0) {
22 office 691 #if defined ___AsyncIO___
692 CloseAsync(tp[i]);
693 #else
1 office 694 fclose(tp[i]);
22 office 695 #endif
1 office 696 }
697 return;
698 }
699 }
700  
701 if(verbose) {
702 fprintf(stdout, "Merging all database lines in temporary files.\r");
703 }
704  
705 count = lines;
14 office 706 j = 0;
1 office 707 while(run && --count > -1) {
708 #if defined ___AmigaOS___
709 // Check if CTRL+C was pressed and abort the program.
710 if(SetSignal(0L, SIGBREAKF_CTRL_C) & SIGBREAKF_CTRL_C) {
711 run = FALSE;
712 }
713 #endif
714 // Find the smallest line in all temporary files.
715 if(verbose) {
716 fprintf(stdout, "Merging all database lines in temporary files: %d%%.\r", 100 - (int)(((float)count / lines) * 100.0));
717 }
718  
14 office 719 min = NULL;
1 office 720 for(i = 0; i < files; ++i) {
721 tmp = ReadDatabaseLine(tp[i]);
722 if(tmp == NULL) {
723 continue;
724 }
14 office 725 if(min == NULL || strncmp(tmp, min, strlen(tmp)) < 0) {
726 if(min != NULL) {
2 office 727 // Free previous instance.
14 office 728 free(min);
2 office 729 }
19 office 730 min = malloc((strlen(tmp) + 1) * sizeof(*min));
14 office 731 sprintf(min, "%s", tmp);
1 office 732 // Remember the index of the file where the smallest entry has been found.
14 office 733 j = i;
1 office 734 free(tmp);
735 continue;
736 }
737 free(tmp);
738 }
739  
740 // Forward the file where the smallest line was found.
14 office 741 SkipDatabaseLine(tp[j]);
1 office 742  
743 // Write the smallest line.
14 office 744 if(min != NULL) {
22 office 745 #if defined ___AsyncIO___
746 WriteAsync(fp, min, (LONG)(strlen(min) * sizeof(char)));
747 WriteAsync(fp, "\n", 1 * sizeof(char));
748 #else
14 office 749 fprintf(fp, "%s\n", min);
22 office 750 #endif
14 office 751 free(min);
1 office 752 }
753 }
754  
755 // Write out any remaining contents from the temporary files.
756 for(i = 0; i < files; ++i) {
757 tmp = ReadDatabaseLine(tp[i]);
758 if(tmp == NULL) {
759 continue;
760 }
22 office 761 #if defined ___AsyncIO___
762 WriteAsync(fp, tmp, (LONG)(strlen(tmp) * sizeof(char)));
763 WriteAsync(fp, "\n", 1 * sizeof(char));
764 #else
1 office 765 fprintf(fp, "%s\n", tmp);
22 office 766 #endif
14 office 767 free(tmp);
1 office 768 }
769  
770 // Close and delete all temporary files.
771 for(i = 0; i < files; ++i) {
22 office 772 #if defined ___AsyncIO___
773 CloseAsync(tp[i]);
774 #else
1 office 775 fclose(tp[i]);
22 office 776 #endif
1 office 777 // Delete temporary file.
778 remove(tmpNames[i]);
779 }
780  
781 if(verbose) {
782 fprintf(stdout, "\n");
783 }
784  
22 office 785 #if defined ___AsyncIO___
786 CloseAsync(fp);
787 #else
1 office 788 fclose(fp);
22 office 789 #endif
1 office 790 }
791  
792 /*
793 *
794 * Indexes a "path" by creating a database "dbFile".
795 */
796 void Gather(char *dbFile, char *path) {
797 stringStack *stack = stringStackCreate(1);
798 stats *stats = malloc(sizeof(stats));
799 char **tmpNames;
800 int dbSize, dbLines, tmpFiles, tmpLines;
801 int i;
802  
803 // Initialize metrics.
804 stats->dirs = 0;
805 stats->files = 0;
806  
807 // Push the first path onto the stack.
808 stringStackPush(stack, path);
809  
810 // Generate the database file.
811 UpdateDatabase(dbFile, stack, stats);
812  
813 // Get the database metrics.
814 dbSize = GetDatabaseSize(dbFile);
815 dbLines = CountDatabaseLines(dbFile);
816  
817 // Compute the amount of temporary files needed.
818 tmpFiles = dbSize / MAX_MEM;
819  
820 // In case no temporary files are required,
821 // just sort the database and terminate.
2 office 822 if(tmpFiles <= 1) {
1 office 823 SortDatabase(dbFile);
824 return;
825 }
826  
827 tmpLines = dbLines / tmpFiles;
828  
829 // Create temporary files.
830 if((tmpNames = CreateTempFiles(tmpFiles)) == NULL) {
831 fprintf(stderr, "Unable to create temporary files.\n");
832 return;
833 }
834  
835 // Write "tmpLines" to temporary files in "tmpFiles" from "dbFile".
836 WriteTempFiles(dbFile, tmpNames, tmpFiles, tmpLines, dbLines);
837  
838 // Sort the temporary files.
839 for(i = 0; i < tmpFiles; ++i) {
840 SortDatabase(tmpNames[i]);
841 }
842  
843 MergeDatabase(dbFile, tmpNames, tmpFiles, dbLines);
844 }
845  
11 office 846 void usage(char *name) {
847 fprintf(stdout, "Hunt & Gather - %s, a file index generating tool. \n", name);
19 office 848 fprintf(stdout, "Version: %s \n", PROGRAM_VERSION);
11 office 849 fprintf(stdout, " \n");
850 fprintf(stdout, "SYNTAX: %s [-q] DATABASE \n", name);
851 fprintf(stdout, " \n");
852 fprintf(stdout, " -q Do not print out any messages. \n");
853 fprintf(stdout, " \n");
854 fprintf(stdout, "DATABASE is a path to where the indexed results will be \n");
855 fprintf(stdout, "stored for searching with the Hunt tool. \n");
856 fprintf(stdout, " \n");
857 fprintf(stdout, "(c) 2021 Wizardry and Steamworks, MIT. \n");
858 }
859  
1 office 860 /*
861 *
862 * Main entry point.
863 */
864 int main(int argc, char **argv) {
865 int option;
2 office 866 char *dbFile;
19 office 867 char *path;
10 office 868 struct stat dirStat;
19 office 869 #if defined ___AmigaOS___
870 BPTR lock;
871 #endif
1 office 872  
873 // Bind handler to SIGINT.
874 signal(SIGINT, SignalHandler);
875  
2 office 876 dbFile = DEFAULT_DATABASE_FILE;
877 while((option = getopt(argc, argv, "hqd:")) != -1) {
1 office 878 switch(option) {
2 office 879 case 'd':
880 dbFile = optarg;
881 break;
1 office 882 case 'q':
883 verbose = FALSE;
884 break;
885 case 'h':
11 office 886 usage(argv[0]);
2 office 887 return 0;
1 office 888 case '?':
889 fprintf(stderr, "Invalid option %ct.\n", optopt);
890 return 1;
891 }
892 }
893  
10 office 894  
895 if(optind >= argc) {
11 office 896 usage(argv[0]);
1 office 897 return 1;
898 }
899  
19 office 900 #if defined ___AmigaOS___
901 path = malloc(PATH_MAX * sizeof(*path));
902 lock = Lock(argv[optind], SHARED_LOCK);
903 NameFromLock(lock, path, PATH_MAX);
904 UnLock(lock);
905 #else
906 path = realpath(argv[optind], NULL);
907 #endif
908  
909 stat(path, &dirStat);
10 office 910 if(!S_ISDIR(dirStat.st_mode)) {
5 office 911 fprintf(stderr, "Path '%s' is not a directory.\n", argv[optind]);
1 office 912 return 1;
913 }
914  
2 office 915 if(verbose) {
916 fprintf(stdout, "Gathering to database file: %s\n", dbFile);
917 }
918  
1 office 919 // Gather.
19 office 920 Gather(dbFile, path);
1 office 921  
19 office 922 free(path);
923  
1 office 924 return 0;
925 }