HuntnGather – Diff between revs 5 and 7

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