HuntnGather – Diff between revs 1 and 2

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