home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
DOS/V Power Report 2001 December (DVD)
/
VPR0112A.ISO
/
OLS
/
UNRAR32008
/
unrar32008.lzh
/
src.lzh
/
src
/
rar.cxx
< prev
next >
Wrap
C/C++ Source or Header
|
2001-08-12
|
20KB
|
866 lines
/*
* Copyright (c) 1998-2001 T. Kamei (kamei@jsdlab.co.jp)
*
* Permission to use, copy, modify, and distribute this software
* and its documentation for any purpose is hereby granted provided
* that the above copyright notice and this permission notice appear
* in all copies of the software and related documentation.
*
* NO WARRANTY
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY WARRANTIES;
* WITHOUT EVEN THE IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS
* FOR A PARTICULAR PURPOSE.
*/
#include <windows.h>
#include <mbstring.h>
#include <stdio.h>
#include "comm-arc.h"
#include "util.h"
#include "unrarapi.h"
#include "rar.h"
#include "dialog.h"
int
UnRAR::open_err (int e) const
{
switch (e)
{
case ERAR_NO_MEMORY:
format (IDS_NOT_ENOUGH_MEMORY);
return ERROR_ENOUGH_MEMORY;
case ERAR_BAD_DATA:
format (IDS_ARCHIVE_HEADER_BROKEN, u_path);
return ERROR_HEADER_BROKEN;
case ERAR_BAD_ARCHIVE:
format (IDS_NOT_VALID_RAR_ARCHIVE, u_path);
return ERROR_FILE_STYLE;
case ERAR_EOPEN:
format (IDS_FILE_OPEN_ERROR, u_path);
return ERROR_NOT_FIND_ARC_FILE;
default:
format (IDS_UNDOCUMENTED, e, u_path);
return ERROR_UNEXPECTED;
}
}
int
UnRAR::process_err (int e, const char *path) const
{
switch (e)
{
case ERAR_BAD_DATA:
format (IDS_CRC_ERROR, path);
return ERROR_FILE_CRC;
case ERAR_BAD_ARCHIVE:
format (IDS_NOT_VALID_RAR_VOLUME, path);
return ERROR_FILE_STYLE;
case ERAR_UNKNOWN_FORMAT:
format (IDS_UNKNOWN_ARCHIVE_FORMAT, path);
return ERROR_FILE_STYLE;
case ERAR_EOPEN:
format (IDS_VOLUME_OPEN, path);
return ERROR_ARC_FILE_OPEN;
case ERAR_ECREATE:
format (IDS_FILE_CREATE_ERROR, path);
return ERROR_FILE_OPEN;
case ERAR_ECLOSE:
format (IDS_FILE_CLOSE_ERROR, path);
return ERROR_CANNOT_WRITE;
case ERAR_EREAD:
format (IDS_READ_ERROR, path);
return ERROR_CANNOT_READ;
case ERAR_EWRITE:
format (IDS_WRITE_ERROR, path);
return ERROR_CANNOT_WRITE;
default:
format (IDS_UNDOCUMENTED, e, path);
return ERROR_UNEXPECTED;
}
}
int
UnRAR::header_err (int e) const
{
switch (e)
{
case ERAR_END_ARCHIVE:
return 0;
case ERAR_BAD_DATA:
format (IDS_FILE_HEADER_BROKEN, u_path);
return ERROR_HEADER_BROKEN;
default:
format (IDS_UNDOCUMENTED, e, u_path);
return ERROR_UNEXPECTED;
}
}
int
UnRAR::format (const char *fmt, ...) const
{
va_list ap;
va_start (ap, fmt);
int x = u_ostr.formatv (fmt, ap);
va_end (ap);
return x;
}
int
UnRAR::format (int id, ...) const
{
char fmt[1024];
if (!LoadString (lstate.hinst, id, fmt, sizeof fmt))
return format ("Unable to load message string: %d\n", id);
else
{
va_list ap;
va_start (ap, id);
int x = u_ostr.formatv (fmt, ap);
va_end (ap);
return x;
}
}
int
UnRAR::parse_opt (int ac, char **av)
{
u_cmd = C_NOTDEF;
u_opt = 0;
u_type = UT_ASK;
u_dest = "";
u_passwd = 0;
u_path = 0;
if (!ac)
{
format (IDS_NO_ARCHIVE_FILE);
return ERROR_NOT_ARC_FILE;
}
int c = av[0][0] == '-' ? av[0][1] : av[0][0];
switch (c)
{
case 'x':
u_cmd = C_EXTRACT;
break;
case 'e':
u_cmd = C_EXTRACT_NODIR;
break;
case 't':
u_cmd = C_TEST;
break;
case 'p':
u_cmd = C_PRINT;
break;
case 'l':
u_cmd = C_LIST;
break;
case 'v':
u_cmd = C_VLIST;
break;
case 'c':
u_cmd = C_COMMENT;
break;
default:
format (IDS_UNRECOGNIZED_OPTION, c);
return ERROR_COMMAND_NAME;
}
for (int i = 1; i < ac && av[i][0] == '-'; i++)
switch (av[i][1])
{
case 'r':
u_opt |= O_RECURSIVE;
break;
case 'y':
u_opt |= O_YES;
break;
case 'o':
u_type = av[i][2] == '-' ? UT_SKIP : UT_OVWRT;
break;
case 'f':
u_type = UT_EXISTING;
break;
case 'u':
u_type = UT_NEWER;
break;
case 's':
u_opt |= O_STRICT;
break;
case 'p':
if (av[i][2])
u_passwd = &av[i][2];
else if (++i < ac)
u_passwd = av[i];
else
{
format (IDS_OPTION_P_REQ_ARGS);
return ERROR_COMMAND_NAME;
}
break;
case '-':
i++;
goto optend;
default:
format (IDS_UNRECOGNIZED_OPTION, av[i][1]);
return ERROR_COMMAND_NAME;
}
optend:
if (i >= ac)
{
format (IDS_NO_ARCHIVE_FILE);
return ERROR_NOT_ARC_FILE;
}
u_path = av[i++];
if (i < ac)
{
char *sl = find_last_slash (av[i]);
if (sl && !sl[1])
{
u_dest = av[i++];
if (strlen (u_dest) >= FNAME_MAX32 - MAX_PATH)
{
format (IDS_FILE_NAME_TOO_LONG, u_dest);
return ERROR_LONG_FILE_NAME;
}
}
}
u_glob.set_pattern (ac - i, av + i);
return 0;
}
class dyn_handle
{
private:
void operator = (const dyn_handle &);
dyn_handle (const dyn_handle &);
protected:
HANDLE h;
public:
dyn_handle () : h (INVALID_HANDLE_VALUE) {}
dyn_handle (HANDLE h_) : h (h_) {}
~dyn_handle () {if (valid ()) CloseHandle (h);}
int valid () const {return h != INVALID_HANDLE_VALUE;}
operator HANDLE () const {return h;}
void fix (HANDLE h_) {h = h_;}
void close ()
{
if (valid ())
{
CloseHandle (h);
h = INVALID_HANDLE_VALUE;
}
}
};
class write_handle: public dyn_handle
{
int w_complete;
int w_delete_if_fail;
const char *w_path;
public:
write_handle (const char *);
~write_handle ();
void complete () {w_complete = 1;}
int open ();
int ensure_room (LONG);
};
write_handle::write_handle (const char *path)
: w_complete (0), w_delete_if_fail (0), w_path (path)
{
}
write_handle::~write_handle ()
{
if (!w_complete && valid () && w_delete_if_fail)
{
close ();
DeleteFile (w_path);
}
}
int
write_handle::ensure_room (LONG size)
{
if (SetFilePointer (*this, size, 0, FILE_BEGIN) == -1
|| !SetEndOfFile (*this))
return 0;
w_delete_if_fail = 1;
if (SetFilePointer (*this, 0, 0, FILE_BEGIN) == -1)
return 0;
return 1;
}
int
write_handle::open ()
{
fix (CreateFile (w_path, GENERIC_WRITE, 0, 0, OPEN_ALWAYS,
FILE_ATTRIBUTE_ARCHIVE | FILE_FLAG_SEQUENTIAL_SCAN, 0));
if (!valid ())
return 0;
w_delete_if_fail = GetLastError () == NO_ERROR;
return 1;
}
int
UnRAR::check_timestamp (const char *path, const RARHeaderData &hd)
{
WIN32_FIND_DATA fd;
HANDLE h = FindFirstFile (path, &fd);
if (h != INVALID_HANDLE_VALUE)
FindClose (h);
switch (u_type)
{
case UT_ASK:
if (h != INVALID_HANDLE_VALUE && !(u_opt & O_YES))
{
replace_param r;
r.old_name = path;
FILETIME ft;
FileTimeToLocalFileTime (&fd.ftLastWriteTime, &ft);
WORD d, t;
FileTimeToDosDateTime (&ft, &d, &t);
r.old_date = (DWORD (d) << 16) + t;
r.old_size = fd.nFileSizeLow;
r.new_name = hd.FileName;
r.new_date = hd.FileTime;
r.new_size = hd.UnpSize;
switch (replace_dialog (u_hwnd, r))
{
case IDYES:
return 1;
case IDNO:
return 0;
case IDC_ALL:
u_opt |= O_YES;
return 1;
default:
return -1;
}
}
break;
case UT_OVWRT:
return 1;
case UT_SKIP:
return h == INVALID_HANDLE_VALUE;
case UT_EXISTING:
if (h == INVALID_HANDLE_VALUE)
return 0;
/* fall thru... */
case UT_NEWER:
if (h != INVALID_HANDLE_VALUE)
{
FILETIME ft;
FileTimeToLocalFileTime (&fd.ftLastWriteTime, &ft);
WORD d, t;
FileTimeToDosDateTime (&ft, &d, &t);
if ((DWORD (d) << 16) + t >= hd.FileTime)
return 0;
}
break;
}
return 1;
}
struct extract_info
{
const progress_dlg *progress;
HWND hwnd_owner;
HANDLE h;
const RARHeaderData *hd;
const char *path;
int canceled;
int error;
long nbytes;
EXTRACTINGINFOEX *xex;
};
static extract_info *xtract_info;
static const UINT UWM_ARCEXTRACT = RegisterWindowMessage (WM_ARCEXTRACT);
static int
run_callback (int mode, EXTRACTINGINFOEX &ex)
{
return (lstate.has_callback
? (lstate.callback
? !lstate.callback (lstate.hwnd_owner, UWM_ARCEXTRACT,
mode, &ex)
: SendMessage (lstate.hwnd_owner, UWM_ARCEXTRACT,
mode, LPARAM (&ex)))
: 0);
}
static int __cdecl
extract_helper (void *, u_char *data, int nbytes)
{
if (!xtract_info)
return 1;
while (nbytes > 0)
{
int size = min (nbytes, 65536);
DWORD nwrite;
if (!WriteFile (xtract_info->h, data, size, &nwrite, 0)
|| nwrite != DWORD (size))
{
xtract_info->error = 1;
xtract_info = 0;
return 0;
}
nbytes -= size;
data += size;
xtract_info->nbytes += size;
if (xtract_info->progress
&& !xtract_info->progress->update (xtract_info->nbytes))
{
xtract_info->canceled = 1;
xtract_info = 0;
return 0;
}
if (lstate.has_callback)
{
xtract_info->xex->exinfo.dwWriteSize = xtract_info->nbytes;
if (run_callback (ARCEXTRACT_INPROCESS, *xtract_info->xex))
{
xtract_info->canceled = 1;
xtract_info = 0;
return 0;
}
}
}
return 1;
}
static int __cdecl
change_volume (void *, char *path, int mode)
{
if (mode != RAR_VOL_ASK)
return 1;
return change_vol_dialog (xtract_info ? xtract_info->hwnd_owner : 0, path);
}
#define DEFINE_STUB(STUB_NAME, REAL_NAME, ARGSIZE) \
static int __declspec (naked) \
STUB_NAME () \
{ \
__asm mov eax, dword ptr [esp] \
__asm mov ax, word ptr [eax] \
__asm cmp ax, 0xc483 /* add esp,NN -> 83 c4 NN */ \
__asm jz cdecl_ \
__asm call REAL_NAME \
__asm ret ARGSIZE \
__asm cdecl_: \
__asm call REAL_NAME \
__asm ret 0 \
}
DEFINE_STUB (extract_helper_stub, extract_helper, 8);
DEFINE_STUB (change_volume_stub, change_volume, 8);
static void
init_exinfo (EXTRACTINGINFOEX &ex, const RARHeaderData &hd,
const char *path)
{
ex.exinfo.dwFileSize = hd.UnpSize;
ex.exinfo.dwWriteSize = 0;
strcpy (ex.exinfo.szSourceFileName, hd.FileName);
strcpy (ex.exinfo.szDestFileName, path);
ex.dwCompressedSize = hd.PackSize;
ex.dwCRC = hd.FileCRC;
ex.uOSType = os_type (hd.HostOS);
ex.wRatio = calc_ratio (hd.PackSize, hd.UnpSize);
ex.wDate = HIWORD (hd.FileTime);
ex.wTime = LOWORD (hd.FileTime);
strcpy (ex.szAttribute, attr_string (hd.FileAttr));
strcpy (ex.szMode, method_string (hd.Method));
}
int
UnRAR::canceled () const
{
format (IDS_CANCELED);
return ERROR_USER_CANCEL;
}
int
UnRAR::skip (rarData &rd, const char *path) const
{
format (IDS_SKIPPING, path);
int e = rd.skip ();
return e ? process_err (e, path) : -1;
}
int
UnRAR::extract (rarData &rd, const char *path, const RARHeaderData &hd,
progress_dlg &progress)
{
if (progress.hwnd)
progress.init (path, hd.UnpSize);
int e = check_timestamp (path, hd);
if (e < 0)
return canceled ();
if (!e)
return skip (rd, path);
write_handle w (path);
if (!w.open ())
{
format (IDS_CANNOT_CREATE, path);
return skip (rd, path);
}
if (!w.ensure_room (hd.UnpSize))
{
format (IDS_DISK_FULL);
return skip (rd, path);
}
extract_info xinfo;
xinfo.progress = progress.hwnd ? &progress : 0;
xinfo.hwnd_owner = u_hwnd;
xinfo.h = w;
xinfo.hd = &hd;
xinfo.path = path;
xinfo.canceled = 0;
xinfo.error = 0;
xinfo.nbytes = 0;
xinfo.xex = &u_ex;
xtract_info = &xinfo;
format (IDS_EXTRACTING, path);
if (lstate.has_callback)
{
init_exinfo (*xinfo.xex, hd, path);
if (run_callback (ARCEXTRACT_BEGIN, *xinfo.xex))
return canceled ();
}
e = rd.test ();
xtract_info = 0;
if (xinfo.canceled)
return canceled ();
if (e)
return process_err (e, path);
if (xinfo.error)
{
format (IDS_WRITE_ERROR, path);
return -1;
}
if (!SetEndOfFile (w))
{
format (IDS_CANNOT_SET_EOF);
return -1;
}
w.complete ();
FILETIME lo, ft;
DosDateTimeToFileTime (hd.FileTime >> 16, hd.FileTime, &lo);
LocalFileTimeToFileTime (&lo, &ft);
SetFileTime (w, 0, 0, &ft);
SetFileAttributes (path, hd.FileAttr);
return 0;
}
int
UnRAR::mkdirhier (const char *path)
{
if (CreateDirectory (path, 0))
{
format (IDS_CREATING, path);
return 1;
}
DWORD a = GetFileAttributes (path);
if (a != -1 && a & FILE_ATTRIBUTE_DIRECTORY)
return 1;
char buf[FNAME_MAX32 + 1];
strcpy (buf, path);
for (char *p = buf;
p = (char *)_mbspbrk ((u_char *)p, (u_char *)"/\\");
*p++ = '\\')
{
*p = 0;
if (CreateDirectory (buf, 0))
format (IDS_CREATING, buf);
}
if (CreateDirectory (path, 0))
{
format (IDS_CREATING, path);
return 1;
}
a = GetFileAttributes (path);
if (a != -1 && a & FILE_ATTRIBUTE_DIRECTORY)
return 1;
format (IDS_CANNOT_CREATE, path);
return 0;
}
int
UnRAR::extract1 ()
{
format (IDS_EXTRACTING_FROM, u_path);
rarData rd;
if (!rd.open (u_path, RAR_OM_EXTRACT))
return open_err (rd.oad.OpenResult);
rarSetProcessDataProc (rd.h, (int (__cdecl *)(u_char *, int)) extract_helper_stub);
rarSetChangeVolProc (rd.h, (int (__cdecl *)(char *,int))change_volume_stub);
progress_dlg progress;
if (!lstate.has_callback)
progress.create (u_hwnd);
char dest[FNAME_MAX32 + 1];
char *de = stpcpy (dest, u_dest);
int nerrors = 0;
int e;
while (1)
{
e = rd.read_header ();
if (e)
{
e = header_err (e);
return e ? e : nerrors;
}
if (rd.hd.Flags & FRAR_ENCRYPTED)
{
if (!u_passwd)
{
u_passwd = askpass_dialog (u_hwnd);
if (!u_passwd)
return ERROR_USER_CANCEL;
}
rarSetPassword (rd.h, u_passwd);
}
if (!u_glob.match (rd.hd.FileName, u_opt & O_STRICT, u_opt & O_RECURSIVE))
{
e = rd.skip ();
if (e)
return process_err (e, rd.hd.FileName);
}
else
{
const char *name = trim_root (rd.hd.FileName);
if (!*name)
name = rd.hd.FileName;
if (u_cmd == C_EXTRACT)
strcpy (de, name);
else
{
char *sl = find_last_slash (name);
strcpy (de, sl ? sl + 1 : name);
}
if (rd.hd.FileAttr & FILE_ATTRIBUTE_DIRECTORY)
{
if (u_cmd == C_EXTRACT && !mkdirhier (dest))
return ERROR_DIRECTORY;
e = rd.skip ();
if (e)
return process_err (e, dest);
}
else
{
char *p = find_last_slash (dest);
if (p)
{
*p = 0;
if (!mkdirhier (dest))
return ERROR_DIRECTORY;
*p = '\\';
}
e = extract (rd, dest, rd.hd, progress);
if (e)
{
if (e > 0)
return e;
nerrors++;
}
}
}
}
}
int
UnRAR::extract ()
{
if (lstate.has_callback)
{
memset (&u_ex, 0, sizeof u_ex);
strcpy (u_ex.exinfo.szSourceFileName, u_path);
if (run_callback (ARCEXTRACT_OPEN, u_ex))
return canceled ();
}
int e = extract1 ();
if (lstate.has_callback)
run_callback (ARCEXTRACT_END, u_ex);
return e;
}
int
UnRAR::test ()
{
format (IDS_TEST_NOT_IMPL);
return ERROR_NOT_SUPPORT;
}
int
UnRAR::list ()
{
rarData rd;
if (!rd.open (u_path, RAR_OM_LIST))
return open_err (rd.oad.OpenResult);
format (" Name Original Packed Ratio Date Time Attr Method CRC\n");
format ("-------------- -------- -------- ------ -------- -------- ---- ------- --------\n");
int nfiles = 0;
int org_sz = 0, comp_sz = 0;
int e;
while (1)
{
e = rd.read_header ();
if (e)
{
if (e == ERAR_END_ARCHIVE)
break;
return header_err (e);
}
if (u_glob.match (rd.hd.FileName, u_opt & O_STRICT, u_opt & O_RECURSIVE))
{
nfiles++;
org_sz += rd.hd.UnpSize;
comp_sz += rd.hd.PackSize;
if (u_cmd == C_VLIST)
format ("%s\n%15c", rd.hd.FileName, ' ');
else
{
char *p = find_last_slash (rd.hd.FileName);
format ("%-14s ", p ? p + 1 : rd.hd.FileName);
}
int ratio = calc_ratio (rd.hd.PackSize, rd.hd.UnpSize);
format ("%8d %8d%c%3d.%d%%%c%02d-%02d-%02d %02d:%02d:%02d %s %-7s %08x\n",
rd.hd.UnpSize, rd.hd.PackSize,
rd.hd.Flags & FRAR_PREVVOL ? '<' : ' ',
ratio / 10, ratio % 10,
rd.hd.Flags & FRAR_NEXTVOL ? '>' : ' ',
((rd.hd.FileTime >> 25) + 80) % 100,
(rd.hd.FileTime >> 21) & 15,
(rd.hd.FileTime >> 16) & 31,
(rd.hd.FileTime >> 11) & 31,
(rd.hd.FileTime >> 5) & 63,
(rd.hd.FileTime & 31) * 2,
attr_string (rd.hd.FileAttr),
method_string (rd.hd.Method),
rd.hd.FileCRC);
}
e = rd.skip ();
if (e)
return process_err (e, rd.hd.FileName);
}
if (nfiles)
{
format ("-------------- -------- -------- ------\n");
char b[32];
sprintf (b, "%d File%s", nfiles, nfiles == 1 ? "" : "s");
int ratio = calc_ratio (comp_sz, org_sz);
format ("%12s %8d %8d %3d.%d%%\n",
b, org_sz, comp_sz, ratio / 10, ratio % 10);
}
return 0;
}
int
UnRAR::print ()
{
format (IDS_PRINT_NOT_IMPL);
return ERROR_NOT_SUPPORT;
}
int
UnRAR::comment ()
{
format (IDS_COMMENT_NOT_IMPL);
return ERROR_NOT_SUPPORT;
}
int
UnRAR::xmain (int ac, char **av)
{
int e = parse_opt (ac, av);
if (e)
return e;
switch (u_cmd)
{
case C_EXTRACT:
case C_EXTRACT_NODIR:
return extract ();
case C_PRINT:
return print ();
case C_LIST:
case C_VLIST:
return list ();
case C_TEST:
return test ();
case C_COMMENT:
return comment ();
}
return 0;
}