HuntnGather – Blame information for rev 24
?pathlinks?
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 | } |