/*
 * compress the given file: create a corresponding .gz file and remove the
 * original.
 */
static off_t
file_compress(char *file, char *outfile, size_t outsize)
{
	int in;
	int out;
	off_t size, insize;
#ifndef SMALL
	struct stat isb, osb;
	const suffixes_t *suff;
#endif

	in = open(file, O_RDONLY);
	if (in == -1) {
		maybe_warn("can't open %s", file);
		return -1;
	}

	if (cflag == 0) {
#ifndef SMALL
		if (fstat(in, &isb) == 0) {
			if (isb.st_nlink > 1 && fflag == 0) {
				maybe_warnx("%s has %d other link%s -- "
					    "skipping", file, isb.st_nlink - 1,
					    isb.st_nlink == 1 ? "" : "s");
				close(in);
				return -1;
			}
		}

		if (fflag == 0 && (suff = check_suffix(file, 0))
		    && suff->zipped[0] != 0) {
			maybe_warnx("%s already has %s suffix -- unchanged",
				    file, suff->zipped);
			close(in);
			return -1;
		}
#endif

		/* Add (usually) .gz to filename */
		if ((size_t)snprintf(outfile, outsize, "%s%s",
					file, suffixes[0].zipped) >= outsize)
			memcpy(outfile + outsize - suffixes[0].ziplen - 1,
				suffixes[0].zipped, suffixes[0].ziplen + 1);

#ifndef SMALL
		if (check_outfile(outfile) == 0) {
			close(in);
			return -1;
		}
#endif
	}

	if (cflag == 0) {
		out = open(outfile, O_WRONLY | O_CREAT | O_EXCL, 0600);
		if (out == -1) {
			maybe_warn("could not create output: %s", outfile);
			fclose(stdin);
			return -1;
		}
	} else
		out = STDOUT_FILENO;

	insize = gz_compress(in, out, &size, basename(file), (uint32_t)isb.st_mtime);

	(void)close(in);

	/*
	 * If there was an error, insize will be -1.
	 * If we compressed to stdout, just return the size.
	 * Otherwise stat the file and check it is the correct size.
	 * We only blow away the file if we can stat the output and it
	 * has the expected size.
	 */
	if (cflag != 0)
		return insize == -1 ? -1 : size;

#ifndef SMALL
	if (fstat(out, &osb) != 0) {
		maybe_warn("couldn't stat: %s", outfile);
		goto bad_outfile;
	}

	if (osb.st_size != size) {
		maybe_warnx("output file: %s wrong size (%" PRIdOFF
				" != %" PRIdOFF "), deleting",
				outfile, osb.st_size, size);
		goto bad_outfile;
	}

	copymodes(out, &isb, outfile);
#endif
	if (close(out) == -1)
		maybe_warn("couldn't close output");

	/* output is good, ok to delete input */
	unlink_input(file, &isb);
	return size;

#ifndef SMALL
    bad_outfile:
	if (close(out) == -1)
		maybe_warn("couldn't close output");

	maybe_warnx("leaving original %s", file);
	unlink(outfile);
	return size;
#endif
}
/* uncompress the given file and remove the original */
static off_t
file_uncompress(char *file, char *outfile, size_t outsize)
{
	struct stat isb, osb;
	off_t size;
	ssize_t rbytes;
	unsigned char header1[4];
	enum filetype method;
	int fd, ofd, zfd = -1;
#ifndef SMALL
	ssize_t rv;
	time_t timestamp = 0;
	char name[PATH_MAX + 1];
#endif

	/* gather the old name info */

	fd = open(file, O_RDONLY);
	if (fd < 0) {
		maybe_warn("can't open %s", file);
		goto lose;
	}

	strlcpy(outfile, file, outsize);
	if (check_suffix(outfile, 1) == NULL && !(cflag || lflag)) {
		maybe_warnx("%s: unknown suffix -- ignored", file);
		goto lose;
	}

	rbytes = read(fd, header1, sizeof header1);
	if (rbytes != sizeof header1) {
		/* we don't want to fail here. */
#ifndef SMALL
		if (fflag)
			goto lose;
#endif
		if (rbytes == -1)
			maybe_warn("can't read %s", file);
		else
			goto unexpected_EOF;
		goto lose;
	}

	method = file_gettype(header1);
#ifndef SMALL
	if (fflag == 0 && method == FT_UNKNOWN) {
		maybe_warnx("%s: not in gzip format", file);
		goto lose;
	}

#endif

#ifndef SMALL
	if (method == FT_GZIP && Nflag) {
		unsigned char ts[4];	/* timestamp */

		rv = pread(fd, ts, sizeof ts, GZIP_TIMESTAMP);
		if (rv >= 0 && rv < (ssize_t)(sizeof ts))
			goto unexpected_EOF;
		if (rv == -1) {
			if (!fflag)
				maybe_warn("can't read %s", file);
			goto lose;
		}
		timestamp = ts[3] << 24 | ts[2] << 16 | ts[1] << 8 | ts[0];

		if (header1[3] & ORIG_NAME) {
			rbytes = pread(fd, name, sizeof(name) - 1, GZIP_ORIGNAME);
			if (rbytes < 0) {
				maybe_warn("can't read %s", file);
				goto lose;
			}
			if (name[0] != '\0') {
				char *dp, *nf;

				/* Make sure that name is NUL-terminated */
				name[rbytes] = '\0';

				/* strip saved directory name */
				nf = strrchr(name, '/');
				if (nf == NULL)
					nf = name;
				else
					nf++;

				/* preserve original directory name */
				dp = strrchr(file, '/');
				if (dp == NULL)
					dp = file;
				else
					dp++;
				snprintf(outfile, outsize, "%.*s%.*s",
						(int) (dp - file), 
						file, (int) rbytes, nf);
			}
		}
	}
#endif
	lseek(fd, 0, SEEK_SET);

	if (cflag == 0 || lflag) {
		if (fstat(fd, &isb) != 0)
			goto lose;
#ifndef SMALL
		if (isb.st_nlink > 1 && lflag == 0 && fflag == 0) {
			maybe_warnx("%s has %d other links -- skipping",
			    file, isb.st_nlink - 1);
			goto lose;
		}
		if (nflag == 0 && timestamp)
			isb.st_mtime = timestamp;
		if (check_outfile(outfile) == 0)
			goto lose;
#endif
	}

	if (cflag == 0 && lflag == 0) {
		zfd = open(outfile, O_WRONLY|O_CREAT|O_EXCL, 0600);
		if (zfd == STDOUT_FILENO) {
			/* We won't close STDOUT_FILENO later... */
			zfd = dup(zfd);
			close(STDOUT_FILENO);
		}
		if (zfd == -1) {
			maybe_warn("can't open %s", outfile);
			goto lose;
		}
	} else
		zfd = STDOUT_FILENO;

	switch (method) {
#ifndef NO_BZIP2_SUPPORT
	case FT_BZIP2:
		/* XXX */
		if (lflag) {
			maybe_warnx("no -l with bzip2 files");
			goto lose;
		}

		size = unbzip2(fd, zfd, NULL, 0, NULL);
		break;
#endif

#ifndef NO_COMPRESS_SUPPORT
	case FT_Z: {
		FILE *in, *out;

		/* XXX */
		if (lflag) {
			maybe_warnx("no -l with Lempel-Ziv files");
			goto lose;
		}

		if ((in = zdopen(fd)) == NULL) {
			maybe_warn("zdopen for read: %s", file);
			goto lose;
		}

		out = fdopen(dup(zfd), "w");
		if (out == NULL) {
			maybe_warn("fdopen for write: %s", outfile);
			fclose(in);
			goto lose;
		}

		size = zuncompress(in, out, NULL, 0, NULL);
		/* need to fclose() if ferror() is true... */
		if (ferror(in) | fclose(in)) {
			maybe_warn("failed infile fclose");
			unlink(outfile);
			(void)fclose(out);
		}
		if (fclose(out) != 0) {
			maybe_warn("failed outfile fclose");
			unlink(outfile);
			goto lose;
		}
		break;
	}
#endif

#ifndef NO_PACK_SUPPORT
	case FT_PACK:
		if (lflag) {
			maybe_warnx("no -l with packed files");
			goto lose;
		}

		size = unpack(fd, zfd, NULL, 0, NULL);
		break;
#endif

#ifndef NO_XZ_SUPPORT
	case FT_XZ:
		if (lflag) {
			maybe_warnx("no -l with xz files");
			goto lose;
		}

		size = unxz(fd, zfd, NULL, 0, NULL);
		break;
#endif

#ifndef SMALL
	case FT_UNKNOWN:
		if (lflag) {
			maybe_warnx("no -l for unknown filetypes");
			goto lose;
		}
		size = cat_fd(NULL, 0, NULL, fd);
		break;
#endif
	default:
		if (lflag) {
			print_list(fd, isb.st_size, outfile, isb.st_mtime);
			close(fd);
			return -1;	/* XXX */
		}

		size = gz_uncompress(fd, zfd, NULL, 0, NULL, file);
		break;
	}

	if (close(fd) != 0)
		maybe_warn("couldn't close input");
	if (zfd != STDOUT_FILENO && close(zfd) != 0)
		maybe_warn("couldn't close output");

	if (size == -1) {
		if (cflag == 0)
			unlink(outfile);
		maybe_warnx("%s: uncompress failed", file);
		return -1;
	}

	/* if testing, or we uncompressed to stdout, this is all we need */
#ifndef SMALL
	if (tflag)
		return size;
#endif
	/* if we are uncompressing to stdin, don't remove the file. */
	if (cflag)
		return size;

	/*
	 * if we create a file...
	 */
	/*
	 * if we can't stat the file don't remove the file.
	 */

	ofd = open(outfile, O_RDWR, 0);
	if (ofd == -1) {
		maybe_warn("couldn't open (leaving original): %s",
			   outfile);
		return -1;
	}
	if (fstat(ofd, &osb) != 0) {
		maybe_warn("couldn't stat (leaving original): %s",
			   outfile);
		close(ofd);
		return -1;
	}
	if (osb.st_size != size) {
		maybe_warnx("stat gave different size: %" PRIdOFF
				" != %" PRIdOFF " (leaving original)",
				size, osb.st_size);
		close(ofd);
		unlink(outfile);
		return -1;
	}
	unlink_input(file, &isb);
#ifndef SMALL
	copymodes(ofd, &isb, outfile);
#endif
	close(ofd);
	return size;

    unexpected_EOF:
	maybe_warnx("%s: unexpected end of file", file);
    lose:
	if (fd != -1)
		close(fd);
	if (zfd != -1 && zfd != STDOUT_FILENO)
		close(fd);
	return -1;
}
int main(int argc, char **argv)
{
   struct ftdi_context *ftdi;
   int err, c;
   FILE *of = NULL;
   char const *outfile  = 0;
   outputFile =0;
   exitRequested = 0;
   char *descstring = NULL;
   int option_index;
   static struct option long_options[] = {{NULL},};

   while ((c = getopt_long(argc, argv, "P:n", long_options, &option_index)) !=- 1)
       switch (c) 
       {
       case -1:
           break;
       case 'P':
           descstring = optarg;
           break;
       case 'n':
           check = 0;
           break;
       default:
           usage(argv[0]);
       }
   
   if (optind == argc - 1)
   {
       // Exactly one extra argument- a dump file
       outfile = argv[optind];
   }
   else if (optind < argc)
   {
       // Too many extra args
       usage(argv[0]);
   }
   
   if ((ftdi = ftdi_new()) == 0)
   {
       fprintf(stderr, "ftdi_new failed\n");
       return EXIT_FAILURE;
   }
   
   if (ftdi_set_interface(ftdi, INTERFACE_A) < 0)
   {
       fprintf(stderr, "ftdi_set_interface failed\n");
       ftdi_free(ftdi);
       return EXIT_FAILURE;
   }
   
   if (ftdi_usb_open_desc(ftdi, 0x0403, 0x6014, descstring, NULL) < 0)
   {
       fprintf(stderr,"Can't open ftdi device: %s\n",ftdi_get_error_string(ftdi));
       ftdi_free(ftdi);
       return EXIT_FAILURE;
   }
   
   /* A timeout value of 1 results in may skipped blocks */
   if(ftdi_set_latency_timer(ftdi, 2))
   {
       fprintf(stderr,"Can't set latency, Error %s\n",ftdi_get_error_string(ftdi));
       ftdi_usb_close(ftdi);
       ftdi_free(ftdi);
       return EXIT_FAILURE;
   }
   
/*   if(ftdi_usb_purge_rx_buffer(ftdi) < 0)
   {
       fprintf(stderr,"Can't rx purge\n",ftdi_get_error_string(ftdi));
       return EXIT_FAILURE;
       }*/
   if (outfile)
       if ((of = fopen(outfile,"w+")) == 0)
           fprintf(stderr,"Can't open logfile %s, Error %s\n", outfile, strerror(errno));
   if (of)
       if (setvbuf(of, NULL, _IOFBF , 1<<16) == 0)
           outputFile = of;
   signal(SIGINT, sigintHandler);
   
   err = ftdi_readstream(ftdi, readCallback, NULL, 8, 256);
   if (err < 0 && !exitRequested)
       exit(1);
   
   if (outputFile) {
       fclose(outputFile);
       outputFile = NULL;
   }
   fprintf(stderr, "Capture ended.\n");
   
   if (ftdi_set_bitmode(ftdi,  0xff, BITMODE_RESET) < 0)
   {
       fprintf(stderr,"Can't set synchronous fifo mode, Error %s\n",ftdi_get_error_string(ftdi));
       ftdi_usb_close(ftdi);
       ftdi_free(ftdi);
       return EXIT_FAILURE;
   }
   ftdi_usb_close(ftdi);
   ftdi_free(ftdi);
   signal(SIGINT, SIG_DFL);
   if (check && outfile)
   {
       if ((outputFile = fopen(outfile,"r")) == 0)
       {
           fprintf(stderr,"Can't open logfile %s, Error %s\n", outfile, strerror(errno));
           ftdi_usb_close(ftdi);
           ftdi_free(ftdi);
           return EXIT_FAILURE;
       }
       check_outfile(descstring);
       fclose(outputFile);
   }
   else if (check)
       fprintf(stderr,"%d errors of %llu blocks (%Le), %d (%Le) blocks skipped\n",
               n_err, (unsigned long long) blocks, (long double)n_err/(long double) blocks,
               skips, (long double)skips/(long double) blocks);
   exit (0);
}