/**************************************************************************************************
	$Header: /pub/cvsroot/yencode/src/yencode.c,v 1.30 2002/03/18 17:20:06 bboy Exp $

	Copyright (C) 2002  Don Moore <bboy@bboy.net>

	This program is free software; you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation; either version 2 of the License, or
	(at Your option) any later version.

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with this program; if not, write to the Free Software
	Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
**************************************************************************************************/

#include "y.h"


int		opt_verbose = 1;							/* Should the program be verbose in its operation? */
int		opt_debug = 0;								/* Debug output? */
size_t	opt_multipart_size = (size_t)0;		/* Split size for multipart if splitting is desired */
int		opt_overwrite = 0;						/* Overwrite existing files? */
char		*opt_output_dir = NULL;					/* Write output to this directory */
char		*opt_extension = Y_EXT;					/* File extension */
int		opt_line_length = Y_LL;					/* Line length */
int		opt_keep_paths = 0;						/* Strip paths from filenames? */

YENCFILE	**input_files = (YENCFILE **)NULL;	/* List of files to process */
int		num_input_files = 0;						/* Number of items in files */

/* Optional common support files */
int		opt_sfv = 0;								/* Create .SFV file? */
char		*opt_sfv_filename = (char *)NULL;	/* SFV filename */
int		opt_crc = 0;								/* Create .CRC file? */
char		*opt_crc_filename = (char *)NULL;	/* CRC filename */


/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
	USAGE
	Display program usage information.
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
void
usage(int status)
{
	if (status != EXIT_SUCCESS)
	{
		fprintf(stderr, _("Try `%s --help' for more information."), progname);
		fputs("\n", stderr);
	}
	else
	{
		printf(_("Usage: %s [OPTION]... FILE..."), progname);
		puts("");
      puts(_("Usenet file encoder."));
		puts("");
//		puts("----------------------------------------------------------------------------78");
		puts(_("  -d, --debug           output extra debugging information while running"));
		puts(_("  -e, --extension=EXT   use EXT for file extension instead of the default"));
		puts(_("  -f, --force           overwrite existing files, never prompt"));
		puts(_("  -l, --line=LEN        output lines that are LEN bytes in length"));
		puts(_("  -m, --multipart=SIZE  create multipart output each containing SIZE bytes"));
		puts(_("                        (defaults to 620k if SIZE is not specified)"));
		puts(_("  -o, --output=DIR      create output in DIR instead of the current dir"));
		puts(_("  -p, --paths           maintain paths in input filenames"));
		puts(_("  -q, --quiet           inhibit all messages written to the standard output"));
		puts(_("      --sfv=NAME        create SFV checksum file for all input files"));
		puts(_("      --crc=NAME        create CRC checksum file for all input files"));
		puts(_("      --help            display this help and exit"));
		puts(_("      --version         output version information and exit"));
		puts("");
      puts(_("Report bugs to bugs@yencode.org."));
	}
	exit(status);
}
/*--- usage() -----------------------------------------------------------------------------------*/


/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
	CMDLINE
	Process command line options.
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
static void
cmdline(int argc, char **argv)
{
	int  optc, optindex;
	char *optstr;
	struct option const longopts[] =
	{
		{"debug",			no_argument,			NULL,	'd'},
		{"extension",		required_argument,	NULL,	'e'},
		{"force",			no_argument,			NULL,	'f'},
		{"line",				required_argument,	NULL,	'l'},
		{"multipart",		optional_argument,	NULL,	'm'},
		{"output",			required_argument,	NULL,	'o'},
		{"paths",			no_argument,			NULL,	'p'},
		{"quiet",			no_argument,			NULL,	'q'},
		{"sfv",				optional_argument,	0,		0},
		{"crc",				optional_argument,	0,		0},
		{"help",				no_argument,			0,		0},
		{"version",			no_argument,			0,		0},

		{NULL, 0, NULL, 0}
	};

	optstr = getoptstr(longopts);
	while ((optc = getopt_long(argc, argv, optstr, longopts, &optindex)) != -1)
	{
		switch (optc)
		{
			case 0:
				{
					const char *opt = longopts[optindex].name;

					if (!strcmp(opt, "version"))									// --version
					{
						printf("%s - " PACKAGE " " VERSION "\n", short_progname);
						exit(EXIT_SUCCESS);
					}
					else if (!strcmp(opt, "help"))								// --help
						usage(EXIT_SUCCESS);
					else if (!strcmp(opt, "sfv"))									// --sfv
					{
						opt_sfv = 1;
						opt_sfv_filename = optarg;
					}
					else if (!strcmp(opt, "crc"))									// --crc
					{
						opt_crc = 1;
						opt_crc_filename = optarg;
					}
				}
				break;

			case 'd':																	// -d, --debug
				opt_debug = opt_verbose = 1;
				break;

			case 'e':																	// -e, --extension=EXT
				opt_extension = optarg;
				break;

			case 'f':																	// -f, --force
				opt_overwrite = 1;
				break;

			case 'l':																	// -l, --line
				opt_line_length = atoi(optarg);
				if (opt_line_length > 254)
					Warn(_("line lengths greater than 254 are not allowed by the yEnc specification"));
				break;

			case 'm':																	// -m, --multipart=SIZE
				opt_multipart_size = 620000;
				if (optarg)
					opt_multipart_size = human_file_size(optarg);
				break;

			case 'o':																	// -o, --output=DIR
				{
					struct stat st;
					if (stat(optarg, &st))
						ErrERR("%s", optarg);
					if (!S_ISDIR(st.st_mode))
						Err("%s: %s", optarg, _("not a directory"));
					opt_output_dir = optarg;
					while (opt_output_dir[strlen(opt_output_dir)-1] == '/')
						opt_output_dir[strlen(opt_output_dir)-1] = '\0';
				}
				break;

			case 'p':																	// -p, --paths
				opt_keep_paths = 1;
				break;

			case 'q':																	// -q, --quiet
				opt_debug = opt_verbose = 0;
				break;

				default:
				usage(EXIT_FAILURE);
		}
	}

	/* Set these options for the routines in "error.c" in the library */
	err_debug = opt_debug;
	err_verbose = opt_verbose;

	while (optind < argc)
	{
		YENCFILE *y = yencfile_create(argv[optind++], opt_output_dir, YSUPPORT_NOT_SPECIAL, opt_multipart_size);
		if (y)
		{
			input_files = (YENCFILE **)xrealloc(input_files, (num_input_files + 1) * sizeof(YENCFILE *));
			input_files[num_input_files++] = y;
		}
	}

	/* Some input must be specified.. */
	if (!num_input_files)
	{
		Warn(_("no input files"));
		usage(EXIT_FAILURE);
	}

	/* Make sure split size is sane */
	if (opt_multipart_size && opt_multipart_size < opt_line_length)
	{
		Warn(_("multipart file size (from -m) may not be less than line length (%d)"), opt_line_length);
		usage(EXIT_FAILURE);
	}
}
/*--- cmdline() ---------------------------------------------------------------------------------*/


/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
	Outputs a summary line showing filenames, compression factor, etc.
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
static void
output_summary(const char *infile, const char *outfile, int part, int tpart,
					size_t rawlen, size_t enclen)
{
	struct stat st;
	size_t z;

	if (!opt_verbose)
		return;

	z = (stat(outfile, &st)) ? enclen : st.st_size;
	usermsg(infile, outfile, part, tpart, _("file OK"), "(%.1f%%)", PCT(rawlen,z));
}
/*--- output_summary() --------------------------------------------------------------------------*/


/* Macro to "output" a byte using the output buffer */
#define OUT(c)																					\
	outbuf[ob] = (unsigned char)c; 														\
	if (++ob == BUFSIZ) 																		\
	{ 																								\
		if (out && (fwrite(outbuf, sizeof(unsigned char), ob, out) != ob))	\
			ErrERR("%s", outfile);															\
		ob = 0;																					\
	}

/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
	YENCODE_DATA
	Copies data from `in' to `out', storing values in crc, pcrc, elen, and dlen if specified.
	`max_size' is the max number of data bytes to encode.
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
static size_t
yencode_data(FILE *in, const char *infile, FILE *out, const char *outfile,
				 crc32_t *crcp, crc32_t *pcrcp, size_t *elen, size_t *dlen,
				 size_t max_size)
{
	unsigned char inbuf[BUFSIZ], outbuf[BUFSIZ];			/* Output buffer */
	register int	ob;											/* Offset in outbuf */
	register int	ct;											/* Counter in buf */
	register unsigned char c;									/* Current character */
	register size_t linect = 0;								/* Number of bytes output this line */
	register size_t total = 0;									/* Total bytes output */
	register size_t want, rb;									/* Bytes we want to read & actually read */

	/* Set initial value for `want', reset output buffer */
	want = (max_size && (max_size < BUFSIZ)) ? max_size : BUFSIZ;
	ob = 0;

	while ((rb = fread(inbuf, sizeof(unsigned char), want, in)) > 0)
	{
		for (ct = 0; ct < rb; ct++)
		{
			c = inbuf[ct];
			total++;
			if (crcp) CRC_UPDATE(*crcp, c);
			if (pcrcp) CRC_UPDATE(*pcrcp, c);
			c = YENCODE(c);
			if (elen) *elen += 1;
			if (dlen) *dlen += 1;
			if (YSHOULD_ESCAPE(c,linect,opt_line_length))
			{
				OUT('=');
				c = YESCAPE(c);
				if (elen) *elen += 1;
				linect++;
			}
			OUT(c);
			linect++;
			if (linect >= opt_line_length)
			{
				OUT('\r');
				OUT('\n');
				if (elen) *elen += 2;
				linect = 0;
			}
		}
		if (max_size && total >= max_size)
			break;
		if (rb < want)
			break;
		/* Set amount to read on the next pass */
		want = (max_size && (max_size - total < BUFSIZ)) ? max_size - total : BUFSIZ;
	}

	/* Close the current line if we've output any data */
	if (linect)
	{
		OUT('\r');
		OUT('\n');
		if (elen) *elen += 2;
	}

	if (ob && out && (fwrite(outbuf, sizeof(unsigned char), ob, out) != ob))
		ErrERR("%s", outfile);

	if (ferror(in))
		ErrERR("%s", infile);

	return (total);
}
/*--- yencode_data() ----------------------------------------------------------------------------*/


/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
	YENCODE_FILE
	Encode single or multipart data.
	Returns 1 if encoded successfully, 0 if not (wierd return value so we can count the number
	of successful files)
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
static int
yencode_file(YENCFILE *f, FILE *in, size_t insize)
{
	FILE		*out = NULL;										/* Output file */
	char		outfile[PATH_MAX];								/* Output file name */
	crc32_t	pcrc32, crc32;										/* CRC value for this part, file */
	int		part = 0, p;										/* This part number */
	size_t	encsize = 0, decsize = 0;						/* Encoded/decoded size for entire file */
	size_t	encpart = 0, decpart = 0;						/* Encoded/decoded size for this part */
	size_t	e, d;													/* Return data from yencode_data() */
	int		rv = 1;												/* Return value from yencode_data */
	size_t	headers;												/* Length of headers for this file */
	int		totalparts = 1;									/* Total number of parts to this archive */

	if (opt_multipart_size && insize > opt_multipart_size)
	{
		totalparts = insize / opt_multipart_size;
		if (insize % opt_multipart_size)
			totalparts++;
	}
	else
		totalparts = 1;

	CRC_START(crc32);
	while (!feof(in))
	{
		/* Set part counters */
		part++;
		encpart = decpart = e = d = 0;

		/* Open output file for this part */
		if (opt_multipart_size)
			snprintf(outfile, sizeof(outfile), "%s-%03d.%s", f->output_prefix, part, opt_extension);
		else
			snprintf(outfile, sizeof(outfile), "%s.%s", f->output_prefix, opt_extension);
		if (!(out = open_output_file(outfile, opt_overwrite, f->input_filename)))
			return (0);

		/* Write headers */
		if (opt_multipart_size)
		{
			headers = fprintf(out, "=ybegin part=%d total=%d line=%d size=%u name=%s\r\n",
					  part, totalparts, opt_line_length, insize, STRIP_PATH(f->input_filename));
			{
				size_t remaining = insize - decsize;
				off_t	 begin = decsize + 1;

				if (remaining < opt_multipart_size)
					headers += fprintf(out, "=ypart begin=%lu end=%lu\r\n",
												  (unsigned long)begin, (unsigned long)(begin+remaining-1));
				else
					headers += fprintf(out, "=ypart begin=%lu end=%lu\r\n",
												  (unsigned long)begin, (unsigned long)(begin+opt_multipart_size-1));
			}
		}
		else
		{
			headers = fprintf(out, "=ybegin line=%d size=%u name=%s\r\n",
										 opt_line_length, insize, STRIP_PATH(f->input_filename));
		}

		/* Fill this file */
		CRC_START(pcrc32);
		rv = yencode_data(in, f->input_filename, out, outfile, &crc32, &pcrc32, &e, &d, opt_multipart_size);
		encpart += e; decpart += d;
		encsize += e; decsize += d;
		CRC_FINISH(pcrc32);

		/* Write footers */
		if (opt_multipart_size)
		{
			headers += fprintf(out, "=yend part=%d total=%d size=%u pcrc32=%08X",
										  part, totalparts, decpart, pcrc32);
		}
		else
		{
			CRC_FINISH(crc32);
			headers += fprintf(out, "=yend size=%u crc32=%08X\r\n", insize, crc32);
			f->ok = 1;
			f->crc = crc32;
		}

		/* Close this file */
		fclose(out);

		output_summary(f->input_filename, outfile, part, totalparts, decpart, encpart + headers);

		if (decsize >= insize)
			break;
	}

	/* Append the final CRC to the last output file, and CRLF's to the rest */
	if (opt_multipart_size)
	{
		CRC_FINISH(crc32);

		for (p = 1; p <= part; p++)
		{
			snprintf(outfile, sizeof(outfile), "%s-%03d.%s", f->output_prefix, p, opt_extension);
			if (!(out = fopen(outfile, "a")))
				ErrERR("%s", outfile);
			if (p == part)
				fprintf(out, " crc32=%08X\r\n", crc32);
			else
				fprintf(out, "\r\n");
			fclose(out);
		}

		f->ok = 1;
		f->crc = crc32;
	}
	return (f->ok);
}
/*--- yencode_file() ----------------------------------------------------------------------------*/


/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
	Encodes the specified file in yEnc format.
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
static int
yencode(YENCFILE *f)
{
	struct stat st;												/* File stat info */
	FILE			*fp;												/* Input file pointer */
	int			rv;

	/*
	**  Verify and open the file to be encoded
	*/
	if (stat(f->input_filename, &st))
	{
		WarnERR("%s", f->input_filename);
		return (0);
	}
	if (!S_ISREG(st.st_mode))
	{
		Warn("%s: %s", f->input_filename, _("not a regular file"));
		return (0);
	}
	if (!(fp = fopen(f->input_filename, "rb")))
	{
		WarnERR("%s", f->input_filename);
		return (0);
	}

	/*
	**  Create the encoded file(s)
	*/
	rv = yencode_file(f, fp, st.st_size);
	fclose(fp);

	return (rv);
}
/*--- yencode() ---------------------------------------------------------------------------------*/


/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
	MAIN
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
int
main(int argc, char **argv)
{
	register int ct, good;

	set_progname(argv[0]);
	setlocale(LC_ALL, "");
	bindtextdomain(PACKAGE, LOCALEDIR);
	textdomain(PACKAGE);
	cmdline(argc, argv);
	qsort(input_files, num_input_files, sizeof(YENCFILE *), yencfile_cmp);

	/*
	**  Encode all files
	*/
	for (good = ct = 0; ct < num_input_files; ct++)
		good += yencode(input_files[ct]);

	/*
	**  Create support files if requested
	*/
	if (good)
	{
		if (opt_sfv)
			sfv_create(opt_sfv_filename, input_files, num_input_files, opt_output_dir);
		if (opt_crc)
			crc_create(opt_crc_filename, input_files, num_input_files, opt_output_dir);
	}

	return (EXIT_SUCCESS);
}
/*--- main() ------------------------------------------------------------------------------------*/

/* vi:set ts=3: */


syntax highlighted by Code2HTML, v. 0.9.1