HuntnGather – Blame information for rev 24

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