wxWidgets/utils/Install/sfxace/unace.c
Bryan Petty f6bcfd974e merged 2.2 branch
git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@7748 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
2000-07-15 19:51:35 +00:00

556 lines
14 KiB
C

/* ------------------------------------------------------------------------ */
/* */
/* Main file of public UNACE. */
/* */
/* ------------------------------------------------------------------------ */
#define INCL_WIN /* Window Manager Functions */
#define INCL_DOS
#define INCL_BASE
//--------------- include general files ------------------------------------//
#include <ctype.h> // tolower()
#include <fcntl.h> // open()
#include <stdio.h> // printf() sprintf() remove()
#include <stdlib.h> // malloc()
#include <string.h> // str*()
#include <sys/types.h>
#include <sys/stat.h> // S_I* AMIGA: fstat()
#if (!defined(__EMX__) && !defined(__OS2__) && !defined(WINNT) && !defined(WIN32)) || defined(__CYGWIN__)
#include <sys/errno.h>
#endif
#if !defined(__EMX__) && !defined(__OS2__) && !defined(WIN32) && !defined(WINNT)
#define stricmp strcasecmp
#endif
#include "install.h"
#include "instsup.h"
extern int installstate;
off_t acelseek(off_t offset, int whence);
int aceread(void *buf, size_t count);
int aceopen(const char *path, int flags);
int aceclose(int fd);
int acesize(void);
int acetell(int fd);
char *replacestr(char *str1, char *str2, char *str3);
//--------------- include unace specific header files ----------------------//
#include "os.h"
#include "globals.h"
#include "portable.h"
#include "uac_comm.h"
#include "uac_crc.h"
#include "uac_crt.h"
#include "uac_dcpr.h"
#include "uac_sys.h"
#ifdef CRYPT
#include "unace_ps.h"
#endif /* CRYPT */
//--------------- BEGIN OF UNACE ROUTINES ----------------------------------//
int pipeit(char *format, ...)
{
/* Do nothing ... perhaps pipe this somewhere in the future */
return 0;
}
void init_unace(void) // initializes unace
{
buf_rd =malloc(size_rdb * sizeof(ULONG)); // Allocate buffers: increase
buf =malloc(size_buf); // sizes when possible to speed
buf_wr =malloc(size_wrb); // up the program
readbuf=malloc(size_headrdb);
if (buf_rd ==NULL ||
buf ==NULL ||
buf_wr ==NULL ||
readbuf==NULL )
f_err = ERR_MEM;
make_crctable(); // initialize CRC table
dcpr_init(); // initialize decompression
set_handler(); // ctrl+break etc.
}
void done_unace(void)
{
if (buf_rd ) free(buf_rd );
if (buf ) free(buf );
if (buf_wr ) free(buf_wr );
if (readbuf ) free(readbuf );
if (dcpr_text) free(dcpr_text);
}
INT read_header(INT print_err) // reads any header from archive
{
USHORT rd,
head_size,
crc_ok;
LONG crc;
UCHAR *tp=readbuf;
acelseek(skipsize, SEEK_CUR); // skip ADDSIZE block
if (aceread(&head, 4)<4)
return (0); // read CRC and header size
#ifdef HI_LO_BYTE_ORDER
WORDswap(&head.HEAD_CRC);
WORDswap(&head.HEAD_SIZE);
#endif
// read size_headrdb bytes into
head_size = head.HEAD_SIZE; // header structure
rd = (head_size > size_headrdb) ? size_headrdb : head_size;
if (aceread(readbuf, rd) < rd)
return (0);
head_size -= rd;
crc = getcrc(CRC_MASK, readbuf, rd);
while (head_size) // skip rest of header
{
rd = (head_size > size_buf) ? size_buf : head_size;
if (aceread(buf, rd) < rd)
return (0);
head_size -= rd;
crc = getcrc(crc, (UCHAR*)buf, rd);
}
head.HEAD_TYPE =*tp++; // generic buffer to head conversion
head.HEAD_FLAGS=BUFP2WORD(tp);
if (head.HEAD_FLAGS & ACE_ADDSIZE)
skipsize = head.ADDSIZE = BUF2LONG(tp); // get ADDSIZE
else
skipsize = 0;
// check header CRC
if (!(crc_ok = head.HEAD_CRC == (crc & 0xffff)) && print_err)
pipeit("\nError: archive is broken\n");
else
switch (head.HEAD_TYPE) // specific buffer to head conversion
{
case MAIN_BLK:
memcpy(mhead.ACESIGN, tp, acesign_len); tp+=acesign_len;
mhead.VER_MOD=*tp++;
mhead.VER_CR =*tp++;
mhead.HOST_CR=*tp++;
mhead.VOL_NUM=*tp++;
mhead.TIME_CR=BUFP2LONG(tp);
mhead.RES1 =BUFP2WORD(tp);
mhead.RES2 =BUFP2WORD(tp);
mhead.RES =BUFP2LONG(tp);
mhead.AV_SIZE=*tp++;
memcpy(mhead.AV, tp, rd-(USHORT)(tp-readbuf));
break;
case FILE_BLK:
fhead.PSIZE =BUFP2LONG(tp);
fhead.SIZE =BUFP2LONG(tp);
fhead.FTIME =BUFP2LONG(tp);
fhead.ATTR =BUFP2LONG(tp);
fhead.CRC32 =BUFP2LONG(tp);
fhead.TECH.TYPE =*tp++;
fhead.TECH.QUAL =*tp++;
fhead.TECH.PARM =BUFP2WORD(tp);
fhead.RESERVED =BUFP2WORD(tp);
fhead.FNAME_SIZE=BUFP2WORD(tp);
memcpy(fhead.FNAME, tp, rd-(USHORT)(tp-readbuf));
break;
// default: (REC_BLK and future things):
// do nothing 'cause isn't needed for extraction
}
return (crc_ok);
}
// maximum SFX module size
#define max_sfx_size 65536 // (needed by read_arc_head)
INT read_arc_head(void) // searches for the archive header and reads it
{
INT i,
flags,
buf_pos = 0;
LONG arc_head_pos,
old_fpos,
fpos = 0;
struct stat st;
fstat(archan, &st);
memset(buf, 0, size_buf);
while (acetell(archan)<acesize() && fpos < max_sfx_size)
{
old_fpos = fpos;
fpos += aceread(&buf[buf_pos], size_buf - buf_pos);
for (i = 0; i < size_buf; i++) // look for the acesign
{
if (!memcmp(acesign, &buf[i], acesign_len))
{
// seek to the probable begin
// of the archive
arc_head_pos = old_fpos + i - buf_pos - bytes_before_acesign;
acelseek(arc_head_pos, SEEK_SET);
if (read_header(0)) // try to read archive header
{
flags = mhead.HEAD_FLAGS;
adat.sol = (flags & ACE_SOLID) > 0;
adat.vol = (flags & ACE_MULT_VOL) > 0;
adat.vol_num = mhead.VOL_NUM;
adat.time_cr = mhead.TIME_CR;
return (1);
}
}
}
// was no archive header,
// continue search
acelseek(fpos, SEEK_SET);
memcpy(buf, &buf[size_buf - 512], 512);
buf_pos = 512; // keep 512 old bytes
}
return (0);
}
INT open_archive(INT print_err) // opens archive (or volume)
{
CHAR av_str[80];
#if defined(__OS2_) || defined(__EMX__) || defined(WIN32)
archan = aceopen(aname, O_RDONLY | O_BINARY); // open file
#else
archan = aceopen(aname, O_RDONLY); // open file
#endif
#if !defined(__EMX__) && !defined(__OS2__)
farchan = fdopen(archan, "rb");
#endif
if (archan == -1)
{
error("\nError opening file %s", aname);
return (0);
}
if (!read_arc_head()) // read archive header
{
error("Invalid embedded archive file.");
#if !defined(__EMX__) && !defined(__OS2__)
fclose(farchan);
#endif
aceclose(archan);
return (0);
}
pipeit("\nProcessing archive: %s\n\n", aname);
if (head.HEAD_FLAGS & ACE_AV)
{
pipeit("Authenticity Verification:"); // print the AV
sprintf(av_str, "\ncreated on %d.%d.%d by ",
ts_day(adat.time_cr), ts_month(adat.time_cr), ts_year(adat.time_cr));
pipeit(av_str);
strncpy(av_str, (CHAR*)mhead.AV, mhead.AV_SIZE);
av_str[mhead.AV_SIZE] = 0;
pipeit("%s\n\n", av_str);
}
comment_out("Main comment:"); // print main comment
return (1);
}
void get_next_volname(void) // get file name of next volume
{
CHAR *cp;
INT num;
if ((cp = (CHAR *) strrchr(aname, '.')) == NULL || !*(cp + 1))
num = -1;
else
{
cp++;
num = (*(cp + 1) - '0') * 10 + *(cp + 2) - '0';
if (!in(num, 0, 99))
num = -1;
if (in(*cp, '0', '9'))
num += (*cp - '0') * 100;
}
num++;
if (num < 100)
*cp = 'C';
else
*cp = num / 100 + '0';
*(cp + 1) = (num / 10) % 10 + '0';
*(cp + 2) = num % 10 + '0';
}
INT proc_vol(void) // opens volume
{
INT i;
CHAR s[80];
if (!fileexists(aname) || !f_allvol_pr)
{
do
{
sprintf(s, "Ready to process %s?", aname);
#if defined(__MINGW32__)
beep(500,500);
#else
beep();
#endif
i = confirm(s); // ask whether ready or not
f_allvol_pr = (i == 1); // "Always" --> process all volumes
if (i >= 2)
{
f_err = ERR_FOUND;
return (0);
}
}
while (!fileexists(aname));
}
if (!open_archive(1)) // open volume
{
pipeit("\nError while opening archive. File not found or archive broken.\n");
f_err = ERR_OPEN;
return (0);
}
return (1);
}
INT proc_next_vol(void) // opens next volume to process
{
#if !defined(__EMX__) && !defined(__OS2__)
fclose(farchan);
#endif
aceclose(archan); // close handle
get_next_volname(); // get file name of next volume
if (!proc_vol()) // try to open volume, read archive header
return 0;
if (!read_header(1)) // read 2nd header
{
f_err=ERR_READ;
return 0;
}
return 1;
}
INT read_adds_blk(CHAR * buffer, INT len) // reads part of ADD_SIZE block
{
INT rd = 0,
l = len;
LONG i;
#ifdef CRYPT
char *cbuffer=buffer;
if (head.HEAD_TYPE == FILE_BLK && (head.HEAD_FLAGS & ACE_PASSW))
len = crypt_len(len);
#endif /* CRYPT */
while (!f_err && len && skipsize)
{
i = (skipsize > len) ? len : skipsize;
skipsize -= i;
/* How do I test failure when compiling -mno-cygwin? */
#if !defined(__MINGW32__)
errno = 0;
#endif
rd += aceread(buffer, i);
#if !defined(__MINGW32__)
if (errno)
{
error("\nRead error\n");
f_err = ERR_READ;
}
#endif
buffer += i;
len -= i;
if (!skipsize) // if block is continued on next volume
if (head.HEAD_FLAGS & ACE_SP_AFTER && !proc_next_vol())
break;
}
#ifdef CRYPT
if (head.HEAD_TYPE == FILE_BLK && (head.HEAD_FLAGS & ACE_PASSW))
decrypt(cbuffer, rd);
#endif /* CRYPT */
return (rd > l ? l : rd);
}
void crc_print(void) // checks CRC, prints message
{
INT crc_not_ok = rd_crc != fhead.CRC32; /* check CRC of file */
if(crc_not_ok && installstate != ABORTED)
error("CRC error reading archive!");
}
void analyze_file(void) // analyzes one file (for solid archives)
{
pipeit("\n Analyzing");
flush;
while (!cancel() && (dcpr_adds_blk(buf_wr, size_wrb))) // decompress only
;
crc_print();
}
void extract_file(void) // extracts one file
{
INT rd;
pipeit("\n Extracting");
flush; // decompress block
while (!cancel() && (rd = dcpr_adds_blk(buf_wr, size_wrb)))
{
if (write(wrhan, buf_wr, rd) != rd) // write block
{
error("\nWrite error\n");
f_err = ERR_WRITE;
}
}
crc_print();
}
/* extracts or tests all files of the archive
*/
void extract_files(int nopath, int test, char *getfilename)
{
CHAR file[PATH_MAX];
char *tmpfile;
extern unsigned current_file;
extern char installdir2[];
while (!cancel() && read_header(1))
{
if (head.HEAD_TYPE == FILE_BLK)
{
if(installstate == ABORTED)
return;
current_file++;
if(installstate == INSTALLING)
DoGUI();
comment_out("File comment:"); // show file comment
ace_fname(file, &head, nopath); // get file name
/* We allow expansion of "userdir" to the selected directory by the user */
tmpfile = replacestr(file, "USERDIR", installdir2);
strcpy(file, tmpfile);
free(tmpfile);
if(!getfilename || (getfilename && stricmp(getfilename, file) == 0))
{
pipeit("\n%s", file);
flush;
dcpr_init_file(); // initialize decompression of file
if (!f_err)
{
if (test ||
(wrhan = create_dest_file(file, (INT) fhead.ATTR))<0)
{
if (test || adat.sol)
analyze_file(); // analyze file
}
else
{
extract_file(); // extract it
#ifdef DOS // set file time
_dos_setftime(wrhan, (USHORT) (fhead.FTIME >> 16), (USHORT) fhead.FTIME);
#endif
close(wrhan);
#if defined(__OS2__) || defined(__EMX__)
{
FILESTATUS3 fileinfo;
DosQueryPathInfo(file, FIL_STANDARD, &fileinfo, sizeof(FILESTATUS3));
*((USHORT*)&fileinfo.fdateCreation) = (USHORT)(fhead.FTIME >> 16);
*((USHORT*)&fileinfo.ftimeCreation) = (USHORT)fhead.FTIME;
*((USHORT*)&fileinfo.fdateLastAccess) = (USHORT)(fhead.FTIME >> 16);
*((USHORT*)&fileinfo.ftimeLastAccess) = (USHORT)fhead.FTIME;
*((USHORT*)&fileinfo.fdateLastWrite) = (USHORT)(fhead.FTIME >> 16);
*((USHORT*)&fileinfo.ftimeLastWrite) = (USHORT)fhead.FTIME;
DosSetPathInfo(file, FIL_STANDARD, (PVOID)&fileinfo, sizeof(FILESTATUS3), 0);
}
#endif
#ifdef DOS // set file attributes
_dos_setfileattr(file, (UINT) fhead.ATTR);
#endif
#ifdef AMIGA
{ // set file date and time
struct DateTime dt;
char Date[9], Time[9];
ULONG tstamp=fhead.FTIME;
sprintf(Date, "%02d-%02d-%02d", ts_year(tstamp)-1900, ts_month(tstamp), ts_day(tstamp));
sprintf(Time, "%02d:%02d:%02d", ts_hour(tstamp), ts_min(tstamp), ts_sec(tstamp));
dt.dat_Format = FORMAT_INT;
dt.dat_Flags = 0;
dt.dat_StrDate= Date;
dt.dat_StrTime= Time;
if (StrToDate(&dt))
SetFileDate(file, &dt.dat_Stamp);
}
#endif
if (f_err)
remove(file);
}
}
}
}
}
}
unsigned percentage(ULONG p, ULONG d)
{
return (unsigned)( d ? (d/2+p*100)/d : 100 );
}
int installer_unpack(CHAR * filename, int operation) // processes the archive
{
init_unace(); // initialize unace
strcpy(aname, "installer"); // get archive name
if(filename)
f_ovrall=1;
if (open_archive(1)) // open archive to process
{
if (adat.vol_num)
pipeit("\nFirst volume of archive required!\n");
else
{
if(operation == 2)
extract_files(0, 0, filename);
}
#if !defined(__EMX__) && !defined(__OS2__)
fclose(farchan);
#endif
aceclose(archan);
if (f_err)
{
pipeit("\nError occurred\n");
if (f_criterr)
pipeit("Critical error on drive %c\n", f_criterr);
}
}
done_unace();
return 0;
}