Logo Search packages:      
Sourcecode: yencode version File versions

ydecode.c

/**************************************************************************************************
      $Header: /pub/cvsroot/yencode/src/ydecode.c,v 1.27 2002/03/16 05:29:14 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"
#include "file.h"


int         opt_verbose = 1;                                      /* Should the program be verbose in its operation? */
int         opt_debug = 0;                                              /* Debug output? */
int         opt_overwrite = 0;                                    /* Overwrite existing files? */
char        *opt_output_dir = NULL;                         /* Write output to this directory */
int         opt_recursive = 0;                                    /* Recursive scan? */
int         opt_scan = 0;                                               /* Scan files? */
int         opt_keep = 0;                                               /* Keep output files with errors? */
int         opt_keep_paths = 0;                                   /* Keep paths in filenames? */
int         opt_list = 0;                                               /* Only list files found? */
int         opt_test = 0;                                               /* Test files? */
int         opt_strict = 0;                                       /* Strict test? */
int         opt_delete = 0;                                       /* Delete files after decoding? */

char        **dirs = (char **)NULL;                         /* List of directories to process */
int         num_dirs = 0;                                               /* Number of directories in `dirs' list */

YDECFILE    **yfiles = (YDECFILE **)NULL;             /* List of files to process */
int         num_yfiles = 0;                                       /* Number of items in yfiles */

/* Macro for passing filenames to usermsg() */
#define YNAMES(y) (y && y->output_filename) ? y->output_filename : _("<UNKNOWN>"), \
                                    (y && y->input_filename) ? y->input_filename : _("<UNKNOWN>")


/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
      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 decoder."));
            puts("");
//          puts("----------------------------------------------------------------------------78");
            puts(_("  -d, --debug       output extra debugging information while running"));
            puts(_("  -D, --delete      delete input files if decoded successfully"));
            puts(_("  -f, --force       overwrite existing files, never prompt"));
            puts(_("  -k, --keep        keep output files that contain errors"));
            puts(_("  -l, --list        find and list input file information only"));
            puts(_("  -o, --output=DIR  create output in DIR instead of the current dir"));
            puts(_("  -p, --paths       maintain paths in output filenames"));
            puts(_("  -q, --quiet       inhibit all messages written to the standard output"));
            puts(_("  -r, --recursive   scan directories recursively"));
            puts(_("  -s, --scan        scan files if any parts are missing"));
            puts(_("  -t, --test        do not write output files (just test archives)"));
            puts(_("      --strict      perform strict format checks when testing/decoding?"));
            puts(_("      --verify      synonym for `--test --strict'"));
            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() -----------------------------------------------------------------------------------*/


/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
      QUEUE_FILE
      Adds a file to the file or dir list.
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
static void
queue_file(char *filename, int *file_errs)
{
      struct stat st;

      if ((stat(filename, &st)))
      {
            *file_errs += 1;
            return (void)WarnERR("%s", filename);
      }

      /* Add directory to input_dirs */
      if (S_ISDIR(st.st_mode))
      {
            dirs = (char **)xrealloc(dirs, (num_dirs + 1) * sizeof(char *));

            while (filename[strlen(filename)-1] == '/')           /* Strip trailing slashes */
                  filename[strlen(filename)-1] = '\0';

            dirs[num_dirs++] = filename;
      }

      /* Add file to yfiles */
      if (S_ISREG(st.st_mode))
      {
            YDECFILE *y = ydecfile_create(filename, opt_strict);

            /* Don't treat non-yEnc files as errors.. Just ignore them */
            if (!y)
                  return;

            yfiles = (YDECFILE **)xrealloc(yfiles, (num_yfiles + 1) * sizeof(YDECFILE *));
            yfiles[num_yfiles++] = y;
      }

      /* Ignore unsupported file types */
}
/*--- queue_file() ------------------------------------------------------------------------------*/


/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
      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'},
            {"delete",              no_argument,                  NULL, 'D'},
            {"force",               no_argument,                  NULL, 'f'},
            {"keep",                      no_argument,                  NULL, 'k'},
            {"list",                      no_argument,                  NULL, 'l'},
            {"output",              required_argument,      NULL, 'o'},
            {"paths",               no_argument,                  NULL, 'p'},
            {"quiet",               no_argument,                  NULL, 'q'},
            {"recursive",           no_argument,                  NULL, 'r'},
            {"recurse",             no_argument,                  NULL, 'R'},
            {"scan",                      no_argument,                  NULL, 's'},
            {"test",                      no_argument,                  NULL, 't'},
            {"strict",              no_argument,                  0,          0},
            {"verify",              no_argument,                  0,          0},
            {"help",                      no_argument,                  0,          0},
            {"version",             no_argument,                  0,          0},

            {NULL, 0, NULL, 0}
      };
      int   file_errs = 0;

      opt_verbose = 1;
      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, "strict"))                                              // --strict
                                    opt_strict = 1;
                              else if (!strcmp(opt, "verify"))                                              // --verify
                                    opt_strict = opt_test = 1;
                        }
                        break;

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

                  case 'D':                                                                                                   // -D, --delete
                        opt_delete = 1;
                        break;

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

                  case 'k':                                                                                                   // -k, --keep
                        opt_keep = 1;
                        break;

                  case 'l':                                                                                                   // -l, --list
                        opt_list = 1;
                        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;

                  case 'R':
                  case 'r':                                                                                                   // -r, --recursive
                        opt_scan = opt_recursive = 1;
                        break;

                  case 's':                                                                                                   // -s, --scan
                        opt_scan = 1;
                        break;

                  case 't':                                                                                                   // -t, --test
                        opt_test = 1;
                        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)
            queue_file(argv[optind++], &file_errs);
      if (file_errs)
            exit(EXIT_FAILURE);

      /* If nothing was specified, scan by default */
      if (!opt_scan && !opt_recursive && !num_yfiles)
            opt_scan = 1;
}
/*--- cmdline() ---------------------------------------------------------------------------------*/


/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
      SCAN_FILES
      Scans the current directory, the output directory, and any directories recursively.
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
static void
scan_files(const char *path)
{
      DIR                     *dirf;
      struct dirent     *dn;
      struct stat       st;
      char                    filename[PATH_MAX];

      if (!(dirf = opendir(path ? path : ".")))
            return;
      while ((dn = readdir(dirf)))
      {
            if (dn->d_name[0] == '.' && dn->d_name[1] == '\0')
                  continue;
            if (dn->d_name[0] == '.' && dn->d_name[1] == '.' && dn->d_name[2] == '\0')
                  continue;
            if (!path)
                  strncpy(filename, dn->d_name, sizeof(filename)-1);
            else
                  snprintf(filename, sizeof(filename), "%s/%s", path, dn->d_name);
            if (stat(filename, &st))
                  continue;
            if (S_ISDIR(st.st_mode) && opt_recursive)
                  scan_files(filename);
            else if (!S_ISDIR(st.st_mode))
            {
                  YDECFILE *y = ydecfile_create(filename, opt_strict);
                  if (y)
                  {
                        yfiles = (YDECFILE **)xrealloc(yfiles, (num_yfiles + 1) * sizeof(YDECFILE *));
                        yfiles[num_yfiles++] = y;
                  }
            }
      }
      closedir(dirf);
}
/*--- scan_files() ------------------------------------------------------------------------------*/


/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
      SCAN_FOR_FILES
      Scans directories if necessary.
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
static void
scan_for_files(void)
{
      /* Do general recursive scan if no dirs were specified on the command line */
      if (!num_dirs)
            scan_files(NULL);
      else                                                                                      /* Scan specified dirs */
      {
            register int ct;

            for (ct = 0; ct < num_dirs; ct++)
                  scan_files(dirs[ct]);
      }
}
/*--- scan_for_files() --------------------------------------------------------------------------*/


/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
      FLAG_MULTIPART_FILE
      Attempts to determine which of the input files (if any) are multipart archives.
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
static void
flag_multipart_file(YDECFILE *y)
{
      register int ct;

      /* If the part number is 0 (i.e. part not present or '0' explicitly specified) consider
            it a single part archive always */
      if (y->header->part == 0)
            return;

      /* If we found another file with the same name, it's a multipart */
      for (ct = 0; ct < num_yfiles; ct++)
      {
            if (!strcmp(y->header->name, yfiles[ct]->header->name))
            {
                  y->multipart = 1;
                  return;
            }
      }
}
/*--- flag_multipart_file() ---------------------------------------------------------------------*/


/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
      SET_OUTPUT_FILENAME
      Sets output_filename for each item in the list of files.
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
static void
set_output_filename(YDECFILE *y)
{
      char  buf[PATH_MAX];                                                                /* Copy of input filename for mangling */
      char  *path, *file;                                                                 /* Path and file components */
      char  outfile[PATH_MAX];                                                      /* Output filename buffer */

      strncpy(buf, y->header->name, sizeof(buf)-1);
      outfile[0] = '\0';

      /* Start with opt_output_dir if present */
      if (opt_output_dir)
      {
            strncat(outfile, opt_output_dir, sizeof(outfile) - strlen(outfile) - 1);
            if (outfile[strlen(outfile)-1] != '/')
                  strncat(outfile, "/", sizeof(outfile) - strlen(outfile) - 1);
      }

      /* Filename with path */
      if ((file = strrchr(buf, '/')))
      {
            *file++ = '\0';
            path = buf;

            if (opt_output_dir && *path == '/')                         /* Make absolute path relative */
                  path++;

            if (opt_keep_paths)
            {
                  strncat(outfile, path, sizeof(outfile) - strlen(outfile) - 1);
                  if (outfile[strlen(outfile)-1] != '/')
                        strncat(outfile, "/", sizeof(outfile) - strlen(outfile) - 1);
            }
      }
      /* Filename without a path */
      else
      {
            path = NULL;
            file = buf;
      }

      strncat(outfile, file, sizeof(outfile) - strlen(outfile) - 1);
      y->output_filename = xstrdup(outfile);
}
/*--- set_output_filename() ---------------------------------------------------------------------*/


/* 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", y->output_filename);                                                               \
            ob = 0;                                                                                                                             \
      }

/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
      YDECODE_DATA
      Reads data from `in', decodes the data, and outputs to `out'.
      Returns 0 on success, -1 on error.
      Optionally sets pcrc, crc, encsize (encoded size), and decsize (decoded size).
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
static int
ydecode_data(YDECFILE *y, FILE *in, FILE *out, crc32_t *crc32p, crc32_t *pcrc32p,
                         size_t *elen, size_t *dlen)
{
      unsigned char inbuf[BUFSIZ], outbuf[BUFSIZ];                /* Input buffer */
      register unsigned char *b;                                                    /* Current location in `buf' */
      register int ob;                                                                    /* Current offset in `outbuf' */
      register int linect;
      register unsigned long lineno;
      register int last_line_length, char_escaped;

      ob = linect = last_line_length = char_escaped = 0;
      lineno = 0;

      while (fgets(inbuf, sizeof(inbuf), in))
      {
            if (YKEYWORD_END(inbuf))
                  break;

            /* If strict checking is enabled, warn if we found a line of invalid length */
            if (opt_strict && last_line_length && y->header && (last_line_length != *y->header->line))
                  if (!char_escaped || (char_escaped && (last_line_length - 1 != *y->header->line)))
                        Notice(_("%s:%lu: invalid line length %d (should be %lu)"),
                                      y->input_filename, lineno + y->line_offset, last_line_length, *y->header->line);

            lineno++;

            for (b = inbuf; *b; b++)
            {
                  /* If strict checking is enabled, warn about invalid first and/or last characters in line */
                  if (opt_strict)
                  {
                        if (linect == 0)
                        {
                              if (*b == ' ')
                                    Verbose("%s:%lu:%d: %s", y->input_filename, lineno + y->line_offset, linect+1,
                                                  _("line begins with an unescaped space"));
                              else if (*b == '\t')
                                    Verbose("%s:%lu:%d: %s", y->input_filename, lineno + y->line_offset, linect+1,
                                                  _("line begins with an unescaped TAB"));
                              else if (*b == '.' && *(b+1) != '.')
                                    Verbose("%s:%lu:%d: %s", y->input_filename, lineno + y->line_offset, linect+1,
                                                  _("line begins with an unescaped single dot"));
                        }
                        if (*(b+1) == '\r' || *(b+1) == '\n')
                        {
                              if (*b == ' ')
                                    Verbose("%s:%lu:%d: %s", y->input_filename, lineno + y->line_offset, linect+1,
                                                  _("line ends with an unescaped space"));
                              else if (*b == '\t')
                                    Verbose("%s:%lu:%d: %s", y->input_filename, lineno + y->line_offset, linect+1,
                                                  _("line ends with an unescaped TAB"));
                        }
                  }

                  /* If this is the beginning of a line, and it's a plain dot, and the next character is also a dot,
                        move one character forward. */
                  if (linect == 0 && *b == '.' && *(b+1) == '.')
                        b++;

                  /* For all relevant characters, set char_escaped if it was escaped (for strict checking line length) */
                  if (*b != '\n' && *b != '\r')
                        char_escaped = (*b == '=');

                  switch (*b)
                  {
                        case '\r':
                              continue;

                        case '\n':
                              last_line_length = linect;
                              linect = 0;
                              continue;

                        case '=':
                              if (elen) *elen += 1;
                              b++; linect++;
                              *b = YUNESCAPE(*b);
                              if (opt_strict && !YESCAPE_MAKES_SENSE(*b))
                                    Verbose("%s:%lu:%d: %s: %02X", y->input_filename, lineno + y->line_offset, linect + 1,
                                                  _("byte escaped for no good reason"), *b);
                              /* FALLTHROUGH */

                        default:
                              *b = YDECODE(*b);
                              if (elen) *elen += 1;
                              if (dlen) *dlen += 1;
                              if (crc32p)  CRC_UPDATE(*crc32p, *b);
                              if (pcrc32p) CRC_UPDATE(*pcrc32p, *b);
                              OUT(*b);
                              linect++;
                              break;
                  }
            }
      }
      if (ob && out && (fwrite(outbuf, sizeof(unsigned char), ob, out) != ob))
            ErrERR("%s", y->output_filename);
      if (ferror(in))
            ErrERR("%s", y->input_filename);
      if (out && ferror(out))
            ErrERR("%s", y->output_filename);

      return (0);
}
/*--- ydecode_data() ----------------------------------------------------------------------------*/


/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
      YDECODE_SINGLE
      Decodes the specified single part file.
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
static int
ydecode_single(YDECFILE *y)
{
      FILE        *in, *out;                                                              /* Input and output file pointers */
      crc32_t     crc32;                                                                        /* CRC value */
      int         errors;                                                                       /* Number of errors found */
      char        errmsg[PATH_MAX];                                                 /* Error message to insert when renaming */
      size_t      encodedsize, decodedsize;                                   /* Length of encoded and decoded data */

      encodedsize = decodedsize = (size_t)0;
      errors = 0;
      errmsg[0] = '\0';

      if (!(in = fopen(y->input_filename, "r")))
            ErrERR("%s", y->input_filename);
      if (fseek(in, y->data_start, SEEK_SET))                     /* Move input fp to data start */
            ErrERR("%s", y->input_filename);

      if (opt_test)
            out = NULL;
      else if (!(out = open_output_file(y->output_filename, opt_overwrite, y->input_filename)))
            return (-1);

      /* Decode the data */
      CRC_START(crc32);
      ydecode_data(y, in, out, &crc32, NULL, &encodedsize, &decodedsize);
      CRC_FINISH(crc32);

      /* Clean up */
      fclose(in);
      if (out)
            fclose(out);

      /* Check for errors */
      if (y->header->size && (*y->header->size != decodedsize))
      {
            usermsg(YNAMES(y), 1, 1, _("file size mismatch"),
                          "(%s=%s %s=%s)",
                          _("ybegin"), comma1(*y->header->size),
                          _("actual"), comma2(decodedsize));
            errors++;
            snprintf(errmsg, sizeof(errmsg), "size-%u", decodedsize);
      }
      if (y->footer->crc32 && (*y->footer->crc32 != crc32))
      {
            usermsg(YNAMES(y), 1, 1, _("CRC mismatch"),
                          "(%s=%08x %s=%08x)",
                          _("yend"), *y->footer->crc32,
                          _("actual"), crc32);
            errors++;
            snprintf(errmsg, sizeof(errmsg), "crc-%08x", crc32);
      }
      else if (opt_debug && y->footer->crc32 && (*y->footer->crc32 == crc32))
            usermsg(YNAMES(y), 1, 1, _("CRC OK"),
                          "(%s=%08x %s=%08x)",
                          _("yend"), *y->footer->crc32,
                          _("actual"), crc32);
      else if (opt_debug && !y->footer->crc32)
            usermsg(YNAMES(y), 1, 1, _("no CRC for file"), NULL);

      if (errors && !opt_keep && !opt_test)
      {
            if (!unlink(y->output_filename))
                  usermsg(y->output_filename, NULL, 0, 0, _("errors found, output file removed"), NULL);
            else
                  usermsg(y->output_filename, NULL, 0, 0, _("unable to remove output file"), strerror(errno));
      }
      else if (!errors && opt_verbose)
      {
            if (opt_delete && !opt_test)
            {
                  if (unlink(y->input_filename))
                        usermsg(y->output_filename, NULL, 1, 1, _("file OK"), "(%.2f%%) (%s: %s)",
                                      PCT(decodedsize,encodedsize), _("input file not deleted"), strerror(errno));
                  else
                        usermsg(y->output_filename, NULL, 1, 1, _("file OK"), "(%.2f%%) (%s)",
                                      PCT(decodedsize,encodedsize), _("input file deleted"));
            }
            else
                  usermsg(y->output_filename, NULL, 1, 1, _("file OK"), "(%.2f%%)",
                                PCT(decodedsize,encodedsize));
      }
      else if (errors && *errmsg && !opt_test)
      {
            char *newname = rename_output_file(y->output_filename, errmsg);

            if (newname)
                  usermsg(y->output_filename, NULL, 0, 0, NULL, _("file renamed to `%s'"), newname);
            else
                  usermsg(y->output_filename, NULL, 0, 0, _("unable to rename output file"), strerror(errno));
      }

      return (0);
}
/*--- ydecode_single() --------------------------------------------------------------------------*/


/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
      VERIFY_MULTI_FILE
      Verifies a single file of a multipart archive.
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
static void
verify_multi_file(int part, int total, int first, int last, unsigned long *begin, int *errors)
{
      YDECFILE    *y = NULL;                                                                    /* File currently being processed */
      int   ct;                                                                                 /* Current position in yfiles */

      /* Find the yfile containing this part number */
      for (ct = first; ct < last && !y; ct++)
            if (yfiles[ct]->header->part && (*yfiles[ct]->header->part == part))
                  y = yfiles[ct];

      /* Part number not found - try to find it based on begin offset */
      if (!y)
      {
            for (ct = first; ct < last && !y; ct++)
                  if (yfiles[ct]->part && yfiles[ct]->part->begin && (*yfiles[ct]->part->begin == *begin))
                  {
                        usermsg(YNAMES(yfiles[ct]), 0, 0, NULL,
                                      _("file designated as part %d really begins where part %d should"),
                                      *yfiles[ct]->header->part, part);
                        y = yfiles[ct];
                        *errors += 1;
                  }
      }

      /* Part number STILL not found - part is missing */
      if (!y)
      {
            usermsg(yfiles[first]->output_filename, NULL, part, total, _("part missing"), NULL);
            *errors += 1;
            return;
      }

      /* Make sure a `=ypart' was present */
      if (!y->part)
      {
            usermsg(YNAMES(y), part, total, _("missing `=ypart' header"), NULL);
            *errors += 1;
            return;
      }

      /* Make sure this part begins where we think it should */
      if (y->part->begin && (*y->part->begin != *begin))
      {
            usermsg(YNAMES(y), part, total, _("incorrect beginning offset"),
                          "(%s: %s %s: %s)",
                          _("want"), comma1(*begin),
                          _("got"), comma2(*y->part->begin));
            *errors += 1;
      }

      /* Make sure size of part was specified */
      if (!y->footer->size)
      {
            usermsg(YNAMES(y), part, total, _("size of part not specified in `=yend'"), NULL);
            *errors += 1;
            return;
      }

      /* Advance begin pointer for next file */
      *begin += *y->footer->size;

      return;
}
/*--- verify_multi_file() -----------------------------------------------------------------------*/


/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
      VERIFY_MULTI
      Examines all parts of the current archive.  Attempts to determine the total number of parts,
      and if all parts of this archive are present.
      Returns 0 if everything looks OK, or the number of errors found.
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
static int
verify_multi(int first, int last, int *total)
{
      int   ct;                                                                                 /* Current position in yfiles */
      int   errors = 0;                                                                   /* Number of errors found */
      int   part = 0, highpart = 0;                                           /* Counters for calculating total parts */
      unsigned long     next_begin;                                                                   /* Next expected `begin' value */

      /* Look for a total part number specified in any part of the archive.  Save the highest part
            number found and use it if no other total is known */
      for (*total = 0, ct = first; ct < last; ct++)
      {
            if (yfiles[ct]->header->total)
                  *total = *yfiles[ct]->header->total;
            if (yfiles[ct]->header->part && (*yfiles[ct]->header->part > highpart))
                  highpart = *yfiles[ct]->header->part;
      }
      if (*total)
            Debug(_("%s: %d parts total"), yfiles[first]->output_filename, *total);
      else
      {
            *total = highpart;
            Debug(_("%s: %d parts total (estimated)"), yfiles[first]->output_filename, *total);
      }

      /* Now examine the file list one at a time, making sure each part is present and with the
            correct `begin' offset */
      for (next_begin = 1, part = 1; part <= *total; part++)
      {
            Debug(_("%s: verifying part %d of %d"), yfiles[first]->output_filename, part, *total);
            verify_multi_file(part, *total, first, last, &next_begin, &errors);
      }

      return (errors);
}
/*--- verify_multi() ----------------------------------------------------------------------------*/


/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
      YDECODE_MULTI
      Decodes the specified multipart file beginning at the record specified by `pos' in yfiles.
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
static int
ydecode_multi(int *first_pos)
{
      int         first, last;                                                            /* Offset of first and last file in group */
      YDECFILE    *y = yfiles[*first_pos];                                    /* File currently being processed */
      FILE        *in, *out = NULL;                                                 /* Input and output file pointers */
      int         errors = 0;                                                             /* Number of errors found */
      int         ct;                                                                           /* Current position in yfiles */
      crc32_t     pcrc32, crc32;                                                          /* CRC of current part and total file */
      size_t      encodedsize = 0, decodedsize = 0;               /* Length of encoded and decoded data */
      size_t      encpart, decpart;                                                 /* Length of data, this file */
      int         verify_errors;                                                          /* Number of errors found in verification */
      int         total;                                                                        /* Total number of parts */
      char        errmsg[PATH_MAX];                                                 /* Error message to insert when renaming */

      errmsg[0] = '\0';

      /* Get offset in `yfiles' for last file in this file group */
      first = *first_pos;
      for (last = first;
              last < num_yfiles && !strcmp(yfiles[last]->header->name, yfiles[first]->header->name);
           last++)
            /* DONOTHING */;
      *first_pos = last - 1;

      /* Verify consistency of header data for all parts before decoding to save time */
      if ((verify_errors = verify_multi(first, last, &total)) && !opt_keep)
      {
            usermsg(yfiles[first]->output_filename, NULL, 0, 0, _("errors found, nothing done"), NULL);
            return (-1);
      }

      if (opt_test)
            out = NULL;
      else if (!(out = open_output_file(yfiles[first]->output_filename, opt_overwrite,
                                                                         yfiles[first]->input_filename)))
            return (-1);

      /* Process each file in part */
      CRC_START(crc32);
      for (ct = first; ct < last; ct++)
      {
            y = yfiles[ct];

            if (!(in = fopen(y->input_filename, "r")))
                  ErrERR("%s", y->input_filename);
            if (fseek(in, y->data_start, SEEK_SET))               /* Move input fp to data start */
                  ErrERR("%s", y->input_filename);
            encpart = decpart = 0;

            /*
            **  Decode the file
            */
            CRC_START(pcrc32);
            ydecode_data(y, in, out, &crc32, &pcrc32, &encpart, &decpart);
            CRC_FINISH(pcrc32);
            encodedsize += encpart;
            decodedsize += decpart;
            fclose(in);

            if (y->footer->size && (*y->footer->size != decpart))
            {
                  usermsg(YNAMES(y), *y->header->part, total, _("file size mismatch"),
                                "(%s=%s %s=%s)",
                                _("yend"), comma1(*y->footer->size),
                                _("actual"), comma2(decpart));
                  snprintf(errmsg, sizeof(errmsg), "psize-%u", decpart);
                  errors++;
            }
            if (y->footer->pcrc32 && (*y->footer->pcrc32 != pcrc32))
            {
                  usermsg(YNAMES(y), *y->header->part, total, _("CRC mismatch"),
                                "(%s=%08x %s=%08x)",
                                _("yend"), *y->footer->pcrc32,
                                _("actual"), pcrc32);
                  snprintf(errmsg, sizeof(errmsg), "pcrc-%08x", pcrc32);
                  errors++;
            }
            else if (opt_debug && y->footer->pcrc32 && (*y->footer->pcrc32 == pcrc32))
                  usermsg(YNAMES(y), *y->header->part, total, _("CRC OK"),
                                "(%s=%08x %s=%08x)",
                                _("yend"), *y->footer->pcrc32,
                                _("actual"), pcrc32);
            else if (opt_debug && !y->footer->pcrc32)
                  usermsg(YNAMES(y), *y->header->part, total, _("no CRC for part"), NULL);
            else if (opt_debug)
                  usermsg(YNAMES(y), *y->header->part, total, _("file OK"), "(%.2f%%)", PCT(decpart, encpart));
      }
      CRC_FINISH(crc32);

      if (out)
            fclose(out);

      if (y->header->size && (*y->header->size != decodedsize))
      {
            usermsg(y->output_filename, NULL, 0, 0, _("file size mismatch"),
                          "(%s=%s %s=%s)",
                          _("ybegin"), comma1(*y->header->size),
                          _("actual"), comma2(decodedsize));
            snprintf(errmsg, sizeof(errmsg), "size-%u", decodedsize);
            errors++;
      }
      if (y->footer->crc32 && (*y->footer->crc32 != crc32))
      {
            usermsg(y->output_filename, NULL, 0, 0, _("CRC mismatch"),
                          "(%s=%08x %s=%08x)",
                          _("yend"), *y->footer->crc32,
                          _("actual"), crc32);
            snprintf(errmsg, sizeof(errmsg), "crc-%08x", crc32);
            errors++;
      }
      else if (opt_debug && y->footer->crc32 && (*y->footer->crc32 == crc32))
            usermsg(y->output_filename, NULL, 0, 0, _("CRC OK"),
                          "(%s=%08x %s=%08x)",
                          _("yend"), *y->footer->crc32,
                          _("actual"), crc32);
      else if (opt_debug && !y->footer->crc32)
            usermsg(y->output_filename, NULL, 0, 0, _("no CRC for file"), NULL);

      /* Delete output file if errors occurred, output summary */
      if (errors && !opt_keep && !opt_test)
      {
            if (!unlink(y->output_filename))
                  usermsg(y->output_filename, NULL, 0, 0, _("errors found, output file removed"), NULL);
            else
                  usermsg(y->output_filename, NULL, 0, 0, _("unable to remove output file"), strerror(errno));
      }
      else if (errors && *errmsg && !opt_test)
      {
            char *newname = rename_output_file(y->output_filename, errmsg);

            if (newname)
                  usermsg(y->output_filename, NULL, 0, 0, NULL, _("file renamed to `%s'"), newname);
            else
                  usermsg(y->output_filename, NULL, 0, 0, _("unable to rename output file"), strerror(errno));
      }
      if (errors)
            return (-1);

      /* Anything below here is working with a successfully decoded file */
      if (opt_verbose)
      {
            if (verify_errors)
                  usermsg(y->output_filename, NULL, 0, 0, _("errors detected in multipart archive"), NULL);
            else
            {
                  char  msgbuf[80];

                  if (total == 1)
                        snprintf(msgbuf, sizeof(msgbuf), _("%d part OK, file OK"), total);
                  else
                        snprintf(msgbuf, sizeof(msgbuf), _("%d parts OK, file OK"), total);

                  /* Remember to delete all files in the set */
                  if (opt_delete && !opt_test)
                  {
                        for (ct = first; ct < last; ct++)
                        {
                              y = yfiles[ct];
                              if (unlink(y->input_filename))
                                    WarnERR("%s: error deleting file", y->input_filename);
                        }
                        usermsg(y->output_filename, NULL, 0, 0, msgbuf, "(%.2f%%) (%s)",
                                      PCT(decodedsize,encodedsize),
                                      (total == 1) ? _("input file deleted") : _("input files deleted"));
                  }
                  else
                        usermsg(y->output_filename, NULL, 0, 0, msgbuf, "(%.2f%%)", PCT(decodedsize,encodedsize));
            }
      }

      return (0);
}
/*--- ydecode_multi() ---------------------------------------------------------------------------*/


/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
      OUTPUT_FILE_LIST
      Outputs a list of files found with information about each.
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
static void
output_file_list(void)
{
      FILE  *out;
      register int ct;

      if (opt_list)
            out = stdout;
      else
            out = stderr;

      for (ct = 0; ct < num_yfiles; ct++)
      {
            YDECFILE *y = yfiles[ct];

            fprintf(out, "%10s: \"%s\"\n", _("input"), y->input_filename);
            fprintf(out, "%10s: \"%s\"\n", _("output"), y->output_filename);
            fprintf(out, "%10s: \"%s\"\n", _("filename"), y->header->name);
            fprintf(out, "%10s: %s\n", _("size"), y->header->size ? comma(*y->header->size) : _("UNSPECIFIED"));

            if (y->part)
            {
                  fprintf(out, "%10s: %s\n", _("begin"), y->part->begin ? comma(*y->part->begin) : _("UNSPECIFIED"));
                  fprintf(out, "%10s: %s\n", _("end"), y->part->end ? comma(*y->part->end) : _("UNSPECIFIED"));
            }
            fprintf(out, "%10s: %s\n", _("psize"), y->footer->size ? comma(*y->footer->size) : _("UNSPECIFIED"));
            if (y->footer->pcrc32)
                  fprintf(out, "%10s: %08x\n", _("pcrc32"), *y->footer->pcrc32);
            else
                  fprintf(out, "%10s: %s\n", _("pcrc32"), _("UNSPECIFIED"));
            if (y->footer->crc32)
                  fprintf(out, "%10s: %08x\n", _("crc32"), *y->footer->crc32);
            else
                  fprintf(out, "%10s: %s\n", _("crc32"), _("UNSPECIFIED"));

            if (y->multipart)
            {
                  if (y->header->part && y->header->total)
                        fprintf(out, _("%10s  multipart archive, part %d of %d\n"), " ", *y->header->part, *y->header->total);
                  else if (y->header->part)
                        fprintf(out, _("%10s  multipart archive, part %d\n"), " ", *y->header->part);
            }
            else
                  fprintf(out, _("%10s  single part archive\n"), " ");

            fprintf(out, "\n");
      }
      if (opt_list)
            exit(EXIT_SUCCESS);
}
/*--- output_file_list() ------------------------------------------------------------------------*/


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

      set_progname(argv[0]);
      setlocale(LC_ALL, "");
      bindtextdomain(PACKAGE, LOCALEDIR);
      textdomain(PACKAGE);
      cmdline(argc, argv);

      /* Scan for files if requested */
      if (opt_scan || opt_recursive)
            scan_for_files();

      /* If there are no input files at this point, there's nothing else we can do */
      if (!num_yfiles)
      {
            Warn(_("no input files found"));
            usage(EXIT_FAILURE);
      }

      /* Process the file list */
      for (ct = 0; ct < num_yfiles; ct++)
      {
            flag_multipart_file(yfiles[ct]);
            set_output_filename(yfiles[ct]);
      }
      qsort(yfiles, num_yfiles, sizeof(YDECFILE *), ydecfile_cmp);

      /* Output file list for --list option (or debug) */
      if (opt_list || err_debug)
            output_file_list();

      /* Decode all files in the list */
      for (ct = 0; ct < num_yfiles; ct++)
      {
            if (yfiles[ct]->multipart)
                  ydecode_multi(&ct);
            else
                  ydecode_single(yfiles[ct]);
      }

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

/* vi:set ts=3: */

Generated by  Doxygen 1.6.0   Back to index