/************************************************************************************************** $Header: /pub/cvsroot/yencode/src/yencode.c,v 1.30 2002/03/18 17:20:06 bboy Exp $ Copyright (C) 2002 Don Moore 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: */