⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 unrar.c

📁 RAR的解压缩算法
💻 C
字号:
/* Copyright (C) 2004  Jeroen Dekkers <jeroen@dekkers.cx>
   Copyright (C) 2004  Ben Asselstine <benasselstine@canada.com>

   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.,
   51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <error.h>
#include <errno.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include "opts.h"
#include "unrar.h"
#include "unrarlib.h"

/*
 *
 * dos_to_unix_time came from unzip sources.
 * dump_file is based on a simple GPL'd unrar client by Andreas F Borchert.
 * 
 *
 */
extern struct arguments_t arguments;

void
show_copyright ()
{
  printf ("\n");
  printf ("%s %s  Copyright (C) 2004  Ben Asselstine, Jeroen Dekkers\n",
          PACKAGE, VERSION);
  printf ("\n");
  return;
}

void
show_list_header (struct unrar_arguments_t *unrar)
{
  printf ("\n");
  printf ("RAR archive %s\n", unrar->archive_filename);
  printf ("\n");
  printf ("Pathname/Comment\n");
  printf
    ("                  Size   Packed Ratio  Date   Time     Attr      CRC   Meth Ver\n");
  printf
    ("-------------------------------------------------------------------------------\n");
  return;
}

void
show_list_footer (struct unrar_arguments_t *unrar)
{
  printf
    ("-------------------------------------------------------------------------------\n");
  return;
}

void
show_list_stats (unsigned int num_files, unsigned int num_compressed_bytes,
                 unsigned int num_bytes)
{
  float ratio;
  ratio = ((float) num_compressed_bytes / (float) num_bytes) * 100;
  printf ("%5u       %10u %8u %3.0f%%\n", num_files, num_bytes,
          num_compressed_bytes, ratio);
  return;
}

#define DOSTIME_2038_01_18 ((unsigned long)0x74320000L)
#define U_TIME_T_MAX  ((time_t)(unsigned long)0xffffffffL)
#define S_TIME_T_MAX  ((time_t)(unsigned long)0x7fffffffL)
#define YRBASE  1900

time_t
dos_to_unix_time (unsigned long dosdatetime)
{
  time_t m_time;
  struct tm *tm;
  time_t now = time (NULL);

  tm = localtime (&now);
  tm->tm_isdst = -1;            /* let mktime determine if DST is in effect */

  /* dissect date */
  tm->tm_year = ((int) (dosdatetime >> 25) & 0x7f) + (1980 - YRBASE);
  tm->tm_mon = ((int) (dosdatetime >> 21) & 0x0f) - 1;
  tm->tm_mday = ((int) (dosdatetime >> 16) & 0x1f);

  /* dissect time */
  tm->tm_hour = (int) ((unsigned) dosdatetime >> 11) & 0x1f;
  tm->tm_min = (int) ((unsigned) dosdatetime >> 5) & 0x3f;
  tm->tm_sec = (int) ((unsigned) dosdatetime << 1) & 0x3e;

  m_time = mktime (tm);

  if ((dosdatetime >= DOSTIME_2038_01_18) && (m_time < (time_t) 0x70000000L))
    m_time = U_TIME_T_MAX;      /* saturate in case of (unsigned) overflow */
  if (m_time < (time_t) 0L)     /* a converted DOS time cannot be negative */
    m_time = S_TIME_T_MAX;      /*  -> saturate at max signed time_t value */
  return m_time;

}                               /* end function dos_to_unix_time() */

void
replace_backslash_with_slash (char *s1)
{
  char *ptr;
  if (!s1)
    return;
  while ((ptr = strchr (s1, '\\')))
  {
    ptr[0] = '/';
    s1 = &ptr[1];
  }
  return;
}

void
unrar_list_item (struct unrar_arguments_t *unrar,
                 ArchiveList_struct * listptr)
{
  char datetime[20];
  char *attr;
  float ratio;

  printf (" %s\n", listptr->item.Name);
  if (listptr->item.UnpSize == 0)
    ratio = 0;
  else
    ratio =
      ((float) listptr->item.PackSize / (float) listptr->item.UnpSize) * 100;
  if (ratio > 100.0)
    ratio = 100.0;
  if (listptr->item.FileTime)
  {
    struct tm filetime;
    time_t unixtime;
    unixtime = dos_to_unix_time ((unsigned long) listptr->item.FileTime);
    localtime_r ((const time_t *) &unixtime, &filetime);
    strftime (datetime, sizeof (datetime), "%d-%m-%y %H:%M", &filetime);
  }
  else
    snprintf (datetime, sizeof (datetime), "00-00-00 00:00");

  if (listptr->item.FileAttr & 0x10)
    attr = ".D....";
  else
    attr = ".....A";

  printf ("            %10u %8u %3.0f%% %s   %s   %08X m%c? %3.1f\n",
          (unsigned int) listptr->item.UnpSize,
          (unsigned int) listptr->item.PackSize,
          ratio,
          datetime,
          attr,
          (unsigned int) listptr->item.FileCRC,
          listptr->item.Method, (float) (listptr->item.UnpVer / 10.0));
  return;
}

int
unrar_list (struct unrar_arguments_t *unrar, int num_files, char **files)
{
  ArchiveList_struct list;
  ArchiveList_struct *listptr;
  int i, j, n;
  unsigned int count_files = 0;
  unsigned int count_bytes = 0;
  unsigned int count_compressed_bytes = 0;
  list.next = NULL;

  n = urarlib_list (unrar->archive_filename, &list.next);
  if (n < 0)
    return n;
  if (unrar->verbose)
    printf ("showing %d files...\n", n);
  if (unrar->verbose)
    printf ("given a list of %d files.\n", num_files);
  listptr = list.next;
  show_list_header (unrar);
  for (i = 0; i < n; i++, listptr = listptr->next)
  {
    replace_backslash_with_slash (listptr->item.Name);
    if (num_files)
    {
      int found = 0;
      for (j = 0; j < num_files; j++)
      {
        if (unrar->verbose)
          printf ("comparing '%s' vs '%s'\n", files[j], listptr->item.Name);
        if (strcmp (files[j], listptr->item.Name) == 0)
        {
          found = 1;
          break;
        }

      }
      if (!found)
        continue;
    }
    count_files++;
    count_compressed_bytes += listptr->item.PackSize;
    count_bytes += listptr->item.UnpSize;
    unrar_list_item (unrar, listptr);
    if (listptr == NULL)        //sanity check.
      break;
  }

  urarlib_freelist (list.next);
  show_list_footer (unrar);
  show_list_stats (count_files, count_compressed_bytes, count_bytes);

  return 0;
}

int
dump_file (struct unrar_arguments_t *unrar, char *filename, void *data,
           unsigned long data_size)
{
  int fd;
  int written;
  ssize_t nbytes = 0;
  int fcntl_flags = O_WRONLY | O_CREAT;
  if (unrar->force)
    fcntl_flags |= O_TRUNC;
  else
    fcntl_flags |= O_EXCL;
  fd = open (filename, fcntl_flags, 0666);
  if (fd < 0)
    return -1;

  while (nbytes < data_size)
  {
    written = write (fd, data + nbytes, data_size - nbytes);
    if (written < 0)
      return -1;
    nbytes += written;
  }
  if (close (fd) < 0)
    return -1;
  return 0;
}

void
show_status_line (char *action, char *file, char *status)
{
  printf ("%-11s %-57s %-10s\n", action, file, status);
  return;
}

int
unrar_mkpath (char *path)
{
  char *path_tmp;
  int i, ret = 0;
  if (path == NULL || path[0] == '\0')
    return (-1);
  path_tmp = strdup (path);
  if (path_tmp == NULL)
    return (-1);
  for (i = 0; path_tmp[i] != '\0'; i++)
  {
    if (path_tmp[i] == '/')
    {
      path_tmp[i] = '\0';
      if (path_tmp[0] != '\0')
      {
        ret = mkdir (path_tmp, 0777);
      }
      path_tmp[i] = '/';
    }
  }
  free (path_tmp);
  return ret;
}

int
unrar_extract_directory (struct unrar_arguments_t *unrar, char *dir)
{
  char *destination = NULL;
  int ret = 0;
  if (unrar->junk_paths)
    return 0;
  if (asprintf (&destination, "%s/%s/", unrar->destination_dir, dir) == -1)
  {
    error (0, 0, "asprintf failed: %m\n");
    return -1;
  }
  if (mkdir (destination, 0777) < 0)
  {
    switch (errno)
    {
    case EEXIST:
      if (unrar->force)
        chmod (destination, 0777);
      break;
    case ENOENT:
      ret = unrar_mkpath (destination);
      if (ret == (-1))
      {
        error (0, 0, "mkpath failed '%s': %m\n", destination);
      }
      break;
    default:
      error (0, 0, "mkdir failed '%s': %m\n", destination);
      ret = (-1);
      break;
    }
  }
  free (destination);
  return ret;
}

int
unrar_extract_file (struct unrar_arguments_t *unrar, char *filename,
                    char *archive_member, time_t FileTime)
{
  int retval;
  char *destination = NULL;
  void *data;
  unsigned long data_size;
  if (!urarlib_get
      (&data, &data_size, archive_member, unrar->archive_filename,
       unrar->password))
    return -1;
  else
  {
    char *file;
    if (unrar->junk_paths)
    {
      file = strrchr (filename, '/');
      if (!file)
        file = filename;
    }
    else
      file = filename;

    if (asprintf (&destination, "%s/%s", unrar->destination_dir, file) == -1)
    {
      error (0, 0, "asprintf failed: %m\n");
      return 0;
    }
    //where do i put it?  in destination.
    if (unrar->extract_newer)
    {
      struct stat statbuf;
      time_t unixtime;
      int non_existent_files_are_not_newer = 1;
      retval = lstat (destination, &statbuf);
      if (retval < 0)
      {
        if (non_existent_files_are_not_newer)
        {
          free (destination);
          return 1;
        }
      }
      else
      {
        unixtime = dos_to_unix_time ((unsigned long) FileTime);
        if (unixtime <= statbuf.st_mtime);
        {
          free (destination);
          return 1;
        }
      }
    }
    //okay put it in destination.
    if (dump_file (unrar, destination, data, data_size) < 0)
    {
      free (data);
      free (destination);
      return -1;
    }
    free (data);
    free (destination);
  }
  return 0;
}

int
unrar_extract (struct unrar_arguments_t *unrar, int num_files, char **files)
{
  ArchiveList_struct list;
  ArchiveList_struct *listptr;
  int i, j, n;
  int retval;
  char *action;
  char *status;
  char *orig_name;
  int num_failed = 0;

  printf ("\n");
  printf ("Extracting from %s\n", unrar->archive_filename);
  printf ("\n");

  list.next = NULL;
  n = urarlib_list (unrar->archive_filename, &list.next);
  if (n < 0)
    return n;

  if (unrar->verbose)
  {
    printf ("showing %d files...\n", n);
    printf ("given a list of %d files.\n", num_files);
  }

  listptr = list.next;
  for (i = 0; i < n; i++, listptr = listptr->next)
  {
    orig_name = strdup (listptr->item.Name);

    replace_backslash_with_slash (listptr->item.Name);
    if (num_files)
    {
      int found = 0;
      for (j = 0; j < num_files; j++)
      {
        if (unrar->verbose)
          printf ("comparing '%s' vs '%s'\n", files[j], listptr->item.Name);
        if (strcmp (files[j], listptr->item.Name) == 0)
        {
          found = 1;
          break;
        }

      }
      if (!found)
      {
        free (orig_name);
        if (listptr->item.FileAttr & 0x10)
          continue;
        action = "Skipping";
        status = "";
        show_status_line (action, listptr->item.Name, status);
        continue;
      }
    }

    status = "OK";
    if (listptr->item.FileAttr & 0x10)  //is directory.
    {
      action = "Creating";
      if ((retval = unrar_extract_directory (unrar, listptr->item.Name)) < 0)
      {
        status = "Failed";
      }
      else if (retval == 0)
      {
        free (orig_name);
        continue;
      }

    }
    else                        //is file.
    {
      action = "Extracting";
      if ((retval =
           unrar_extract_file (unrar, listptr->item.Name, orig_name,
                               listptr->item.FileTime)) < 0)
      {
        status = "Failed";
      }
      else if (retval == 1)
      {
        action = "Skipping";
        status = "";
        show_status_line (action, listptr->item.Name, status);
        free (orig_name);
        continue;
      }
      else if (retval == 0)
      {
        status = "OK";
        show_status_line (action, listptr->item.Name, status);
        free (orig_name);
        continue;
      }
    }
    free (orig_name);
    show_status_line (action, listptr->item.Name, status);
    if (strcmp (status, "Failed") == 0)
      num_failed++;
    if (listptr == NULL)        //sanity check.
      break;
  }

  urarlib_freelist (list.next);
  if (num_failed)
  {
    printf ("%d Failed\n", num_failed);
    return -1;
  }
  else
    printf ("All OK\n");
  return 0;
}

int
main (int argc, char **argv)
{
  int retval = 0;
  char **files = NULL;
  int num_files = 0;

  if (compat_parse_opts (argc, argv, &arguments) == 0)
  {
    /* compatible mode success */
  }
  else if (parse_opts (argc, argv, &arguments) < 0)
    exit (1);

  if (arguments.unrar.verbose)
  {
    printf ("archive name is '%s'\n", arguments.unrar.archive_filename);
    printf ("destination directory is '%s'\n",
            arguments.unrar.destination_dir);
    printf ("mode = %d\n", arguments.unrar.mode);
    printf ("force = %d\n", arguments.unrar.force);
    printf ("extract_newer = %d\n", arguments.unrar.extract_newer);
    printf ("junk_paths = %d\n", arguments.unrar.junk_paths);
  }

  if (arguments.arraylen > 1)
  {
    files = &arguments.args[1];
    num_files = arguments.arraylen - 1;
  }

  show_copyright ();
  if (arguments.unrar.mode == MODE_LIST)
    retval = unrar_list (&arguments.unrar, num_files, files);
  else if (arguments.unrar.mode == MODE_EXTRACT)
    retval = unrar_extract (&arguments.unrar, num_files, files);

  if (retval < 0)
    exit (1);

  exit (0);
}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -