/**************************************************************************************************
$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