1 /*************************************************************************
2 
3 $Id: bsdsfv.c,v 1.18 2003/10/05 01:07:45 webbie Exp $
4 
5 Copyright (c) 2000-2003, webbie@ipfw.org
6 All rights reserved.
7 
8 Redistribution and use in source and binary forms, with or without
9 modification, are permitted provided that the following conditions are
10 met:
11 
12 Redistributions of source code must retain the above copyright notice,
13 this list of conditions and the following disclaimer.
14 
15 Redistributions in binary form must reproduce the above copyright notice,
16 this list of conditions and the following disclaimer in the documentation
17 and/or other materials provided with the distribution.
18 
19 Neither name of the Webbie nor the names of its contributors may be
20 used to endorse or promote products derived from this software without
21 specific prior written permission.
22 
23 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24 ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
25 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
26 PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR
27 CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
28 EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
29 PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
30 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
31 LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
32 NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
33 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34 
35 *************************************************************************/
36 
37 
38 #define FNAMELEN		250
39 #define MAXSFVFILE		1024
40 #define BSDSFV_VERSION	"BSDSFV for UNiX, $Revision: 1.18 $"
41 #define MISSINGTAG 		".missing"
42 #define BADTAG 			".bad"
43 
44 typedef struct sfvtable {
45 	char filename[FNAMELEN];
46 	int crc;
47 	int found;
48 } SFVTABLE;
49 
50 
51 #include <stdlib.h>
52 #include <stdio.h>
53 #include <string.h>
54 #include <time.h>
55 #include <unistd.h>
56 #include <ctype.h>
57 #include <sys/param.h>
58 #include <sys/types.h>
59 #include <sys/stat.h>
60 #include <sys/mman.h>
61 #include <dirent.h>
62 
63 
64 long
UpdateCRC(unsigned long CRC,const char * buffer,long count)65 UpdateCRC(unsigned long CRC, const char *buffer, long count)
66 {
67 	/*
68 	 * Note: if you want to know how CRC32-checking works, I
69 	 * recommend grabbing any old (mid 90's) Z-Modem source.
70 	 * There is not much you can change in this function, so
71 	 * if you need a CRC32-check yourself, feel free to rip.
72 	 */
73 	unsigned long CRCTABLE[] = {
74 		0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419,
75 		    0x706af48f,
76 		0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e,
77 		    0x97d2d988,
78 		0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064,
79 		    0x6ab020f2,
80 		0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551,
81 		    0x83d385c7,
82 		0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f,
83 		    0x63066cd9,
84 		0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4,
85 		    0xa2677172,
86 		0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa,
87 		    0x42b2986c,
88 		0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf,
89 		    0xabd13d59,
90 		0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5,
91 		    0x56b3c423,
92 		0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2,
93 		    0xb10be924,
94 		0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190,
95 		    0x01db7106,
96 		0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5,
97 		    0xe8b8d433,
98 		0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb,
99 		    0x086d3d2d,
100 		0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8,
101 		    0xf262004e,
102 		0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6,
103 		    0x12b7e950,
104 		0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3,
105 		    0xfbd44c65,
106 		0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541,
107 		    0x3dd895d7,
108 		0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846,
109 		    0xda60b8d0,
110 		0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c,
111 		    0x270241aa,
112 		0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409,
113 		    0xce61e49f,
114 		0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17,
115 		    0x2eb40d81,
116 		0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c,
117 		    0x74b1d29a,
118 		0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12,
119 		    0x94643b84,
120 		0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27,
121 		    0x7d079eb1,
122 		0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d,
123 		    0x806567cb,
124 		0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a,
125 		    0x67dd4acc,
126 		0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8,
127 		    0xa1d1937e,
128 		0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd,
129 		    0x48b2364b,
130 		0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3,
131 		    0xa867df55,
132 		0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0,
133 		    0x5268e236,
134 		0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe,
135 		    0xb2bd0b28,
136 		0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b,
137 		    0x5bdeae1d,
138 		0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9,
139 		    0xeb0e363f,
140 		0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae,
141 		    0x0cb61b38,
142 		0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4,
143 		    0xf1d4e242,
144 		0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1,
145 		    0x18b74777,
146 		0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff,
147 		    0xf862ae69,
148 		0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354,
149 		    0x3903b3c2,
150 		0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a,
151 		    0xd9d65adc,
152 		0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f,
153 		    0x30b5ffe9,
154 		0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605,
155 		    0xcdd70693,
156 		0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02,
157 		    0x2a6f2b94,
158 		0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
159 	};
160 
161 	if (buffer && count) {
162 		do {
163 			CRC =
164 			    ((CRC >> 8) & 0xFFFFFF) ^
165 			    CRCTABLE[(unsigned char)((CRC & 0xff) ^
166 				*buffer++)];
167 		}
168 		while (--count);
169 
170 	}
171 	return CRC;
172 }
173 
174 /* ^ UpdateCRC ^ */
175 
176 
177 
178 long
GetFileCRC(char * filename)179 GetFileCRC(char *filename)
180 {
181 	unsigned long crc = 0xffffffff;
182 	FILE *f;
183 	long totalread = 0;
184 	long localread;
185 
186 	/*
187 	 * Note: different buffer sizes may result in noticable
188 	 * different performance depending on system, so feel
189 	 * free to modify.
190 	 */
191 #define BUFFERSIZE 65536*16
192 	char buffer[BUFFERSIZE];
193 
194 	if ((f = fopen(filename, "rb")) != NULL) {
195 		do {
196 			if ((localread = fread(buffer, 1, BUFFERSIZE, f))) {
197 				crc = UpdateCRC(crc, buffer, localread);
198 				totalread = totalread + localread;
199 			}
200 		}
201 		while (localread > 0);
202 		fclose(f);
203 
204 		crc = crc ^ 0xffffffff;
205 	} else {		/* error opening file */
206 
207 		fprintf(stderr, "\nFatal error: cannot read file: %s\n",
208 		    filename);
209 	}
210 
211 	return crc;
212 }
213 
214 int
FileExists(char * name)215 FileExists(char *name)
216 {
217 	FILE *testfile;
218 
219 	testfile = fopen(name, "rb");
220 	if (testfile != NULL) {
221 		fclose(testfile);
222 		return 1;
223 	}
224 	return 0;
225 }
226 
227 
228 int
CheckFileExists(char * name,SFVTABLE sfvTable[],int n)229 CheckFileExists(char *name, SFVTABLE sfvTable[], int n)
230 {
231 	while (n > 0) {
232 		if (!strncasecmp(sfvTable[--n].filename, name, FNAMELEN))
233 			return n;
234 	}
235 	return (-1);
236 }
237 
238 
239 void
usage(char * prog)240 usage(char *prog)
241 {
242 	printf("\nUsage: %s mode [option] sfv-filename [filename...]\n\n",
243 	    prog);
244 	printf
245 	    ("Possible modes: -c : create SFV file (only read .rar file as default, see -a)\n");
246 	printf
247 	    ("                -t : test single file(s) (multiple filenames possible)\n");
248 	printf("                -T : test whole SFV file\n");
249 	printf("                -m : count missing files\n");
250 	printf
251 	    ("Options:        -l <filename> : use <filename> as banner file (create mode)\n");
252 	printf
253 	    ("                -w : Win-SFV emulation mode                   (create mode)\n");
254 	printf
255 	    ("                -a : add all files to .sfv (even .nfo etc.)   (create mode)\n");
256 	printf
257 	    ("                -d : add files' dates & times to SFV file     (create mode)\n");
258 	printf
259 	    ("                -g : glftpd mode     (counting and test whole mode)\n\n");
260 	printf("Examples:\n");
261 	printf
262 	    ("(1) Create test.sfv and add test.* to it       : %s -c test.sfv test.*\n",
263 	    prog);
264 	printf
265 	    ("(2) Check file test.r02 against CRC in test.sfv: %s -t test.sfv test.r02\n",
266 	    prog);
267 	printf
268 	    ("(3) Check all files listed in test.sfv         : %s -T test.sfv\n",
269 	    prog);
270 	printf
271 	    ("(4) Count number of files missing of test.sfv  : %s -m test.sfv\n",
272 	    prog);
273 	printf
274 	    ("(5) Create myrls.sfv with myrls.* in it, use Win-SFV compatibility mode and file\n");
275 	printf
276 	    ("/tmp/mylogo.nfo as banner: %s -c -w -l /tmp/mylogo.nfo myrls.sfv myrls.*\n",
277 	    prog);
278 	printf("\n");
279 	printf
280 	    ("Read documentation files for more help and check for updates regularily!\n\n");
281 	return;
282 }
283 
284 
285 int
main(int argc,char * argv[])286 main(int argc, char *argv[])
287 {
288 	int compatmode = 0;	// Win-SFV compatibility mode
289 	int haslogo = 0;	// add banner to created SFVs
290 	int glftpdmode = 0;	// glftpd mode
291 	int doallfiles = 0;	// include even crappy files to SFV
292 	int addfinfo = 0;	// add files' dates & times to SFV
293 	char logoname[FNAMELEN];	// filename of bannerfile
294 	char sfvname[FNAMELEN];	// name of .sfv file to use
295 	char sfvline[FNAMELEN];	// current line in SFV file
296 	int mode = 0;		// mode of operation
297 	int paramcnt = 0;	// command line parameter offset
298 
299 	SFVTABLE sfvTable[MAXSFVFILE];
300 	char COMPLETETAG[] = "[000\%]-[-all.files.CRC.ok-]";
301 	char OLDTAG[FNAMELEN];
302 	char NEWTAG[FNAMELEN];
303 
304 	FILE *sfvfile;
305 	FILE *logofile;
306 	FILE *missingfile;
307 	DIR *dirp;
308 	struct dirent *dp;
309 	long mycrc;
310 	int cnt;
311 	int dothisone;
312 	char cfname[FNAMELEN];
313 	char crap[FNAMELEN];
314 	char precent[5];
315 	int numfiles = 0;
316 	int badfiles = 0;
317 	int msgfiles = 0;
318 	int missingfiles = 0;
319 	int paramsave;
320 	struct stat fileinfo;
321 	time_t curtime;
322 	struct tm zeit;
323 	char mytime[80];
324 	char ch;
325 
326 // output program name & version number
327 	printf("%s\n", BSDSFV_VERSION);
328 
329 // parse command line
330 	cnt = 0;
331 	while ((ch = getopt(argc, argv, "acdgmtTvwl:")) != -1) {
332 		switch (ch) {
333 		case 'c':	// prepare for create mode
334 			mode = 1;
335 			cnt++;
336 			break;
337 
338 		case 't':	// prepare single file test mode
339 			mode = 2;
340 			cnt++;
341 			break;
342 
343 		case 'T':	// prepare sfv file test mode
344 			mode = 3;
345 			cnt++;
346 			break;
347 
348 		case 'm':	// prepare missing file test mode
349 			mode = 4;
350 			cnt++;
351 			break;
352 
353 		case 'v':	// print version and exit
354 			exit(0);
355 			break;
356 
357 		case 'l':	// set banner file name
358 			haslogo = 1;
359 			sprintf(logoname, "%s", optarg);
360 			break;
361 
362 		case 'a':	// set lame allfiles-add-mode
363 			doallfiles = 1;
364 			break;
365 
366 		case 'w':	// set win-sfv compatibility mode
367 			compatmode = 1;
368 			break;
369 
370 		case 'd':	// add files' dates & times to SFV
371 			addfinfo = 1;
372 			break;
373 
374 		case 'g':	// set glftpd mode
375 			glftpdmode = 1;
376 			break;
377 
378 		case '?':
379 		default:
380 			usage(argv[0]);
381 			return 1;
382 			break;
383 		}
384 	}
385 
386 	if (argc == optind || cnt != 1) {
387 		usage(argv[0]);
388 		return 1;
389 	}
390 	argc -= optind;
391 	argv += optind;
392 
393 	strncpy(sfvname, argv[0], FNAMELEN);
394 	argc--;
395 	argv++;
396 
397 	if (mode == 1) {
398 		printf("\nCreating new checksum file: %s\n", sfvname);
399 
400 		if (haslogo)
401 			printf("Adding banner file        : %s\n", logoname);
402 
403 		if (compatmode)
404 			printf("Using compatibility mode  : Win-SFV\n");
405 
406 		if (doallfiles)
407 			printf("Add-all-files mode active\n");
408 
409 		if (addfinfo)
410 			printf("Adding files' times & dates to SFV file\n");
411 
412 		printf("\n");
413 
414 		sfvfile = fopen(sfvname, "wt");
415 		if (sfvfile == NULL) {
416 			fprintf
417 			    (stderr,
418 			    "Oh mama! We can\'t write to %s (check permissions!), aborting ...\n",
419 			    sfvname);
420 			return 1;
421 		}
422 
423 		time(&curtime);
424 		zeit = *localtime(&curtime);
425 		strftime(mytime, 50, "%Y-%m-%d at %H:%M.%S", &zeit);
426 
427 		if (compatmode)
428 			fprintf(sfvfile,
429 			    "; Generated by WIN-SFV32 v1.1a on %s\r\n;\r\n",
430 			    mytime);
431 		else
432 			fprintf(sfvfile, "; Generated by %s on %s\r\n;\r\n",
433 			    BSDSFV_VERSION, mytime);
434 
435 		if (haslogo) {
436 			logofile = fopen(logoname, "rt");
437 			if (logofile == NULL) {
438 				fprintf
439 				    (stderr,
440 				    "Oh mama! I couldn\'t find logo file %s ... omitting!\n",
441 				    logoname);
442 			} else {
443 				while (fgets(sfvline, FNAMELEN,
444 					logofile) != NULL) {
445 					for (cnt = 0; cnt < strlen(sfvline);
446 					    cnt++)
447 						if (sfvline[cnt] == '\n')
448 							sfvline[cnt] = '\0';
449 					fprintf(sfvfile, "; %s\r\n", sfvline);
450 				}
451 				fclose(logofile);
452 			}
453 		}		// add logo
454 
455 		if (addfinfo) {
456 			paramsave = paramcnt;
457 			while (paramsave < argc) {
458 				stat(argv[paramsave], &fileinfo);
459 
460 				zeit = *localtime(&fileinfo.st_mtime);
461 				strftime(mytime, 50, "%H:%M.%S %Y-%m-%d",
462 				    &zeit);
463 
464 				fprintf(sfvfile, "; %12d  %s %s\r\n",
465 				    (int)fileinfo.st_size, mytime,
466 				    argv[paramsave]);
467 
468 				paramsave++;
469 			}
470 			fprintf(sfvfile, ";\r\n");
471 		}
472 
473 
474 		while (paramcnt < argc) {
475 			sprintf(cfname, "%s", argv[paramcnt]);
476 
477 			// check if we should process this file
478 			dothisone = 1;
479 			if (!doallfiles) {
480 				if (strstr(cfname, ".r") == NULL &&
481 				    strstr(cfname, ".R") == NULL &&
482 				    strstr(cfname, ".0") == NULL)
483 					dothisone = 0;
484 			}
485 
486 			if (dothisone) {
487 				printf("Adding file: %s ... ", cfname);
488 				fflush(stdout);
489 				mycrc = GetFileCRC(cfname);
490 				printf("CRC = 0x%08lX\n", mycrc);
491 				sprintf(sfvline, "%s %08lX", cfname, mycrc);
492 
493 				// uncomment next 2 lines to
494 				// convert filename to upper case, for whatever reason
495 				// for (cnt = 0; sfvline[cnt] != '\0'; cnt++)
496 				//   sfvline[cnt] = toupper (sfvline[cnt]);
497 
498 				fprintf(sfvfile, "%s\r\n", sfvline);
499 				numfiles++;
500 			}
501 			paramcnt++;
502 		}
503 
504 		fclose(sfvfile);
505 
506 		printf
507 		    ("\nSFV file successfully created (%d files processed) ...\n\n",
508 		    numfiles);
509 
510 	}			// create SFV file mode
511 
512 
513 	if (mode == 2) {
514 		while (paramcnt < argc) {
515 			sprintf(cfname, "%s", argv[paramcnt]);
516 			printf("Testing %s ... ", cfname);
517 			fflush(stdout);
518 			mycrc = GetFileCRC(cfname);
519 			printf("local = 0x%08lX, listed = ", mycrc);
520 			fflush(stdout);
521 
522 			sfvfile = fopen(sfvname, "rt");
523 			if (sfvfile == NULL) {
524 				fprintf(stderr,
525 				    "Oh mama! I can\'t open %s ... :-(\n",
526 				    sfvname);
527 				return 1;
528 			}
529 
530 			dothisone = 0;
531 
532 			if (mycrc != 0xffffffff) {
533 				while (fgets(sfvline, FNAMELEN,
534 					sfvfile) != NULL) {
535 					// Skip comments, blank lines, other crap
536 					if (sfvline[0] != ';'
537 					    && sfvline[0] != '#'
538 					    && sfvline[0] != ' '
539 					    && strlen(sfvline) > 8) {
540 						for (cnt = strlen(sfvline); cnt >= 0 && sfvline[cnt] != ' '; cnt--) ;
541 						if (cnt > 0 ) {
542 							strncpy(sfvTable[0].filename, sfvline, cnt);
543 							sscanf(sfvline+cnt+1, "%X", &sfvTable[0].crc);
544 						}
545 
546 						if (strncasecmp(cfname,
547 							sfvTable[0].filename,
548 							strlen(cfname)) == 0) {
549 							dothisone = 1;
550 							sfvTable[0].found = 1;
551 							break;
552 						}
553 					}
554 				}
555 			}
556 
557 			if (dothisone) {
558 				printf("0x%08X - ", sfvTable[0].crc);
559 
560 				if (sfvTable[0].crc == mycrc) {
561 					printf("OK\n");
562 					if (glftpdmode) {
563 						sprintf(crap, "%s%s",
564 						    sfvTable[0].filename,
565 						    MISSINGTAG);
566 						unlink(crap);
567 						sprintf(crap, "%s%s",
568 						    sfvTable[0].filename,
569 						    BADTAG);
570 						unlink(crap);
571 					}
572 				} else {
573 					printf("BAD\n");
574 					badfiles++;
575 					if (glftpdmode) {
576 						sprintf(crap, "%s%s",
577 						    sfvTable[0].filename,
578 						    MISSINGTAG);
579 						unlink(crap);
580 						sprintf(crap, "%s%s",
581 						    sfvTable[0].filename,
582 						    BADTAG);
583 						rename(cfname, crap);
584 					}
585 				}
586 			} else {
587 				printf("MISSING\n");
588 				missingfiles++;
589 			}
590 			numfiles++;
591 			paramcnt++;
592 			fclose(sfvfile);
593 		}
594 
595 		printf
596 		    ("\n%d file(s) tested - %d OK - %d bad - %d missing...\n",
597 		    numfiles, numfiles - badfiles - missingfiles, badfiles,
598 		    missingfiles);
599 		fflush(stdout);
600 
601 
602 		if (badfiles || missingfiles)
603 			return 1;
604 		return 0;
605 	}			// test single file(s) mode
606 
607 
608 	if (mode == 3) {
609 		printf("\nProcessing complete check of %s ...\n", sfvname);
610 
611 		if (glftpdmode)
612 			printf("Using glftpd mode\n");
613 
614 		/*
615 		 * Read the whole sfv file into memory
616 		 */
617 		sfvfile = fopen(sfvname, "rt");
618 		if (sfvfile == NULL) {
619 			fprintf(stderr, "Oh mama! I can\'t open %s ... :-(\n",
620 			    sfvname);
621 			return 1;
622 		}
623 		numfiles = 0;
624 		while (fgets(sfvline, FNAMELEN, sfvfile) != NULL) {
625 			// Skip comments, blank lines, other crap
626 			if (sfvline[0] != ';' && sfvline[0] != '#'
627 			    && sfvline[0] != ' ' && strlen(sfvline) > 8) {
628 				sfvTable[cnt].found = 0;
629 
630 				for (cnt = strlen(sfvline); cnt >= 0 && sfvline[cnt] != ' '; cnt--) ;
631 				if (cnt > 0 ) {
632 					strncpy(sfvTable[numfiles].filename, sfvline, cnt);
633 					sscanf(sfvline+cnt+1, "%X", &sfvTable[numfiles].crc);
634 				}
635 
636 				numfiles++;
637 				if (numfiles >= MAXSFVFILE) {
638 					printf
639 					    ("bsdsfv cannot handle more than %d files\n",
640 					    MAXSFVFILE);
641 					return 1;
642 				}
643 			}
644 		};
645 		fclose(sfvfile);
646 
647 		/*
648 		 * Read current directory file by file
649 		 */
650 		missingfiles = 0;
651 		badfiles = 0;
652 		OLDTAG[0] = '\0';
653 		dirp = opendir(".");
654 		while ((dp = readdir(dirp)) != NULL) {
655 			if (glftpdmode &&
656 			    (dp->d_type == DT_DIR || dp->d_type == DT_UNKNOWN)
657 			    && (strlen(dp->d_name) == strlen(COMPLETETAG))
658 			    && (dp->d_name[0] == '[')
659 			    && !strncmp(dp->d_name + 5, COMPLETETAG + 5,
660 				strlen(COMPLETETAG) - 5)) {
661 				strcpy(OLDTAG, dp->d_name);
662 			}
663 			if (dp->d_type == DT_REG || dp->d_type == DT_UNKNOWN) {
664 				cnt =
665 				    CheckFileExists(dp->d_name, sfvTable,
666 				    numfiles);
667 				if (cnt >= 0) {
668 					sfvTable[cnt].found = 1;
669 
670 					printf
671 					    ("Testing %s ... listed = 0x%08X ... ",
672 					    dp->d_name, sfvTable[cnt].crc);
673 
674 					fflush(stdout);
675 
676 					mycrc = GetFileCRC(dp->d_name);
677 
678 					printf("local = 0x%08lX ... ", mycrc);
679 
680 					if (mycrc == sfvTable[cnt].crc) {
681 						printf("OK\n");
682 					} else {
683 						if (mycrc == 0xffffffff) {
684 							printf("MISSING\n");
685 							missingfiles++;
686 						} else {
687 							printf("BAD\n");
688 							badfiles++;
689 						}
690 					}
691 					fflush(stdout);
692 				}
693 			}
694 		}
695 		(void)closedir(dirp);
696 
697 		for (cnt = 0; cnt < numfiles; cnt++) {
698 			if (sfvTable[cnt].found) {
699 				if (glftpdmode) {
700 					sprintf(crap, "%s%s",
701 					    sfvTable[cnt].filename,
702 					    MISSINGTAG);
703 					unlink(crap);
704 				}
705 			} else {
706 				printf
707 				    ("Testing %s ... listed = 0x%08X ... Local = MISSING\n",
708 				    sfvTable[cnt].filename, sfvTable[cnt].crc);
709 				missingfiles++;
710 				if (glftpdmode) {
711 					sprintf(crap, "%s%s",
712 					    sfvTable[cnt].filename,
713 					    MISSINGTAG);
714 					missingfile = fopen(crap, "w+");
715 					if (missingfile != NULL) {
716 						fclose(missingfile);
717 					}
718 				}
719 			}
720 		}
721 
722 		if (glftpdmode) {
723 			if (numfiles > 0) {
724 				cnt =
725 				    ((numfiles -
726 					missingfiles) * 100) / numfiles;
727 				if (cnt > 100)
728 					cnt = 100;
729 			} else {
730 				cnt = 0;
731 			}
732 
733 			sprintf(precent, "%.3d", cnt);
734 			strcpy(NEWTAG, COMPLETETAG);
735 			NEWTAG[1] = precent[0];
736 			NEWTAG[2] = precent[1];
737 			NEWTAG[3] = precent[2];
738 
739 #ifdef DEBUG
740 			printf("cnt %d OLD %s NEW %s\n", cnt, OLDTAG, NEWTAG);
741 #endif
742 
743 			if (strcmp(OLDTAG, NEWTAG)) {
744 				rmdir(OLDTAG);
745 				if (mkdir(NEWTAG,
746 					S_IRWXU | S_IRWXG | S_IRWXO) == 0)
747 					chmod(NEWTAG,
748 					    S_IRWXU | S_IRWXG | S_IRWXO);
749 			}
750 			printf("Completion Status: %s\n", NEWTAG);
751 		}
752 
753 		printf
754 		    ("\n%d file(s) tested - %d OK - %d bad - %d missing ...\n\n",
755 		    numfiles, numfiles - (badfiles + missingfiles), badfiles,
756 		    missingfiles);
757 
758 		if (missingfiles)
759 			return 2;
760 
761 		if (badfiles)
762 			return 1;
763 
764 		return 0;
765 	}
766 
767 	/*
768 	 * test whole SFV file mode
769 	 */
770 	if (mode == 4) {
771 		printf("\nPerforming completion check...\n");
772 
773 		if (glftpdmode)
774 			printf("Using glftpd mode\n");
775 
776 		/*
777 		 * Read the whole sfv file into memory
778 		 */
779 		sfvfile = fopen(sfvname, "rt");
780 		if (sfvfile == NULL) {
781 			fprintf(stderr, "Oh mama! I can\'t open %s ... :-(\n",
782 			    sfvname);
783 			return 1;
784 		}
785 		numfiles = 0;
786 		while (fgets(sfvline, FNAMELEN, sfvfile) != NULL) {
787 			// Skip comments, blank lines, other crap
788 			if (sfvline[0] != ';' && sfvline[0] != '#'
789 			    && sfvline[0] != ' ' && strlen(sfvline) > 8) {
790 				sfvTable[cnt].found = 0;
791 				sscanf(sfvline, "%s %X",
792 				    sfvTable[numfiles].filename,
793 				    &sfvTable[numfiles].crc);
794 				numfiles++;
795 				if (numfiles >= MAXSFVFILE) {
796 					printf
797 					    ("bsdsfv cannot handle more than %d files\n",
798 					    MAXSFVFILE);
799 					return 1;
800 				}
801 			}
802 		};
803 		fclose(sfvfile);
804 
805 		/*
806 		 * Read current directory file by file
807 		 */
808 		OLDTAG[0] = '\0';
809 		dirp = opendir(".");
810 		while ((dp = readdir(dirp)) != NULL) {
811 			if (glftpdmode &&
812 			    (dp->d_type == DT_DIR || dp->d_type == DT_UNKNOWN)
813 			    && (strlen(dp->d_name) == strlen(COMPLETETAG))
814 			    && (dp->d_name[0] == '[')
815 			    && !strncmp(dp->d_name + 5, COMPLETETAG + 5,
816 				strlen(COMPLETETAG) - 5)) {
817 				strcpy(OLDTAG, dp->d_name);
818 			}
819 			if (dp->d_type == DT_REG || dp->d_type == DT_UNKNOWN) {
820 				cnt =
821 				    CheckFileExists(dp->d_name, sfvTable,
822 				    numfiles);
823 				if (cnt >= 0) {
824 					sfvTable[cnt].found = 1;
825 				}
826 			}
827 		}
828 		(void)closedir(dirp);
829 
830 		/*
831 		 * Read the sfv array and create/delete missing tag file as required
832 		 */
833 		for (cnt = 0; cnt < numfiles; cnt++) {
834 			if (sfvTable[cnt].found) {
835 				msgfiles++;
836 				if (glftpdmode) {
837 					sprintf(crap, "%s%s",
838 					    sfvTable[cnt].filename,
839 					    MISSINGTAG);
840 					unlink(crap);
841 				}
842 			} else {
843 				if (glftpdmode) {
844 					sprintf(crap, "%s%s",
845 					    sfvTable[cnt].filename,
846 					    MISSINGTAG);
847 					missingfile = fopen(crap, "w+");
848 					if (missingfile != NULL) {
849 						fclose(missingfile);
850 					}
851 				}
852 			}
853 		}
854 
855 /*
856  Make progress directory
857  */
858 		if (glftpdmode) {
859 			if (numfiles > 0) {
860 				cnt = (msgfiles * 100) / numfiles;
861 				if (cnt > 100)
862 					cnt = 100;
863 			} else
864 				cnt = 0;
865 
866 			sprintf(precent, "%.3d", cnt);
867 			strcpy(NEWTAG, COMPLETETAG);
868 			NEWTAG[1] = precent[0];
869 			NEWTAG[2] = precent[1];
870 			NEWTAG[3] = precent[2];
871 
872 #ifdef DEBUG
873 			printf("cnt %d OLD %s NEW %s\n", cnt, OLDTAG, NEWTAG);
874 #endif
875 
876 			if (strcmp(OLDTAG, NEWTAG)) {
877 				rmdir(OLDTAG);
878 				if (mkdir(NEWTAG,
879 					S_IRWXU | S_IRWXG | S_IRWXO) == 0)
880 					chmod(NEWTAG,
881 					    S_IRWXU | S_IRWXG | S_IRWXO);
882 			}
883 			printf("Completion Status: %s\n", NEWTAG);
884 		} else {
885 			printf
886 			    ("[total files listed] [local files] [missing files]\n%-4d                 %-4d          %-4d\n",
887 			    numfiles, msgfiles, numfiles - msgfiles);
888 		}
889 		return 0;
890 	}			/* count files mode */
891 	return 0;
892 }
893