HuntnGather – Blame information for rev 23

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) {
23 office 114 fprintf(stdout, "Sorting temporary database file: '%s'\n", dbFile);
1 office 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___
23 office 190 if((fp = OpenAsync(dbFile, MODE_WRITE, ASYNC_BUF)) == NULL) {
22 office 191 #else
23 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) {
23 office 199 #if defined ___AmigaOS___
200 // Check if CTRL+C was pressed and abort the program.
201 if(SetSignal(0L, SIGBREAKF_CTRL_C) & SIGBREAKF_CTRL_C) {
202 run = FALSE;
203 continue;
204 }
205 #endif
22 office 206 #if defined ___AsyncIO___
207 WriteAsync(fp, database[i], (LONG)(strlen(database[i]) * sizeof(char)));
208 WriteAsync(fp, "\n", 1 * sizeof(char));
209 #else
1 office 210 fprintf(fp, "%s\n", database[i]);
22 office 211 #endif
1 office 212 }
213  
22 office 214 #if defined ___AsyncIO___
215 CloseAsync(fp);
216 #else
217 fclose(fp);
218 #endif
219  
1 office 220 free(database);
221 }
222  
223 /*
224 *
225 * Updates a database file "dbFile".
226 */
227 void UpdateDatabase(char *dbFile, stringStack *dirStack, stats *stats) {
22 office 228 #if defined ___AsyncIO___
229 struct AsyncFile *fp;
230 #else
1 office 231 FILE *fp;
22 office 232 #endif
1 office 233 DIR *dir;
234 struct dirent *dirEntry;
235 struct stat dirStat;
236 unsigned int size;
237 char *path;
238 char *subPath;
239  
22 office 240 #if defined ___AsyncIO___
23 office 241 if((fp = OpenAsync(dbFile, MODE_WRITE, ASYNC_BUF)) == NULL) {
22 office 242 #else
23 office 243 if((fp = fopen(dbFile, "w")) == NULL) {
22 office 244 #endif
1 office 245 fprintf(stderr, "Unable to open gather database for writing.\n");
246 return;
247 }
248  
249 while(run && !stringStackIsEmpty(dirStack)) {
250 #if defined ___AmigaOS___
251 // Check if CTRL+C was pressed and abort the program.
252 if(SetSignal(0L, SIGBREAKF_CTRL_C) & SIGBREAKF_CTRL_C) {
253 run = FALSE;
23 office 254 continue;
1 office 255 }
256 #endif
19 office 257 if((path = stringStackPop(dirStack)) == NULL) {
1 office 258 return;
259 }
260  
261 if((dir = opendir(path)) == NULL) {
262 return;
263 }
264  
265 while(run && (dirEntry = readdir(dir)) != NULL) {
266 #if defined ___AmigaOS___
267 // Check if CTRL+C was pressed and abort the program.
268 if(SetSignal(0L, SIGBREAKF_CTRL_C) & SIGBREAKF_CTRL_C) {
269 run = FALSE;
23 office 270 continue;
1 office 271 }
272 #endif
273 size = sizeof(path) + sizeof(dirEntry->d_name) + 1;
274 switch(path[strlen(path) - 1]) {
275 case '/':
276 case ':': // This is a drive path.
11 office 277 subPath = malloc(size);
1 office 278 sprintf(subPath, "%s%s", path, dirEntry->d_name);
279 break;
280 default:
11 office 281 subPath = malloc(size + 1);
1 office 282 sprintf(subPath, "%s/%s", path, dirEntry->d_name);
283 break;
284 }
285 stat(subPath, &dirStat);
286 if(S_ISDIR(dirStat.st_mode)) {
287 stringStackPush(dirStack, subPath);
288  
289 ++stats->dirs;
290  
291 if(verbose) {
292 fprintf(stdout,
293 "Gathered %d directories and %d files.\r",
294 stats->dirs,
295 stats->files);
296 }
297  
298 free(subPath);
299 continue;
300 }
301  
302 // Write to database file.
15 office 303  
304 #if defined ___NOCASE_FS___
305 strupr(dirEntry->d_name);
306 #endif
307  
22 office 308 #if defined ___AsyncIO___
309 WriteAsync(fp, dirEntry->d_name, (LONG)(strlen(dirEntry->d_name) * sizeof(char)));
310 WriteAsync(fp, "\t", 1 * sizeof(char));
311 WriteAsync(fp, subPath, (LONG)(strlen(subPath) * sizeof(char)));
312 WriteAsync(fp, "\n", 1 * sizeof(char));
313 #else
1 office 314 fprintf(fp, "%s\t%s\n", dirEntry->d_name, subPath);
22 office 315 #endif
1 office 316 ++stats->files;
317  
318 if(verbose) {
319 fprintf(stdout,
320 "Gathered %d directories and %d files.\r",
321 stats->dirs,
322 stats->files);
323 }
324  
325 free(subPath);
326 }
327  
328 closedir(dir);
329 free(path);
330 }
331  
332 if(verbose) {
333 fprintf(stdout, "\n");
334 }
335  
22 office 336 #if defined ___AsyncIO___
337 CloseAsync(fp);
338 #else
1 office 339 fclose(fp);
22 office 340 #endif
1 office 341  
342 }
343  
344 /*
345 *
23 office 346 * Gets the size of a file "dbFle".
1 office 347 */
23 office 348 int GetFileSize(char *dbFile) {
349 #if defined ___AsyncIO___
350 struct AsyncFile *fp;
351 LONG size;
352 #else
1 office 353 FILE *fp;
354 int size;
23 office 355 #endif
1 office 356  
23 office 357 #if defined ___AsyncIO___
358 if((fp = OpenAsync(dbFile, MODE_READ, ASYNC_BUF)) == NULL) {
359 #else
1 office 360 if((fp = fopen(dbFile, "r")) == NULL) {
23 office 361 #endif
1 office 362 fprintf(stderr, "Unable to open gather database for reading.\n");
363 return 0;
364 }
365  
23 office 366 #if defined ___AsyncIO___
367 SeekAsync(fp, 0, MODE_END);
368 size = SeekAsync(fp, 0, MODE_CURRENT);
369 #else
1 office 370 fseek(fp, 0L, SEEK_END);
371 size = ftell(fp);
23 office 372 #endif
1 office 373  
23 office 374 #if defined ___AsyncIO___
375 CloseAsync(fp);
376 #else
1 office 377 fclose(fp);
23 office 378 #endif
379  
1 office 380 return size;
381 }
382  
383 /*
384 *
23 office 385 * Counts the lines of a file.
1 office 386 */
23 office 387 int GetFileLines(char *dbFile) {
22 office 388 #if defined ___AsyncIO___
389 struct AsyncFile *fp;
390 LONG c;
391 #else
1 office 392 FILE *fp;
22 office 393 char c;
394 #endif
1 office 395 int lines;
396  
22 office 397 #if defined ___AsyncIO___
398 if((fp = OpenAsync(dbFile, MODE_READ, ASYNC_BUF)) == NULL) {
399 #else
1 office 400 if((fp = fopen(dbFile, "r")) == NULL) {
22 office 401 #endif
1 office 402 fprintf(stderr, "Unable to open gather database for reading.\n");
403 return 0;
404 }
405  
406 lines = 0;
22 office 407 #if defined ___AsyncIO___
23 office 408 while(run && (c = ReadCharAsync(fp)) != -1) {
22 office 409 #else
23 office 410 while(run && fscanf(fp, "%c", &c) == 1) {
22 office 411 #endif
23 office 412 #if defined ___AmigaOS___
413 // Check if CTRL+C was pressed and abort the program.
414 if(SetSignal(0L, SIGBREAKF_CTRL_C) & SIGBREAKF_CTRL_C) {
415 run = FALSE;
416 continue;
417 }
418 #endif
1 office 419 switch(c) {
420 case '\n':
421 ++lines;
422 break;
423 }
424 }
425  
22 office 426 #if defined ___AsyncIO___
427 CloseAsync(fp);
428 #else
1 office 429 fclose(fp);
22 office 430 #endif
1 office 431  
432 return lines;
433 }
434  
435 /*
436 *
437 * Creates "files" temporary filenames.
438 */
23 office 439 char **CreateTemporaryFiles(int files) {
1 office 440 char **tmpNames;
441 int count;
442  
23 office 443 tmpNames = malloc(files * sizeof(*tmpNames));
1 office 444  
445 if(verbose) {
446 fprintf(stdout, "Creating temporary files.\r");
447 }
448  
449 count = files;
23 office 450 while(run && --count > -1) {
1 office 451 #if defined ___AmigaOS___
452 // Check if CTRL+C was pressed and abort the program.
453 if(SetSignal(0L, SIGBREAKF_CTRL_C) & SIGBREAKF_CTRL_C) {
454 run = FALSE;
23 office 455 continue;
1 office 456 }
457 #endif
458 tmpNames[count] = tmpnam(NULL);
459  
460 if(verbose) {
461 fprintf(stdout, "Creating temporary files: %d%%\r", 100 - (int)(((float)count / files) * 100.0));
462 }
463 }
464  
465 if(verbose) {
466 fprintf(stdout, "\n");
467 }
468  
469 return tmpNames;
470 }
471  
472 /*
473 *
474 * Writes lines from the database "dbFile" to temporary filenames "tmpNames".
475 */
23 office 476 void WriteTemporaryFiles(char *dbFile, char **tmpNames, int tmpFiles, int tmpLines, int total) {
22 office 477 #if defined ___AsyncIO___
478 struct AsyncFile *fp, *tp;
479 LONG c;
480 #else
1 office 481 FILE *fp, *tp;
482 char c;
22 office 483 #endif
1 office 484 int lines;
485 int linesWritten;
486  
22 office 487 #if defined ___AsyncIO___
488 if((fp = OpenAsync(dbFile, MODE_READ, ASYNC_BUF)) == NULL) {
489 #else
1 office 490 if((fp = fopen(dbFile, "r")) == NULL) {
22 office 491 #endif
23 office 492 fprintf(stderr, "Unable to open gather database '%s' for reading.\n", dbFile);
1 office 493 return;
494 }
495  
22 office 496 #if defined ___AsyncIO___
23 office 497 if((tp = OpenAsync(tmpNames[--tmpFiles], MODE_WRITE, ASYNC_BUF)) == NULL) {
22 office 498 #else
23 office 499 if((tp = fopen(tmpNames[--tmpFiles], "w")) == NULL) {
22 office 500 #endif
1 office 501 fprintf(stderr, "Unable to open temporary file '%s' for writing.\n", tmpNames[tmpFiles]);
502 return;
503 }
504  
505 if(verbose) {
506 fprintf(stdout, "Writing to temporary files.\r");
507 }
508  
509 linesWritten = 0;
510 lines = 0;
22 office 511 #if defined ___AsyncIO___
512 while(run && (c = ReadCharAsync(fp)) != -1) {
513 #else
1 office 514 while(run && fscanf(fp, "%c", &c) == 1) {
22 office 515 #endif
1 office 516 #if defined ___AmigaOS___
517 // Check if CTRL+C was pressed and abort the program.
518 if(SetSignal(0L, SIGBREAKF_CTRL_C) & SIGBREAKF_CTRL_C) {
519 run = FALSE;
23 office 520 continue;
1 office 521 }
522 #endif
523 switch(c) {
524 case '\n':
525 // Increment the total written lines.
526 ++linesWritten;
527  
528 if(verbose) {
529 fprintf(stdout, "Writing to temporary files: %d%%.\r", (int)(((float)linesWritten / total) * 100.0));
530 }
531  
532 // Write the newline character back.
22 office 533 #if defined ___AsyncIO___
534 if(WriteCharAsync(tp, (UBYTE)c) != 1) {
535 #else
1 office 536 if(fprintf(tp, "%c", c) != 1) {
22 office 537 #endif
1 office 538 fprintf(stderr, "Unable to write to temporary file '%s'.\n", tmpNames[tmpFiles]);
22 office 539 #if defined ___AsyncIO___
540 CloseAsync(tp);
541 CloseAsync(fp);
542 #else
19 office 543 fclose(tp);
1 office 544 fclose(fp);
22 office 545 #endif
1 office 546 return;
547 }
548 // Switch to the next temporary file.
549 if(++lines >= tmpLines) {
550 // If there are no temporary files left then run till the end.
551 if(tmpFiles - 1 < 0) {
552 break;
553 }
554  
555 // Close the previous temporary file and write to the next temporary file.
22 office 556 #if defined ___AsyncIO___
557 CloseAsync(tp);
23 office 558 if((tp = OpenAsync(tmpNames[--tmpFiles], MODE_WRITE, ASYNC_BUF)) == NULL) {
22 office 559 #else
1 office 560 fclose(tp);
23 office 561 if((tp = fopen(tmpNames[--tmpFiles], "w")) == NULL) {
22 office 562 #endif
1 office 563 fprintf(stderr, "Unable to open temporary file '%s' for writing.\n", tmpNames[tmpFiles]);
22 office 564 #if defined ___AsyncIO___
565 CloseAsync(tp);
566 CloseAsync(fp);
567 #else
19 office 568 fclose(tp);
1 office 569 fclose(fp);
22 office 570 #endif
1 office 571 }
572 lines = 0;
573 break;
574 }
575 break;
576 default:
22 office 577 #if defined ___AsyncIO___
578 if(WriteCharAsync(tp, (UBYTE)c) != 1) {
579 #else
1 office 580 if(fprintf(tp, "%c", c) != 1) {
22 office 581 #endif
1 office 582 fprintf(stderr, "Unable to write to temporary file '%s'.\n", tmpNames[tmpFiles]);
22 office 583 #if defined ___AsyncIO___
584 CloseAsync(tp);
585 CloseAsync(fp);
586 #else
1 office 587 fclose(tp);
588 fclose(fp);
22 office 589 #endif
1 office 590 return;
591 }
592 break;
593 }
594 }
595  
596 fprintf(stdout, "\n");
597  
22 office 598 #if defined ___AsyncIO___
599 CloseAsync(tp);
600 CloseAsync(fp);
601 #else
1 office 602 fclose(tp);
603 fclose(fp);
22 office 604 #endif
1 office 605 }
606  
607 /*
608 *
609 * Skips a line in a database file "fp".
610 */
22 office 611  
612 #if defined ___AsyncIO___
23 office 613 void SkipLine(struct AsyncFile *fp) {
22 office 614 LONG c;
23 office 615 while(run && (c = ReadCharAsync(fp)) != -1) {
22 office 616 #else
1 office 617 void SkipDatabaseLine(FILE *fp) {
618 char c;
23 office 619 while(run && fscanf(fp, "%c", &c) == 1) {
22 office 620 #endif
23 office 621 #if defined ___AmigaOS___
622 // Check if CTRL+C was pressed and abort the program.
623 if(SetSignal(0L, SIGBREAKF_CTRL_C) & SIGBREAKF_CTRL_C) {
624 run = FALSE;
625 continue;
626 }
627 #endif
22 office 628 switch(c) {
629 case '\n':
630 return;
1 office 631 }
632 }
633 }
634  
635 /*
636 *
637 * Reads a line from the database file "fp".
638 */
22 office 639 #if defined ___AsyncIO___
23 office 640 char *ReadLine(struct AsyncFile *fp) {
22 office 641 LONG c;
642 #else
23 office 643 char *ReadLine(FILE *fp) {
1 office 644 char c;
22 office 645 #endif
1 office 646 char *line;
11 office 647 int line_size;
648 int i;
1 office 649  
11 office 650 line_size = LINE_BUF;
19 office 651 line = malloc(line_size * sizeof(*line));
1 office 652  
11 office 653 i = 0;
22 office 654 #if defined ___AsyncIO___
655 while(run && (c = ReadCharAsync(fp)) != -1) {
656 #else
1 office 657 while(run && fscanf(fp, "%c", &c) == 1) {
22 office 658 #endif
1 office 659 #if defined ___AmigaOS___
660 // Check if CTRL+C was pressed and abort the program.
661 if(SetSignal(0L, SIGBREAKF_CTRL_C) & SIGBREAKF_CTRL_C) {
662 run = FALSE;
23 office 663 continue;
1 office 664 }
665 #endif
666 switch(c) {
667 case '\n':
668 // Rewind the file by the number of read characters.
22 office 669 #if defined ___AsyncIO___
670 if(SeekAsync(fp, -(i + 1), MODE_CURRENT) == -1) {
671 fprintf(stderr, "Could not seek in file.\n");
672 return NULL;
673 }
674 #else
11 office 675 fseek(fp, -(i + 1), SEEK_CUR);
22 office 676 #endif
1 office 677 return line;
678 default:
11 office 679 if(strlen(line) == line_size) {
680 line_size = line_size * 1.5;
19 office 681 line = realloc(line, line_size * sizeof(*line));
11 office 682 }
683 //line = realloc(line, (chars + 1 + 1) * sizeof(char));
684 line[i] = c;
685 line[i + 1] = '\0';
1 office 686 break;
687 }
11 office 688 ++i;
1 office 689 }
690  
691 return NULL;
692 }
693  
694 /*
695 *
696 * Merges temporary files "tmpNames" into a database "dbFile".
697 */
698 void MergeDatabase(char *dbFile, char **tmpNames, int files, int lines) {
22 office 699 #if defined ___AsyncIO___
700 struct AsyncFile *fp;
701 struct AsyncFile **tp;
702 #else
1 office 703 FILE *fp;
704 FILE **tp;
22 office 705 #endif
1 office 706 int i;
14 office 707 int j;
1 office 708 char *tmp;
14 office 709 char *min;
1 office 710 int count;
711  
22 office 712 #if defined ___AsyncIO___
23 office 713 if((fp = OpenAsync(dbFile, MODE_WRITE, ASYNC_BUF)) == NULL) {
22 office 714 #else
23 office 715 if((fp = fopen(dbFile, "w")) == NULL) {
22 office 716 #endif
1 office 717 fprintf(stderr, "Unable to open gather database for writing.\n");
718 return;
719 }
720  
721 // Allocate as many file pointers as temporary files.
19 office 722 tp = malloc(files * sizeof(*tp));
1 office 723  
724 // Open all temporary files for reading.
725 for(i = 0; i < files; ++i) {
22 office 726 #if defined ___AsyncIO___
727 if((tp[i] = OpenAsync(tmpNames[i], MODE_READ, ASYNC_BUF)) == NULL) {
728 #else
1 office 729 if((tp[i] = fopen(tmpNames[i], "r")) == NULL) {
22 office 730 #endif
1 office 731 fprintf(stderr, "Unable to open temporary file '%s' for reading.\n", tmpNames[i]);
732 // Close all temporary files.
733 --i;
734 while(i >= 0) {
22 office 735 #if defined ___AsyncIO___
736 CloseAsync(tp[i]);
737 #else
1 office 738 fclose(tp[i]);
22 office 739 #endif
1 office 740 }
741 return;
742 }
743 }
744  
745 if(verbose) {
746 fprintf(stdout, "Merging all database lines in temporary files.\r");
747 }
748  
749 count = lines;
14 office 750 j = 0;
1 office 751 while(run && --count > -1) {
752 #if defined ___AmigaOS___
753 // Check if CTRL+C was pressed and abort the program.
754 if(SetSignal(0L, SIGBREAKF_CTRL_C) & SIGBREAKF_CTRL_C) {
755 run = FALSE;
23 office 756 continue;
1 office 757 }
758 #endif
759 // Find the smallest line in all temporary files.
760 if(verbose) {
761 fprintf(stdout, "Merging all database lines in temporary files: %d%%.\r", 100 - (int)(((float)count / lines) * 100.0));
762 }
763  
14 office 764 min = NULL;
1 office 765 for(i = 0; i < files; ++i) {
23 office 766 tmp = ReadLine(tp[i]);
1 office 767 if(tmp == NULL) {
768 continue;
769 }
14 office 770 if(min == NULL || strncmp(tmp, min, strlen(tmp)) < 0) {
771 if(min != NULL) {
2 office 772 // Free previous instance.
14 office 773 free(min);
2 office 774 }
19 office 775 min = malloc((strlen(tmp) + 1) * sizeof(*min));
14 office 776 sprintf(min, "%s", tmp);
1 office 777 // Remember the index of the file where the smallest entry has been found.
14 office 778 j = i;
1 office 779 free(tmp);
780 continue;
781 }
782 free(tmp);
783 }
784  
785 // Forward the file where the smallest line was found.
23 office 786 SkipLine(tp[j]);
1 office 787  
788 // Write the smallest line.
14 office 789 if(min != NULL) {
22 office 790 #if defined ___AsyncIO___
791 WriteAsync(fp, min, (LONG)(strlen(min) * sizeof(char)));
792 WriteAsync(fp, "\n", 1 * sizeof(char));
793 #else
14 office 794 fprintf(fp, "%s\n", min);
22 office 795 #endif
14 office 796 free(min);
1 office 797 }
798 }
799  
800 // Write out any remaining contents from the temporary files.
801 for(i = 0; i < files; ++i) {
23 office 802 tmp = ReadLine(tp[i]);
1 office 803 if(tmp == NULL) {
804 continue;
805 }
22 office 806 #if defined ___AsyncIO___
807 WriteAsync(fp, tmp, (LONG)(strlen(tmp) * sizeof(char)));
808 WriteAsync(fp, "\n", 1 * sizeof(char));
809 #else
1 office 810 fprintf(fp, "%s\n", tmp);
22 office 811 #endif
14 office 812 free(tmp);
1 office 813 }
814  
815 // Close and delete all temporary files.
816 for(i = 0; i < files; ++i) {
22 office 817 #if defined ___AsyncIO___
818 CloseAsync(tp[i]);
819 #else
1 office 820 fclose(tp[i]);
22 office 821 #endif
1 office 822 // Delete temporary file.
823 remove(tmpNames[i]);
824 }
825  
826 if(verbose) {
827 fprintf(stdout, "\n");
828 }
829  
22 office 830 #if defined ___AsyncIO___
831 CloseAsync(fp);
832 #else
1 office 833 fclose(fp);
22 office 834 #endif
1 office 835 }
836  
837 /*
838 *
839 * Indexes a "path" by creating a database "dbFile".
840 */
841 void Gather(char *dbFile, char *path) {
842 stringStack *stack = stringStackCreate(1);
843 stats *stats = malloc(sizeof(stats));
844 char **tmpNames;
845 int dbSize, dbLines, tmpFiles, tmpLines;
846 int i;
847  
848 // Initialize metrics.
849 stats->dirs = 0;
850 stats->files = 0;
851  
852 // Push the first path onto the stack.
853 stringStackPush(stack, path);
854  
855 // Generate the database file.
856 UpdateDatabase(dbFile, stack, stats);
857  
858 // Get the database metrics.
23 office 859 dbSize = GetFileSize(dbFile);
860 dbLines = GetFileLines(dbFile);
1 office 861  
862 // Compute the amount of temporary files needed.
863 tmpFiles = dbSize / MAX_MEM;
864  
865 // In case no temporary files are required,
866 // just sort the database and terminate.
2 office 867 if(tmpFiles <= 1) {
1 office 868 SortDatabase(dbFile);
869 return;
870 }
871  
872 tmpLines = dbLines / tmpFiles;
873  
874 // Create temporary files.
23 office 875 if((tmpNames = CreateTemporaryFiles(tmpFiles)) == NULL) {
1 office 876 fprintf(stderr, "Unable to create temporary files.\n");
877 return;
878 }
879  
880 // Write "tmpLines" to temporary files in "tmpFiles" from "dbFile".
23 office 881 WriteTemporaryFiles(dbFile, tmpNames, tmpFiles, tmpLines, dbLines);
1 office 882  
883 // Sort the temporary files.
884 for(i = 0; i < tmpFiles; ++i) {
885 SortDatabase(tmpNames[i]);
886 }
887  
888 MergeDatabase(dbFile, tmpNames, tmpFiles, dbLines);
889 }
890  
11 office 891 void usage(char *name) {
892 fprintf(stdout, "Hunt & Gather - %s, a file index generating tool. \n", name);
19 office 893 fprintf(stdout, "Version: %s \n", PROGRAM_VERSION);
11 office 894 fprintf(stdout, " \n");
895 fprintf(stdout, "SYNTAX: %s [-q] DATABASE \n", name);
896 fprintf(stdout, " \n");
897 fprintf(stdout, " -q Do not print out any messages. \n");
898 fprintf(stdout, " \n");
899 fprintf(stdout, "DATABASE is a path to where the indexed results will be \n");
900 fprintf(stdout, "stored for searching with the Hunt tool. \n");
901 fprintf(stdout, " \n");
902 fprintf(stdout, "(c) 2021 Wizardry and Steamworks, MIT. \n");
903 }
904  
1 office 905 /*
906 *
907 * Main entry point.
908 */
909 int main(int argc, char **argv) {
910 int option;
2 office 911 char *dbFile;
19 office 912 char *path;
10 office 913 struct stat dirStat;
19 office 914 #if defined ___AmigaOS___
915 BPTR lock;
916 #endif
1 office 917  
918 // Bind handler to SIGINT.
919 signal(SIGINT, SignalHandler);
920  
2 office 921 dbFile = DEFAULT_DATABASE_FILE;
922 while((option = getopt(argc, argv, "hqd:")) != -1) {
1 office 923 switch(option) {
2 office 924 case 'd':
925 dbFile = optarg;
926 break;
1 office 927 case 'q':
928 verbose = FALSE;
929 break;
930 case 'h':
11 office 931 usage(argv[0]);
2 office 932 return 0;
1 office 933 case '?':
934 fprintf(stderr, "Invalid option %ct.\n", optopt);
935 return 1;
936 }
937 }
938  
10 office 939  
940 if(optind >= argc) {
11 office 941 usage(argv[0]);
1 office 942 return 1;
943 }
944  
19 office 945 #if defined ___AmigaOS___
946 path = malloc(PATH_MAX * sizeof(*path));
947 lock = Lock(argv[optind], SHARED_LOCK);
948 NameFromLock(lock, path, PATH_MAX);
949 UnLock(lock);
950 #else
951 path = realpath(argv[optind], NULL);
952 #endif
953  
954 stat(path, &dirStat);
10 office 955 if(!S_ISDIR(dirStat.st_mode)) {
5 office 956 fprintf(stderr, "Path '%s' is not a directory.\n", argv[optind]);
1 office 957 return 1;
958 }
959  
2 office 960 if(verbose) {
961 fprintf(stdout, "Gathering to database file: %s\n", dbFile);
962 }
963  
1 office 964 // Gather.
19 office 965 Gather(dbFile, path);
1 office 966  
19 office 967 free(path);
968  
1 office 969 return 0;
970 }