HuntnGather – Diff between revs 7 and 8

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