HuntnGather – Diff between revs 15 and 16

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