HuntnGather – Diff between revs 16 and 19

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