HuntnGather – Diff between revs 12 and 13

Subversion Repositories:
Rev:
Only display areas with differencesIgnore whitespace
Rev 12 Rev 13
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 strcmp(*p, *q); 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 return; 203 return;
204 } 204 }
205   205  
206 if((dir = opendir(path)) == NULL) { 206 if((dir = opendir(path)) == NULL) {
207 return; 207 return;
208 } 208 }
209   209  
210 while(run && (dirEntry = readdir(dir)) != NULL) { 210 while(run && (dirEntry = readdir(dir)) != NULL) {
211 #if defined ___AmigaOS___ 211 #if defined ___AmigaOS___
212 // Check if CTRL+C was pressed and abort the program. 212 // Check if CTRL+C was pressed and abort the program.
213 if(SetSignal(0L, SIGBREAKF_CTRL_C) & SIGBREAKF_CTRL_C) { 213 if(SetSignal(0L, SIGBREAKF_CTRL_C) & SIGBREAKF_CTRL_C) {
214 run = FALSE; 214 run = FALSE;
215 } 215 }
216 #endif 216 #endif
217 size = sizeof(path) + sizeof(dirEntry->d_name) + 1; 217 size = sizeof(path) + sizeof(dirEntry->d_name) + 1;
218 switch(path[strlen(path) - 1]) { 218 switch(path[strlen(path) - 1]) {
219 case '/': 219 case '/':
220 case ':': // This is a drive path. 220 case ':': // This is a drive path.
221 subPath = malloc(size); 221 subPath = malloc(size);
222 sprintf(subPath, "%s%s", path, dirEntry->d_name); 222 sprintf(subPath, "%s%s", path, dirEntry->d_name);
223 break; 223 break;
224 default: 224 default:
225 subPath = malloc(size + 1); 225 subPath = malloc(size + 1);
226 sprintf(subPath, "%s/%s", path, dirEntry->d_name); 226 sprintf(subPath, "%s/%s", path, dirEntry->d_name);
227 break; 227 break;
228 } 228 }
229 stat(subPath, &dirStat); 229 stat(subPath, &dirStat);
230 if(S_ISDIR(dirStat.st_mode)) { 230 if(S_ISDIR(dirStat.st_mode)) {
231 stringStackPush(dirStack, subPath); 231 stringStackPush(dirStack, subPath);
232   232  
233 ++stats->dirs; 233 ++stats->dirs;
234   234  
235 if(verbose) { 235 if(verbose) {
236 fprintf(stdout, 236 fprintf(stdout,
237 "Gathered %d directories and %d files.\r", 237 "Gathered %d directories and %d files.\r",
238 stats->dirs, 238 stats->dirs,
239 stats->files); 239 stats->files);
240 } 240 }
241   241  
242 free(subPath); 242 free(subPath);
243 continue; 243 continue;
244 } 244 }
245   245  
246 // Write to database file. 246 // Write to database file.
247 fprintf(fp, "%s\t%s\n", dirEntry->d_name, subPath); 247 fprintf(fp, "%s\t%s\n", dirEntry->d_name, subPath);
248   248  
249 ++stats->files; 249 ++stats->files;
250   250  
251 if(verbose) { 251 if(verbose) {
252 fprintf(stdout, 252 fprintf(stdout,
253 "Gathered %d directories and %d files.\r", 253 "Gathered %d directories and %d files.\r",
254 stats->dirs, 254 stats->dirs,
255 stats->files); 255 stats->files);
256 } 256 }
257   257  
258 free(subPath); 258 free(subPath);
259 } 259 }
260   260  
261 closedir(dir); 261 closedir(dir);
262 free(path); 262 free(path);
263 } 263 }
264   264  
265 if(verbose) { 265 if(verbose) {
266 fprintf(stdout, "\n"); 266 fprintf(stdout, "\n");
267 } 267 }
268   268  
269 fclose(fp); 269 fclose(fp);
270   270  
271 } 271 }
272   272  
273 /* 273 /*
274 * 274 *
275 * Gets the size of a database "dbFle". 275 * Gets the size of a database "dbFle".
276 */ 276 */
277 int GetDatabaseSize(char *dbFile) { 277 int GetDatabaseSize(char *dbFile) {
278 FILE *fp; 278 FILE *fp;
279 int size; 279 int size;
280   280  
281 if((fp = fopen(dbFile, "r")) == NULL) { 281 if((fp = fopen(dbFile, "r")) == NULL) {
282 fprintf(stderr, "Unable to open gather database for reading.\n"); 282 fprintf(stderr, "Unable to open gather database for reading.\n");
283 fclose(fp); 283 fclose(fp);
284 return 0; 284 return 0;
285 } 285 }
286   286  
287 fseek(fp, 0L, SEEK_END); 287 fseek(fp, 0L, SEEK_END);
288 size = ftell(fp); 288 size = ftell(fp);
289   289  
290 fclose(fp); 290 fclose(fp);
291 return size; 291 return size;
292 } 292 }
293   293  
294 /* 294 /*
295 * 295 *
296 * Counts the lines in a database file "dbFile". 296 * Counts the lines in a database file "dbFile".
297 */ 297 */
298 int CountDatabaseLines(char *dbFile) { 298 int CountDatabaseLines(char *dbFile) {
299 FILE *fp; 299 FILE *fp;
300 int lines; 300 int lines;
301 char c; 301 char c;
302   302  
303 if((fp = fopen(dbFile, "r")) == NULL) { 303 if((fp = fopen(dbFile, "r")) == NULL) {
304 fprintf(stderr, "Unable to open gather database for reading.\n"); 304 fprintf(stderr, "Unable to open gather database for reading.\n");
305 fclose(fp); 305 fclose(fp);
306 return 0; 306 return 0;
307 } 307 }
308   308  
309 lines = 0; 309 lines = 0;
310 while(fscanf(fp, "%c", &c) == 1) { 310 while(fscanf(fp, "%c", &c) == 1) {
311 switch(c) { 311 switch(c) {
312 case '\n': 312 case '\n':
313 ++lines; 313 ++lines;
314 break; 314 break;
315 } 315 }
316 } 316 }
317   317  
318 fclose(fp); 318 fclose(fp);
319   319  
320 return lines; 320 return lines;
321 } 321 }
322   322  
323 /* 323 /*
324 * 324 *
325 * Creates "files" temporary filenames. 325 * Creates "files" temporary filenames.
326 */ 326 */
327 char **CreateTempFiles(int files) { 327 char **CreateTempFiles(int files) {
328 char **tmpNames; 328 char **tmpNames;
329 int count; 329 int count;
330   330  
331 tmpNames = malloc(files * sizeof(char *)); 331 tmpNames = malloc(files * sizeof(char *));
332   332  
333 if(verbose) { 333 if(verbose) {
334 fprintf(stdout, "Creating temporary files.\r"); 334 fprintf(stdout, "Creating temporary files.\r");
335 } 335 }
336   336  
337 count = files; 337 count = files;
338 while(--count > -1) { 338 while(--count > -1) {
339 #if defined ___AmigaOS___ 339 #if defined ___AmigaOS___
340 // Check if CTRL+C was pressed and abort the program. 340 // Check if CTRL+C was pressed and abort the program.
341 if(SetSignal(0L, SIGBREAKF_CTRL_C) & SIGBREAKF_CTRL_C) { 341 if(SetSignal(0L, SIGBREAKF_CTRL_C) & SIGBREAKF_CTRL_C) {
342 run = FALSE; 342 run = FALSE;
343 } 343 }
344 #endif 344 #endif
345 tmpNames[count] = tmpnam(NULL); 345 tmpNames[count] = tmpnam(NULL);
346   346  
347 if(verbose) { 347 if(verbose) {
348 fprintf(stdout, "Creating temporary files: %d%%\r", 100 - (int)(((float)count / files) * 100.0)); 348 fprintf(stdout, "Creating temporary files: %d%%\r", 100 - (int)(((float)count / files) * 100.0));
349 } 349 }
350 } 350 }
351   351  
352 if(verbose) { 352 if(verbose) {
353 fprintf(stdout, "\n"); 353 fprintf(stdout, "\n");
354 } 354 }
355   355  
356 return tmpNames; 356 return tmpNames;
357 } 357 }
358   358  
359 /* 359 /*
360 * 360 *
361 * Writes lines from the database "dbFile" to temporary filenames "tmpNames". 361 * Writes lines from the database "dbFile" to temporary filenames "tmpNames".
362 */ 362 */
363 void WriteTempFiles(char *dbFile, char **tmpNames, int tmpFiles, int tmpLines, int total) { 363 void WriteTempFiles(char *dbFile, char **tmpNames, int tmpFiles, int tmpLines, int total) {
364 FILE *fp, *tp; 364 FILE *fp, *tp;
365 char c; 365 char c;
366 int lines; 366 int lines;
367 int linesWritten; 367 int linesWritten;
368   368  
369 if((fp = fopen(dbFile, "r")) == NULL) { 369 if((fp = fopen(dbFile, "r")) == NULL) {
370 fprintf(stderr, "Unable to open gather database for reading.\n"); 370 fprintf(stderr, "Unable to open gather database for reading.\n");
371 return; 371 return;
372 } 372 }
373   373  
374 if((tp = fopen(tmpNames[--tmpFiles], "w+")) == NULL) { 374 if((tp = fopen(tmpNames[--tmpFiles], "w+")) == NULL) {
375 fprintf(stderr, "Unable to open temporary file '%s' for writing.\n", tmpNames[tmpFiles]); 375 fprintf(stderr, "Unable to open temporary file '%s' for writing.\n", tmpNames[tmpFiles]);
376 fclose(fp); 376 fclose(fp);
377 return; 377 return;
378 } 378 }
379   379  
380 if(verbose) { 380 if(verbose) {
381 fprintf(stdout, "Writing to temporary files.\r"); 381 fprintf(stdout, "Writing to temporary files.\r");
382 } 382 }
383   383  
384 linesWritten = 0; 384 linesWritten = 0;
385 lines = 0; 385 lines = 0;
386 while(run && fscanf(fp, "%c", &c) == 1) { 386 while(run && fscanf(fp, "%c", &c) == 1) {
387 #if defined ___AmigaOS___ 387 #if defined ___AmigaOS___
388 // Check if CTRL+C was pressed and abort the program. 388 // Check if CTRL+C was pressed and abort the program.
389 if(SetSignal(0L, SIGBREAKF_CTRL_C) & SIGBREAKF_CTRL_C) { 389 if(SetSignal(0L, SIGBREAKF_CTRL_C) & SIGBREAKF_CTRL_C) {
390 run = FALSE; 390 run = FALSE;
391 } 391 }
392 #endif 392 #endif
393 switch(c) { 393 switch(c) {
394 case '\n': 394 case '\n':
395 // Increment the total written lines. 395 // Increment the total written lines.
396 ++linesWritten; 396 ++linesWritten;
397   397  
398 if(verbose) { 398 if(verbose) {
399 fprintf(stdout, "Writing to temporary files: %d%%.\r", (int)(((float)linesWritten / total) * 100.0)); 399 fprintf(stdout, "Writing to temporary files: %d%%.\r", (int)(((float)linesWritten / total) * 100.0));
400 } 400 }
401   401  
402 // Write the newline character back. 402 // Write the newline character back.
403 if(fprintf(tp, "%c", c) != 1) { 403 if(fprintf(tp, "%c", c) != 1) {
404 fprintf(stderr, "Unable to write to temporary file '%s'.\n", tmpNames[tmpFiles]); 404 fprintf(stderr, "Unable to write to temporary file '%s'.\n", tmpNames[tmpFiles]);
405 fclose(tp); 405 fclose(tp);
406 fclose(fp); 406 fclose(fp);
407 return; 407 return;
408 } 408 }
409 // Switch to the next temporary file. 409 // Switch to the next temporary file.
410 if(++lines >= tmpLines) { 410 if(++lines >= tmpLines) {
411 // If there are no temporary files left then run till the end. 411 // If there are no temporary files left then run till the end.
412 if(tmpFiles - 1 < 0) { 412 if(tmpFiles - 1 < 0) {
413 break; 413 break;
414 } 414 }
415   415  
416 // Close the previous temporary file and write to the next temporary file. 416 // Close the previous temporary file and write to the next temporary file.
417 fclose(tp); 417 fclose(tp);
418 if((tp = fopen(tmpNames[--tmpFiles], "w+")) == NULL) { 418 if((tp = fopen(tmpNames[--tmpFiles], "w+")) == NULL) {
419 fprintf(stderr, "Unable to open temporary file '%s' for writing.\n", tmpNames[tmpFiles]); 419 fprintf(stderr, "Unable to open temporary file '%s' for writing.\n", tmpNames[tmpFiles]);
420 fclose(tp); 420 fclose(tp);
421 fclose(fp); 421 fclose(fp);
422 } 422 }
423 lines = 0; 423 lines = 0;
424 break; 424 break;
425 } 425 }
426 break; 426 break;
427 default: 427 default:
428 if(fprintf(tp, "%c", c) != 1) { 428 if(fprintf(tp, "%c", c) != 1) {
429 fprintf(stderr, "Unable to write to temporary file '%s'.\n", tmpNames[tmpFiles]); 429 fprintf(stderr, "Unable to write to temporary file '%s'.\n", tmpNames[tmpFiles]);
430 fclose(tp); 430 fclose(tp);
431 fclose(fp); 431 fclose(fp);
432 return; 432 return;
433 } 433 }
434 break; 434 break;
435 } 435 }
436 } 436 }
437   437  
438 fprintf(stdout, "\n"); 438 fprintf(stdout, "\n");
439   439  
440 fclose(tp); 440 fclose(tp);
441 fclose(fp); 441 fclose(fp);
442 } 442 }
443   443  
444 /* 444 /*
445 * 445 *
446 * Skips a line in a database file "fp". 446 * Skips a line in a database file "fp".
447 */ 447 */
448 void SkipDatabaseLine(FILE *fp) { 448 void SkipDatabaseLine(FILE *fp) {
449 char c; 449 char c;
450   450  
451 while(fscanf(fp, "%c", &c) == 1) { 451 while(fscanf(fp, "%c", &c) == 1) {
452 if(c == '\n') { 452 if(c == '\n') {
453 break; 453 break;
454 } 454 }
455 } 455 }
456   456  
457 return; 457 return;
458 } 458 }
459   459  
460 /* 460 /*
461 * 461 *
462 * Reads a line from the database file "fp". 462 * Reads a line from the database file "fp".
463 */ 463 */
464 char *ReadDatabaseLine(FILE *fp) { 464 char *ReadDatabaseLine(FILE *fp) {
465 char c; 465 char c;
466 char *line; 466 char *line;
467 int line_size; 467 int line_size;
468 int i; 468 int i;
469   469  
470 line_size = LINE_BUF; 470 line_size = LINE_BUF;
471 line = malloc(line_size * sizeof(char)); 471 line = malloc(line_size * sizeof(char));
472   472  
473 i = 0; 473 i = 0;
474 while(run && fscanf(fp, "%c", &c) == 1) { 474 while(run && fscanf(fp, "%c", &c) == 1) {
475 #if defined ___AmigaOS___ 475 #if defined ___AmigaOS___
476 // Check if CTRL+C was pressed and abort the program. 476 // Check if CTRL+C was pressed and abort the program.
477 if(SetSignal(0L, SIGBREAKF_CTRL_C) & SIGBREAKF_CTRL_C) { 477 if(SetSignal(0L, SIGBREAKF_CTRL_C) & SIGBREAKF_CTRL_C) {
478 run = FALSE; 478 run = FALSE;
479 } 479 }
480 #endif 480 #endif
481 switch(c) { 481 switch(c) {
482 case '\n': 482 case '\n':
483 // Rewind the file by the number of read characters. 483 // Rewind the file by the number of read characters.
484 fseek(fp, -(i + 1), SEEK_CUR); 484 fseek(fp, -(i + 1), SEEK_CUR);
485 return line; 485 return line;
486 default: 486 default:
487 if(strlen(line) == line_size) { 487 if(strlen(line) == line_size) {
488 line_size = line_size * 1.5; 488 line_size = line_size * 1.5;
489 line = realloc(line, line_size * sizeof(char)); 489 line = realloc(line, line_size * sizeof(char));
490 } 490 }
491 //line = realloc(line, (chars + 1 + 1) * sizeof(char)); 491 //line = realloc(line, (chars + 1 + 1) * sizeof(char));
492 line[i] = c; 492 line[i] = c;
493 line[i + 1] = '\0'; 493 line[i + 1] = '\0';
494 break; 494 break;
495 } 495 }
496 ++i; 496 ++i;
497 } 497 }
498   498  
499 return NULL; 499 return NULL;
500 } 500 }
501   501  
502 /* 502 /*
503 * 503 *
504 * Merges temporary files "tmpNames" into a database "dbFile". 504 * Merges temporary files "tmpNames" into a database "dbFile".
505 */ 505 */
506 void MergeDatabase(char *dbFile, char **tmpNames, int files, int lines) { 506 void MergeDatabase(char *dbFile, char **tmpNames, int files, int lines) {
507 FILE *fp; 507 FILE *fp;
508 FILE **tp; 508 FILE **tp;
509 int i; 509 int i;
510 char *tmp; 510 char *tmp;
511 char *tmpMin; 511 char *tmpMin;
512 int idxMin; 512 int idxMin;
513 int count; 513 int count;
514   514  
515 if((fp = fopen(dbFile, "w+")) == NULL) { 515 if((fp = fopen(dbFile, "w+")) == NULL) {
516 fprintf(stderr, "Unable to open gather database for writing.\n"); 516 fprintf(stderr, "Unable to open gather database for writing.\n");
517 return; 517 return;
518 } 518 }
519   519  
520 // Allocate as many file pointers as temporary files. 520 // Allocate as many file pointers as temporary files.
521 tp = malloc(files * sizeof(FILE *)); 521 tp = malloc(files * sizeof(FILE *));
522   522  
523 // Open all temporary files for reading. 523 // Open all temporary files for reading.
524 for(i = 0; i < files; ++i) { 524 for(i = 0; i < files; ++i) {
525 if((tp[i] = fopen(tmpNames[i], "r")) == NULL) { 525 if((tp[i] = fopen(tmpNames[i], "r")) == NULL) {
526 fprintf(stderr, "Unable to open temporary file '%s' for reading.\n", tmpNames[i]); 526 fprintf(stderr, "Unable to open temporary file '%s' for reading.\n", tmpNames[i]);
527 // Close all temporary files. 527 // Close all temporary files.
528 --i; 528 --i;
529 while(i >= 0) { 529 while(i >= 0) {
530 fclose(tp[i]); 530 fclose(tp[i]);
531 } 531 }
532 return; 532 return;
533 } 533 }
534 } 534 }
535   535  
536 if(verbose) { 536 if(verbose) {
537 fprintf(stdout, "Merging all database lines in temporary files.\r"); 537 fprintf(stdout, "Merging all database lines in temporary files.\r");
538 } 538 }
539   539  
540 count = lines; 540 count = lines;
541 idxMin = 0; 541 idxMin = 0;
542 while(run && --count > -1) { 542 while(run && --count > -1) {
543 #if defined ___AmigaOS___ 543 #if defined ___AmigaOS___
544 // Check if CTRL+C was pressed and abort the program. 544 // Check if CTRL+C was pressed and abort the program.
545 if(SetSignal(0L, SIGBREAKF_CTRL_C) & SIGBREAKF_CTRL_C) { 545 if(SetSignal(0L, SIGBREAKF_CTRL_C) & SIGBREAKF_CTRL_C) {
546 run = FALSE; 546 run = FALSE;
547 } 547 }
548 #endif 548 #endif
549 // Find the smallest line in all temporary files. 549 // Find the smallest line in all temporary files.
550 if(verbose) { 550 if(verbose) {
551 fprintf(stdout, "Merging all database lines in temporary files: %d%%.\r", 100 - (int)(((float)count / lines) * 100.0)); 551 fprintf(stdout, "Merging all database lines in temporary files: %d%%.\r", 100 - (int)(((float)count / lines) * 100.0));
552 } 552 }
553   553  
554 tmpMin = NULL; 554 tmpMin = NULL;
555 for(i = 0; i < files; ++i) { 555 for(i = 0; i < files; ++i) {
556 tmp = ReadDatabaseLine(tp[i]); 556 tmp = ReadDatabaseLine(tp[i]);
557 if(tmp == NULL) { 557 if(tmp == NULL) {
558 continue; 558 continue;
559 } 559 }
560 if(tmpMin == NULL || strcmp(tmp, tmpMin) < 0) { 560 if(tmpMin == NULL || strncmp(tmp, tmpMin, strlen(tmp)) < 0) {
561 if(tmpMin != NULL) { 561 if(tmpMin != NULL) {
562 // Free previous instance. 562 // Free previous instance.
563 free(tmpMin); 563 free(tmpMin);
564 } 564 }
565 tmpMin = malloc((strlen(tmp) + 1) * sizeof(char)); 565 tmpMin = malloc((strlen(tmp) + 1) * sizeof(char));
566 sprintf(tmpMin, "%s", tmp); 566 sprintf(tmpMin, "%s", tmp);
567 // Remember the index of the file where the smallest entry has been found. 567 // Remember the index of the file where the smallest entry has been found.
568 idxMin = i; 568 idxMin = i;
569 free(tmp); 569 free(tmp);
570 continue; 570 continue;
571 } 571 }
572 free(tmp); 572 free(tmp);
573 } 573 }
574   574  
575 // Forward the file where the smallest line was found. 575 // Forward the file where the smallest line was found.
576 SkipDatabaseLine(tp[idxMin]); 576 SkipDatabaseLine(tp[idxMin]);
577   577  
578 // Write the smallest line. 578 // Write the smallest line.
579 if(tmpMin != NULL) { 579 if(tmpMin != NULL) {
580 fprintf(fp, "%s\n", tmpMin); 580 fprintf(fp, "%s\n", tmpMin);
581 free(tmpMin); 581 free(tmpMin);
582 } 582 }
583 } 583 }
584   584  
585 // Write out any remaining contents from the temporary files. 585 // Write out any remaining contents from the temporary files.
586 for(i = 0; i < files; ++i) { 586 for(i = 0; i < files; ++i) {
587 tmp = ReadDatabaseLine(tp[i]); 587 tmp = ReadDatabaseLine(tp[i]);
588 if(tmp == NULL) { 588 if(tmp == NULL) {
589 continue; 589 continue;
590 } 590 }
591 fprintf(fp, "%s\n", tmp); 591 fprintf(fp, "%s\n", tmp);
592 } 592 }
593   593  
594 // Close and delete all temporary files. 594 // Close and delete all temporary files.
595 for(i = 0; i < files; ++i) { 595 for(i = 0; i < files; ++i) {
596 fclose(tp[i]); 596 fclose(tp[i]);
597 // Delete temporary file. 597 // Delete temporary file.
598 remove(tmpNames[i]); 598 remove(tmpNames[i]);
599 } 599 }
600   600  
601 if(verbose) { 601 if(verbose) {
602 fprintf(stdout, "\n"); 602 fprintf(stdout, "\n");
603 } 603 }
604   604  
605 fclose(fp); 605 fclose(fp);
606 } 606 }
607   607  
608 /* 608 /*
609 * 609 *
610 * Indexes a "path" by creating a database "dbFile". 610 * Indexes a "path" by creating a database "dbFile".
611 */ 611 */
612 void Gather(char *dbFile, char *path) { 612 void Gather(char *dbFile, char *path) {
613 stringStack *stack = stringStackCreate(1); 613 stringStack *stack = stringStackCreate(1);
614 stats *stats = malloc(sizeof(stats)); 614 stats *stats = malloc(sizeof(stats));
615 char **tmpNames; 615 char **tmpNames;
616 int dbSize, dbLines, tmpFiles, tmpLines; 616 int dbSize, dbLines, tmpFiles, tmpLines;
617 int i; 617 int i;
618   618  
619 // Initialize metrics. 619 // Initialize metrics.
620 stats->dirs = 0; 620 stats->dirs = 0;
621 stats->files = 0; 621 stats->files = 0;
622   622  
623 // Push the first path onto the stack. 623 // Push the first path onto the stack.
624 stringStackPush(stack, path); 624 stringStackPush(stack, path);
625   625  
626 // Generate the database file. 626 // Generate the database file.
627 UpdateDatabase(dbFile, stack, stats); 627 UpdateDatabase(dbFile, stack, stats);
628   628  
629 // Get the database metrics. 629 // Get the database metrics.
630 dbSize = GetDatabaseSize(dbFile); 630 dbSize = GetDatabaseSize(dbFile);
631 dbLines = CountDatabaseLines(dbFile); 631 dbLines = CountDatabaseLines(dbFile);
632   632  
633 // Compute the amount of temporary files needed. 633 // Compute the amount of temporary files needed.
634 tmpFiles = dbSize / MAX_MEM; 634 tmpFiles = dbSize / MAX_MEM;
635   635  
636 // In case no temporary files are required, 636 // In case no temporary files are required,
637 // just sort the database and terminate. 637 // just sort the database and terminate.
638 if(tmpFiles <= 1) { 638 if(tmpFiles <= 1) {
639 SortDatabase(dbFile); 639 SortDatabase(dbFile);
640 return; 640 return;
641 } 641 }
642   642  
643 tmpLines = dbLines / tmpFiles; 643 tmpLines = dbLines / tmpFiles;
644   644  
645 // Create temporary files. 645 // Create temporary files.
646 if((tmpNames = CreateTempFiles(tmpFiles)) == NULL) { 646 if((tmpNames = CreateTempFiles(tmpFiles)) == NULL) {
647 fprintf(stderr, "Unable to create temporary files.\n"); 647 fprintf(stderr, "Unable to create temporary files.\n");
648 return; 648 return;
649 } 649 }
650   650  
651 // Write "tmpLines" to temporary files in "tmpFiles" from "dbFile". 651 // Write "tmpLines" to temporary files in "tmpFiles" from "dbFile".
652 WriteTempFiles(dbFile, tmpNames, tmpFiles, tmpLines, dbLines); 652 WriteTempFiles(dbFile, tmpNames, tmpFiles, tmpLines, dbLines);
653   653  
654 // Sort the temporary files. 654 // Sort the temporary files.
655 for(i = 0; i < tmpFiles; ++i) { 655 for(i = 0; i < tmpFiles; ++i) {
656 SortDatabase(tmpNames[i]); 656 SortDatabase(tmpNames[i]);
657 } 657 }
658   658  
659 MergeDatabase(dbFile, tmpNames, tmpFiles, dbLines); 659 MergeDatabase(dbFile, tmpNames, tmpFiles, dbLines);
660 } 660 }
661   661  
662 void usage(char *name) { 662 void usage(char *name) {
663 fprintf(stdout, "Hunt & Gather - %s, a file index generating tool. \n", name); 663 fprintf(stdout, "Hunt & Gather - %s, a file index generating tool. \n", name);
664 fprintf(stdout, " \n"); 664 fprintf(stdout, " \n");
665 fprintf(stdout, "SYNTAX: %s [-q] DATABASE \n", name); 665 fprintf(stdout, "SYNTAX: %s [-q] DATABASE \n", name);
666 fprintf(stdout, " \n"); 666 fprintf(stdout, " \n");
667 fprintf(stdout, " -q Do not print out any messages. \n"); 667 fprintf(stdout, " -q Do not print out any messages. \n");
668 fprintf(stdout, " \n"); 668 fprintf(stdout, " \n");
669 fprintf(stdout, "DATABASE is a path to where the indexed results will be \n"); 669 fprintf(stdout, "DATABASE is a path to where the indexed results will be \n");
670 fprintf(stdout, "stored for searching with the Hunt tool. \n"); 670 fprintf(stdout, "stored for searching with the Hunt tool. \n");
671 fprintf(stdout, " \n"); 671 fprintf(stdout, " \n");
672 fprintf(stdout, "(c) 2021 Wizardry and Steamworks, MIT. \n"); 672 fprintf(stdout, "(c) 2021 Wizardry and Steamworks, MIT. \n");
673 } 673 }
674   674  
675 /* 675 /*
676 * 676 *
677 * Main entry point. 677 * Main entry point.
678 */ 678 */
679 int main(int argc, char **argv) { 679 int main(int argc, char **argv) {
680 int option; 680 int option;
681 char *dbFile; 681 char *dbFile;
682 struct stat dirStat; 682 struct stat dirStat;
683   683  
684 // Bind handler to SIGINT. 684 // Bind handler to SIGINT.
685 signal(SIGINT, SignalHandler); 685 signal(SIGINT, SignalHandler);
686   686  
687 dbFile = DEFAULT_DATABASE_FILE; 687 dbFile = DEFAULT_DATABASE_FILE;
688 while((option = getopt(argc, argv, "hqd:")) != -1) { 688 while((option = getopt(argc, argv, "hqd:")) != -1) {
689 switch(option) { 689 switch(option) {
690 case 'd': 690 case 'd':
691 dbFile = optarg; 691 dbFile = optarg;
692 break; 692 break;
693 case 'q': 693 case 'q':
694 verbose = FALSE; 694 verbose = FALSE;
695 break; 695 break;
696 case 'h': 696 case 'h':
697 usage(argv[0]); 697 usage(argv[0]);
698 return 0; 698 return 0;
699 case '?': 699 case '?':
700 fprintf(stderr, "Invalid option %ct.\n", optopt); 700 fprintf(stderr, "Invalid option %ct.\n", optopt);
701 return 1; 701 return 1;
702 } 702 }
703 } 703 }
704   704  
705   705  
706 if(optind >= argc) { 706 if(optind >= argc) {
707 usage(argv[0]); 707 usage(argv[0]);
708 return 1; 708 return 1;
709 } 709 }
710   710  
711 stat(argv[optind], &dirStat); 711 stat(argv[optind], &dirStat);
712 if(!S_ISDIR(dirStat.st_mode)) { 712 if(!S_ISDIR(dirStat.st_mode)) {
713 fprintf(stderr, "Path '%s' is not a directory.\n", argv[optind]); 713 fprintf(stderr, "Path '%s' is not a directory.\n", argv[optind]);
714 return 1; 714 return 1;
715 } 715 }
716   716  
717 if(verbose) { 717 if(verbose) {
718 fprintf(stdout, "Gathering to database file: %s\n", dbFile); 718 fprintf(stdout, "Gathering to database file: %s\n", dbFile);
719 } 719 }
720   720  
721 // Gather. 721 // Gather.
722 Gather(dbFile, argv[optind]); 722 Gather(dbFile, argv[optind]);
723   723  
724 return 0; 724 return 0;
725 } 725 }
726   726