This file contains source code and technical data sampled from the 2.00
release of ARJ.
ARJ TECHNICAL INFORMATION March 1991
Modification history:
Date Description of modification:
03/11/91 Added test_dirtype(). Added directory file type.
02/23/91 Added more comments.
02/19/91 Added get_line() function.
01/21/91 Added get_recd_size() function.
01/10/91 Corrected timestamp description and header order of file mode.
10/30/90 Corrected values of flags in ARJ flags.
ARJ archives contains two types of header blocks:
Archive main header - This is located at the head of the archive
Local file header - This is located before each archived file
Structure of archive block (low order byte first):
Bytes Description
----- -------------------------------------------------------------------
2 header id (main and local file) = 0xEA60 or 60000U
2 basic header size (from 'first_hdr_size' thru 'comment' below)
= first_hdr_size + strlen(filename) + 1 + strlen(comment) + 1
= 0 if end of archive
1 first_hdr_size (size up to and including 'extra data')
1 archiver version number
1 minimum archiver version to extract
1 host OS (0 = MSDOS, 1 = PRIMOS, 2 = UNIX, 3 = AMIGA, 4 = MACDOS)
1 arj flags (0x01 = GARBLED_FLAG) indicates passworded file
(0x02 = RESERVED)
(0x04 = VOLUME_FLAG) indicates continued file to next
volume
(0x08 = EXTFILE_FLAG) indicates file starting position field
(0x10 = PATHSYM_FLAG) indicates path translated
("\" changed to "/")
1 method (0 = stored, 1 = compressed most ... 4 compressed fastest)
1 file type (0 = binary, 1 = 7-bit text, 2 = comment header)
(3 = directory)
1 reserved
4 date time modified
4 compressed size
4 original size (this will be different for text mode compression)
4 original file's CRC
2 filespec position in filename
2 file access mode
2 host data (currently not used)
? extra data
4 bytes for extended file position when used
(this is present when EXTFILE_FLAG is set)
? filename (null-terminated string)
? comment (null-terminated string)
4 basic header CRC
2 1st extended header size (0 if none)
? 1st extended header (not currently used)
4 1st extended header's CRC
...
? compressed file
Time stamp format:
31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16
|<---- year-1980 --->|<- month ->|<--- day ---->|
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
|<--- hour --->|<---- minute --->|<- second/2 ->|
Compression methods:
ARJ methods 1 to 3 use Lempel-Ziv 77 sliding window with static Huffman
encoding.
ARJ method 4 uses Lempel-Ziv 77 sliding window with pointer/length
unary encoding.
There is one decoder for methods 1 to 3 and one decoder for method 4.
Encryption technology:
ARJ does NOT use DES encryption algorithms. It uses a combination of
simple exclusive-or operations.
Source code description:
ARJ is written in ANSI C using only ANSI C library calls. All machine
dependent routines are located in an environment module. Simplified
versions allow porting to other operating systems with some loss of
functionality. The current version has been compiled with TURBO C++
1.0.
Data is stored in low-byte order first and read back the same way to
avoid the "endia" issue.
The environment routines are:
case_path() Uppercase pathname if case is not significant for OS
file_exists() Check for file existence
fix_path() Clean up file pathname (remove drive and root)
get_archive() Get state of archive bit
get_disk_avail() Get disk space availability
get_line() Get STDIN line of text.
get_recd_size() Get minimum disk record size
get_fsize() Get file size (return -1 if getting size is too
expensive)
get_ftime() Get file date-time modified
get_fmode() Get file access mode
get_mode_str() Convert access mode to displayable text (A--W)
match_wild() Provide filename component matching
reset_archive() Reset archive bit
set_fmode() Set file access mode
set_ftime() Set file date-time modified
set_stream() Set stream file translation mode.
test_ftype() Test the output file type for valid flat file
test_dirtype() Test the file for the directory type.
test_dir() Test if file path exist. If not, create it with
permission.
wild_list() Expand wildnames and recurse subdirectories if necessary
end of document
[ENVIRON.C follows]
/* ENVIRON.C, UNARJ, R JUNG, 04/09/91
/* Implementation dependent routines
/* Copyright (c) 1991 by Robert K Jung. All rights reserved.
/*
/* This code may be freely used in programs that are NOT archivers.
/*
/* Modification history:
/* Date Programmer Description of modification.
/* 04/09/91 R. Jung Rewrote code.
/*
*/
#include "unarj.h"
#ifdef __TURBOC__
#define SUBS_DEFINED
#include
#include
#include
#include
void
case_path(char *name)
{
strupper(name);
}
int
file_exists(char *name)
{
return (access(name, 0) == 0);
}
int
fix_path(char *to_name, char *fr_name)
{
char *p;
p = fr_name;
if (p[0] && p[1] == ':') /* get rid of drive */
p += 2;
if (p[0] == '.')
{
if (p[1] == '.' && p[2] == '\\')
p += 2;
else if (p[1] == '\\')
p++;
}
if (p[0] == '\\') /* get rid of root dir */
p++;
if (to_name != NULL)
{
strcpy(to_name, p);
strupper(p);
}
return (int)(p - fr_name);
}
int
set_fmode(char *name, ushort attribute)
{
if (_chmod(name, 1, attribute) == -1)
return -1;
return 0;
}
int
set_ftime(char *name, ulong tstamp)
{
FILE *fd;
int code;
if ((fd = fopen(name, "rb")) == NULL)
return -1;
code = setftime(fileno(fd), (struct ftime *) &tstamp);
fclose(fd);
return code;
}
#endif
#ifdef _QC
#define SUBS_DEFINED
#include
#include
#include
#include
void
case_path(char *name)
{
strupper(name);
}
int
file_exists(char *name)
{
return (access(name, 0) == 0);
}
int
fix_path(char *to_name, char *fr_name)
{
char *p;
p = fr_name;
if (p[0] && p[1] == ':') /* get rid of drive */
p += 2;
if (p[0] == '\\') /* get rid of root dir */
p++;
if (to_name != NULL)
{
strcpy(to_name, p);
strupper(p);
}
return (int)(p - fr_name);
}
int
set_fmode(char *name, ushort attribute)
{
return _dos_setfileattr(name, (uint)attribute);
}
int
set_ftime(char *name, ulong tstamp)
{
FILE *fd;
int code;
uint date_stamp, time_stamp;
date_stamp = (uint)(tstamp >> 16);
time_stamp = (uint)(tstamp & 0xFFFF);
if ((fd = fopen(name, "rb")) == NULL)
return -1;
code = _dos_setftime(fileno(fd), date_stamp, time_stamp);
fclose(fd);
return code;
}
#endif
#ifndef SUBS_DEFINED /* vanilla version for other compilers */
#include
void
case_path(char *name)
{
}
int
file_exists(char *name)
{
FILE *fd;
if ((fd = fopen(name, "rb")) == NULL)
return 0;
fclose(fd);
return 1;
}
int
fix_path(char *to_name, char *fr_name)
{
strcpy(to_name, fr_name);
return 0;
}
int
set_fmode(char *name, ushort attribute)
{
return 0;
}
int
set_ftime(char *name, ulong tstamp)
{
return 0;
}
#endif /* end of vanilla section */
/* end ENVIRON.C */
[ARJ.H follows]
/* UNARJ.H, UNARJ, R JUNG, 04/05/91
/* Include file
/* Copyright (c) 1990 by Robert K Jung. All rights reserved.
/*
/* This code may be freely used in programs that are NOT archivers.
/*
/* Modification history:
/* Date Programmer Description of modification.
/* 04/05/91 R. Jung Rewrote code.
/*
*/
#ifndef _ARH_DEF_
#define _ARH_DEF_
#include
#include
typedef unsigned char uchar; /* 8 bits or more */
typedef unsigned int uint; /* 16 - 32 bits or more */
typedef unsigned short ushort; /* 16 bits or more */
typedef unsigned long ulong; /* 32 bits or more */
#define USHRT_BIT (CHAR_BIT * sizeof(ushort))
/* ********************************************************* */
/* Environment definitions (implementation dependent) */
/* ********************************************************* */
#ifdef _QC
#define __MSDOS__
#endif
#ifdef __MSDOS__
#define OS 0
#define WILDSTRING "?*"
#define WILD_COMPONENT '*'
#define WILD_CHAR '?'
#define WILD_ANY "*.*"
#define PATH_SEPARATORS "\\:"
#define PATH_CHAR '\\'
#define SWITCH_CHARS "-/"
#define MAXSFX 25000L
#endif
#ifdef __CI
#define PRIME 1
#define OS 1
#define WILDSTRING "@+^"
#define WILD_COMPONENT '@'
#define WILD_CHAR '+'
#define WILD_ANY "@@"
#define PATH_SEPARATORS ">"
#define PATH_CHAR '>'
#define ARJ_ENVSTR ".ARJ_SW"
#define LINES_PER_PAGE 24
#define FIX_PARITY(c) c |= ~ASCII_MASK
#define DEFAULT_DIR "*>"
#endif
/* Error levels */
#ifndef ERROR_DEFINES
#define ERROR_OK 0 /* success */
#define ERROR_WARN 1 /* minor problem (file not found) */
#define ERROR_FAIL 2 /* fatal error */
#define ERROR_CRC 3 /* CRC error */
#define ERROR_SECURE 4 /* ARJ security invalid or not found */
#define ERROR_WRITE 5 /* disk full */
#define ERROR_OPEN 6 /* can't open file */
#define ERROR_USER 7 /* user specified bad parameters */
#define ERROR_MEMORY 8 /* not enough memory */
#endif
#ifndef MAXSFX /* size of self-extracting prefix */
#define MAXSFX 500000L
#endif
#ifndef FNAME_MAX
#define FNAME_MAX 512
#endif
#ifndef SWITCH_CHARS
#define SWITCH_CHARS "-"
#endif
#ifndef FIX_PARITY
#define FIX_PARITY(c) c &= ASCII_MASK
#endif
#ifndef ARJ_SUFFIX
#define ARJ_SUFFIX ".ARJ"
#endif
#ifndef BAK_SUFFIX
#define BAK_SUFFIX ".BAK"
#endif
#ifndef ARJ_TEMP
#define ARJ_TEMP "ARJTEMP.$??"
#endif
#ifndef ARJ_TEMPLATE
#define ARJ_TEMPLATE "ARJTEMP.$%02d" /* related to ARJ_TEMP */
#endif
#ifndef ARJSORT_TEMP
#define ARJSORT_TEMP "ARJSORT.$$$"
#endif
#ifndef ARJ_ENVSTR
#define ARJ_ENVSTR "ARJ_SW"
#endif
#ifndef ARJ_DOT
#define ARJ_DOT '.'
#endif
#ifndef ARCHIVE_SUFFIXES
#define ARCHIVE_SUFFIXES ".ARJ.ZIP.PAK.ARC.LZH"
#endif
#ifndef LINES_PER_PAGE
#define LINES_PER_PAGE 24
#endif
#ifndef WILDSTRING
#define WILDSTRING "?*"
#endif
#ifndef WILD_COMPONENT
#define WILD_COMPONENT '*'
#endif
#ifndef WILD_CHAR
#define WILD_CHAR '?'
#endif
#ifndef WILD_ANY
#define WILD_ANY "*.*"
#endif
#ifndef NOT_MATCH_ANY
#define NOT_MATCH_ANY "..."
#endif
#ifndef LIST_CHAR
#define LIST_CHAR '!'
#endif
#ifndef DEFAULT_DIR
#define DEFAULT_DIR ""
#endif
/* ********************************************************* */
/* end of environmental defines */
/* ********************************************************* */
/* ********************************************************* */
/*
/* Structure of archive block (low order byte first):
/*
/* 2 header id (comment and local file) = 0xEA60 or 60000U
/* 2 basic header size (from 'first_hdr_size' thru 'comment' below)
/* = first_hdr_size + strlen(filename) + 1 + strlen(comment) + 1
/* = 0 if end of archive
/*
/* 1 first_hdr_size (size up to 'extra data')
/* 1 archiver version number
/* 1 minimum archiver version to extract
/* 1 host OS (0 = MSDOS, 1 = PRIMOS, 2 = UNIX, 3 = AMIGA, 4 = MACDOS)
/* 1 arj flags (0x01 = GARBLED_FLAG, 0x02 = RESERVED)
/* (0x04 = VOLUME_FLAG, 0x08 = EXTFILE_FLAG)
/* (0x10 = PATHSYM_FLAG)
/* 1 method (0 = stored, 1 = compressed most ... 4 compressed fastest)
/* 1 file type (0 = binary, 1 = text, 2 = comment header)
/* 1 garble password modifier
/* 4 date time stamp modified
/* 4 compressed size
/* 4 original size
/* 4 original file's CRC
/* 2 entryname position in filename
/* 2 file access mode
/* 2 host data
/* ? extra data
/* 4 bytes for extended file position
/*
/* ? filename (null-terminated)
/* ? comment (null-terminated)
/*
/* 4 basic header CRC
/*
/* 2 1st extended header size (0 if none)
/* ? 1st extended header
/* 4 1st extended header's CRC
/* ...
/* ? compressed file
/*
/* ********************************************************* */
/* ********************************************************* */
/* */
/* Time stamp format: */
/* */
/* 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 */
/* |<---- year-1980 --->|<- month ->|<--- day ---->| */
/* */
/* 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 */
/* |<--- hour --->|<---- minute --->|<- second/2 ->| */
/* */
/* ********************************************************* */
#define CODE_BIT 16
#define NULL_CHAR '\0'
#define MAXMETHOD 4
#define ARJ_VERSION 3
#define ARJ_X_VERSION 3 /* decoder version */
#define ARJ_X1_VERSION 1
#define DEFAULT_METHOD 1
#define DEFAULT_TYPE 0 /* if type_sw is selected */
#define HEADER_ID 0xEA60U
#define HEADER_ID_HI 0xEA
#define HEADER_ID_LO 0x60
#define FIRST_HDR_SIZE 30
#define FIRST_HDR_SIZE_V 34
#define COMMENT_MAX 2048
#define HEADERSIZE_MAX (FIRST_HDR_SIZE + 10 + FNAME_MAX + COMMENT_MAX)
#define BINARY_TYPE 0 /* This must line up with binary/text strings */
#define TEXT_TYPE 1
#define COMMENT_TYPE 2
#define DIR_TYPE 3
#define GARBLE_FLAG 0x01
#define VOLUME_FLAG 0x04
#define EXTFILE_FLAG 0x08
#define PATHSYM_FLAG 0x10
typedef ulong UCRC; /* CRC-32 */
#define CRC_MASK 0xFFFFFFFFUL
#define ARJ_PATH_CHAR '/'
/* Timestamp macros */
#define get_tstamp(yy, mm, dd, hr, mn, sc) \
( (((ulong)(yy - 1980)) << 25) + ((ulong)mm << 21) + ((ulong)dd << 16) + \
((ulong)hr << 11) + (mn << 5) + (sc / 2) )
#define ts_year(ts) ((uint)((ts >> 25) & 0x7f) + 1980)
#define ts_month(ts) ((uint)(ts >> 21) & 0x0f) /* 1..12 means Jan..Dec */
#define ts_day(ts) ((uint)(ts >> 16) & 0x1f) /* 1..31 means 1st..31st */
#define ts_hour(ts) ((uint)(ts >> 11) & 0x1f)
#define ts_min(ts) ((uint)(ts >> 5) & 0x3f)
#define ts_sec(ts) ((uint)((ts & 0x1f) * 2))
/* unarj.c */
extern long origsize;
extern long compsize;
extern UCRC crc;
extern FILE *arcfile;
extern FILE *outfile;
extern ushort bitbuf;
extern uchar subbitbuf;
extern uchar header[HEADERSIZE_MAX];
extern char arc_name[FNAME_MAX];
extern int bitcount;
extern int file_type;
extern int error_count;
void strupper(char *str);
void *malloc_msg(size_t size);
void error(char *fmt, ...);
void fillbuf(int n);
ushort getbits(int n);
void fwrite_txt_crc(uchar *p, uint n);
void init_getbits(void);
/* environ.c */
void case_path(char *name);
int file_exists(char *name);
int fix_path(char *to_name, char *fr_name);
int set_ftime(char *name, ulong timestamp);
int set_fmode(char *name, ushort fmode);
/* decode.c */
void decode(void);
void decode_f(void);
/* Message strings */
extern char M_VERSION [];
extern char M_USAGE [];
extern char M_ARCDATE [];
extern char M_BADCOMNT[];
extern char M_BADHEADR[];
extern char M_BADTABLE[];
extern char M_CANTOPEN[];
extern char M_CANTREAD[];
extern char M_CANTWRIT[];
extern char M_CRCERROR[];
extern char M_CRCOK [];
extern char M_DIFFHOST[];
extern char M_DIR [];
extern char M_ENCRYPT [];
extern char M_ERRORCNT[];
extern char M_EXTRACT [];
extern char M_FEXISTS [];
extern char M_HEADRCRC[];
extern char M_NBRFILES[];
extern char M_NOMEMORY[];
extern char M_NOTARJ [];
extern char M_PROCARC [];
extern char M_SKIPPED [];
extern char M_SUFFIX [];
extern char M_TESTING [];
extern char M_UNKNMETH[];
extern char M_UNKNTYPE[];
extern char M_UNKNVERS[];
extern char M_UNSTORE [];
#endif
/* end UNARJ.H */
[ARJ.C follows]
/* UNARJ.C, UNARJ, R JUNG, 04/05/91
/* Main Extractor routine
/* Copyright (c) 1991 by Robert K Jung. All rights reserved.
/*
/* This code may be freely used in programs that are NOT archivers.
/*
/* Modification history:
/* Date Programmer Description of modification.
/* 04/05/91 R. Jung Rewrote code.
/*
*/
#include "unarj.h"
#include
#include
#include
#include
/* Global variables */
UCRC crc;
FILE *arcfile;
FILE *outfile;
ushort bitbuf;
long compsize;
long origsize;
uchar subbitbuf;
uchar header[HEADERSIZE_MAX];
char arc_name[FNAME_MAX];
int bitcount;
int file_type;
int error_count;
/* Messages */
char M_VERSION [] = "UNARJ 2.00 Copyright (c) 1991 Robert K Jung\n\n";
char M_USAGE [] = "Usage: UNARJ archive[.arj] [NUL]\n";
char M_ARCDATE [] = "Archive date : %s\n";
char M_BADCOMNT[] = "Invalid comment header";
char M_BADHEADR[] = "Bad header";
char M_BADTABLE[] = "Bad Huffman code (%d)";
char M_CANTOPEN[] = "Can't open %s";
char M_CANTREAD[] = "Can't read file or unexpected end of file";
char M_CANTWRIT[] = "Can't write file. Disk full?";
char M_CRCERROR[] = "CRC error!\n";
char M_CRCOK [] = "CRC OK\n";
char M_DIFFHOST[] = ", Warning! Binary file from a different OS";
char M_DIR [] = "directory ";
char M_ENCRYPT [] = "File is password encrypted, ";
char M_ERRORCNT[] = "Found %5d error(s)!";
char M_EXTRACT [] = "Extracting %-12s";
char M_FEXISTS [] = "%-12s exists, ";
char M_HEADRCRC[] = "Header CRC error!";
char M_NBRFILES[] = "%5d file(s)\n";
char M_NOMEMORY[] = "Out of memory";
char M_NOTARJ [] = "%s is not an ARJ archive";
char M_PROCARC [] = "Processing archive: %s\n";
char M_SKIPPED [] = "Skipped %s\n";
char M_SUFFIX [] = ARJ_SUFFIX;
char M_TESTING [] = "Testing %-12s";
char M_UNKNMETH[] = "Unknown method: %d, ";
char M_UNKNTYPE[] = "Unknown file type: %d, ";
char M_UNKNVERS[] = "Unknown version: %d, ";
char M_UNSTORE [] = "Unstoring ";
#define get_crc() get_longword()
#define fget_crc(f) fget_longword(f)
#define setup_get(PTR) (get_ptr = (PTR))
#define get_byte() ((uchar)(*get_ptr++ & 0xff))
#define BUFFERSIZE 4096
#define ASCII_MASK 0x7F
#define CRCPOLY 0xEDB88320UL
#if CHAR_BIT == 8
#define UPDATE_CRC(crc, c) \
crc = crctable[(uchar)crc ^ (uchar)(c)] ^ (crc >> CHAR_BIT)
#else
#define UPDATE_CRC(crc, c) \
crc = crctable[((uchar)(crc) ^ (uchar)(c)) & 0xFF] ^ (crc >> CHAR_BIT)
#endif
/* Local variables */
static char filename[FNAME_MAX];
static char comment[COMMENT_MAX];
static char *hdr_filename;
static char *hdr_comment;
static ushort headersize;
static uchar first_hdr_size;
static uchar arj_nbr;
static uchar arj_x_nbr;
static uchar host_os;
static uchar arj_flags;
static short method;
static ushort file_mode;
static ulong time_stamp;
static short entry_pos;
static ushort host_data;
static uchar *get_ptr;
static UCRC file_crc;
static UCRC header_crc;
static long first_hdr_pos;
static int no_output;
static char *writemode[2] = { "wb", "w" };
static UCRC crctable[UCHAR_MAX + 1];
/* Functions */
static void
make_crctable(void)
{
uint i, j;
UCRC r;
for (i = 0; i <= UCHAR_MAX; i++)
{
r = i;
for (j = CHAR_BIT; j > 0; j--)
{
if (r & 1)
r = (r >> 1) ^ CRCPOLY;
else
r >>= 1;
}
crctable[i] = r;
}
}
static void
crc_buf(char *str, uint len)
{
while (len--)
UPDATE_CRC(crc, *str++);
}
void
error(char *fmt,...)
{
va_list args;
va_start(args, fmt);
putc('\n', stdout);
vprintf(fmt, args);
putc('\n', stdout);
va_end(args);
exit(EXIT_FAILURE);
}
static void
strparity(uchar *p)
{
while (*p)
{
FIX_PARITY(*p);
p++;
}
}
static FILE *
fopen_msg(char *name, char *mode)
{
FILE *fd;
fd = fopen(name, mode);
if (fd == NULL)
error(M_CANTOPEN, name);
return fd;
}
static int
fget_byte(FILE *f)
{
int c;
if ((c = getc(f)) == EOF)
error(M_CANTREAD);
return c & 0xFF;
}
static uint
fget_word(FILE *f)
{
uint b0, b1;
b0 = fget_byte(f);
b1 = fget_byte(f);
return (b1 << 8) + b0;
}
static ulong
fget_longword(FILE *f)
{
ulong b0, b1, b2, b3;
b0 = fget_byte(f);
b1 = fget_byte(f);
b2 = fget_byte(f);
b3 = fget_byte(f);
return (b3 << 24) + (b2 << 16) + (b1 << 8) + b0;
}
static uint
fread_crc(uchar *p, uint n, FILE *f)
{
n = fread(p, 1, n, f);
origsize += n;
crc_buf((char *)p, n);
return n;
}
void
fwrite_txt_crc(uchar *p, uint n)
{
uchar c;
crc_buf((char *)p, n);
if (no_output)
return;
if (file_type == TEXT_TYPE)
{
while (n--)
{
c = *p++;
FIX_PARITY(c);
if (putc(c, outfile) == EOF)
error(M_CANTWRIT);
}
}
else
{
if (fwrite(p, 1, n, outfile) != n)
error(M_CANTWRIT);
}
}
void
init_getbits(void)
{
bitbuf = 0;
subbitbuf = 0;
bitcount = 0;
fillbuf(2 * CHAR_BIT);
}
void
fillbuf(int n) /* Shift bitbuf n bits left, read n bits */
{
bitbuf <<= n;
while (n > bitcount)
{
bitbuf |= subbitbuf << (n -= bitcount);
if (compsize != 0)
{
compsize--;
subbitbuf = (uchar) getc(arcfile);
}
else
subbitbuf = 0;
bitcount = CHAR_BIT;
}
bitbuf |= subbitbuf >> (bitcount -= n);
}
ushort
getbits(int n)
{
ushort x;
x = bitbuf >> (2 * CHAR_BIT - n);
fillbuf(n);
return x;
}
static int
decode_path(char *name)
{
for ( ; *name; name++)
{
if (*name == ARJ_PATH_CHAR)
*name = PATH_CHAR;
}
return 1;
}
static void
get_date_str(char *str, ulong tstamp)
{
sprintf(str, "%04u-%02u-%02u %02u:%02u:%02u",
ts_year(tstamp), ts_month(tstamp), ts_day(tstamp),
ts_hour(tstamp), ts_min(tstamp), ts_sec(tstamp));
}
static int
parse_path(char *pathname, char *path, char *entry)
{
char *cptr, *ptr, *fptr;
short pos;
fptr = NULL;
for (cptr = PATH_SEPARATORS; *cptr; cptr++)
{
if ((ptr = strrchr(pathname, *cptr)) != NULL &&
(fptr == NULL || ptr > fptr))
fptr = ptr;
}
if (fptr == NULL)
pos = 0;
else
pos = fptr + 1 - pathname;
if (path != NULL)
{
strncpy(path, pathname, pos);
path[pos] = NULL_CHAR;
}
if (entry != NULL)
strcpy(entry, &pathname[pos]);
return pos;
}
static void
strncopy(char *to, char *from, int len)
{
int i;
for (i = 1; i < len && *from; i++)
*to++ = *from++;
*to = NULL_CHAR;
}
void
strupper(char *s)
{
while (*s)
{
*s = (char)toupper(*s);
s++;
}
}
void *
malloc_msg(size_t size)
{
char *p;
if ((p = (char *)malloc(size)) == NULL)
error(M_NOMEMORY);
return (void *)p;
}
static uint
get_word(void)
{
uint b0, b1;
b0 = get_byte();
b1 = get_byte();
return (b1 << 8) + b0;
}
static ulong
get_longword(void)
{
ulong b0, b1, b2, b3;
b0 = get_byte();
b1 = get_byte();
b2 = get_byte();
b3 = get_byte();
return (b3 << 24) + (b2 << 16) + (b1 << 8) + b0;
}
static long
find_header(FILE *fd, char *name)
{
long arcpos;
int c;
for (arcpos = ftell(fd); arcpos < MAXSFX; arcpos++)
{
fseek(fd, arcpos, SEEK_SET);
c = fget_byte(fd);
while (arcpos < MAXSFX)
{
if (c != HEADER_ID_LO) /* low order first */
c = fget_byte(fd);
else if ((c = fget_byte(fd)) == HEADER_ID_HI)
break;
arcpos++;
}
if ((headersize = fget_word(fd)) <= HEADERSIZE_MAX)
{
crc = CRC_MASK;
fread_crc(header, headersize, fd);
if ((crc ^ CRC_MASK) == fget_crc(fd))
{
fseek(fd, arcpos, SEEK_SET);
return arcpos;
}
}
}
error(M_NOTARJ, name);
return 0;
}
static int
read_header(int first, FILE *fd, char *name)
{
ushort extheadersize, header_id;
header_id = fget_word(fd);
if (header_id != HEADER_ID)
{
if (first)
error(M_NOTARJ, name);
else
error(M_BADHEADR);
}
headersize = fget_word(fd);
if (headersize == 0)
return 0; /* end of archive */
if (headersize > HEADERSIZE_MAX)
error(M_BADHEADR);
crc = CRC_MASK;
fread_crc(header, headersize, fd);
header_crc = fget_crc(fd);
if ((crc ^ CRC_MASK) != header_crc)
error(M_HEADRCRC);
setup_get(header);
first_hdr_size = get_byte();
arj_nbr = get_byte();
arj_x_nbr = get_byte();
host_os = get_byte();
arj_flags = get_byte();
method = get_byte();
file_type = get_byte();
(void)get_byte();
time_stamp = get_longword();
compsize = get_longword();
origsize = get_longword();
file_crc = get_crc();
entry_pos = get_word();
file_mode = get_word();
host_data = get_word();
hdr_filename = (char *)&header[first_hdr_size];
strncopy(filename, hdr_filename, sizeof(filename));
if (host_os != OS)
strparity((uchar *)filename);
if ((arj_flags & PATHSYM_FLAG) != 0)
decode_path(filename);
hdr_comment = (char *)&header[first_hdr_size + strlen(hdr_filename) + 1];
strncopy(comment, hdr_comment, sizeof(comment));
if (host_os != OS)
strparity((uchar *)comment);
while ((extheadersize = fget_word(fd)) != 0)
fseek(fd, extheadersize + 2, SEEK_CUR);
return 1; /* success */
}
static void
skip(void)
{
fseek(arcfile, compsize, SEEK_CUR);
}
static void
unstore(void)
{
uint n;
long pos;
char *buffer;
buffer = (char *)malloc_msg(BUFFERSIZE);
pos = ftell(arcfile);
n = (uint)(BUFFERSIZE - (pos % BUFFERSIZE));
n = compsize > (long)n ? n : (uint)compsize;
while (compsize > 0)
{
if (fread(buffer, 1, n, arcfile) != n)
error(M_CANTREAD);
putc('.', stdout);
compsize -= n;
fwrite_txt_crc((uchar *)buffer, n);
n = compsize > BUFFERSIZE ? BUFFERSIZE : (uint)compsize;
}
free(buffer);
}
static int
check_flags(void)
{
if (arj_x_nbr > ARJ_X_VERSION)
{
printf(M_UNKNVERS, arj_x_nbr);
printf(M_SKIPPED, filename);
skip();
return -1;
}
if ((arj_flags & GARBLE_FLAG) != 0)
{
printf(M_ENCRYPT);
printf(M_SKIPPED, filename);
skip();
return -1;
}
if (method < 0 || method > MAXMETHOD || (method == 4 && arj_nbr == 1))
{
printf(M_UNKNMETH, method);
printf(M_SKIPPED, filename);
skip();
return -1;
}
if (file_type != BINARY_TYPE && file_type != TEXT_TYPE)
{
printf(M_UNKNTYPE, file_type);
printf(M_SKIPPED, filename);
skip();
return -1;
}
return 0;
}
static int
extract(void)
{
if (check_flags())
return 0;
if (no_output)
printf(M_TESTING, filename);
else
{
if (file_exists(filename))
{
printf(M_FEXISTS, filename);
printf(M_SKIPPED, filename);
skip();
return 0;
}
outfile = fopen(filename, writemode[file_type & 1]);
if (outfile == NULL)
{
printf(M_CANTOPEN, filename);
putchar('\n');
skip();
return 0;
}
printf(M_EXTRACT, filename);
if (host_os != OS && file_type == BINARY_TYPE)
printf(M_DIFFHOST);
}
printf(" ");
crc = CRC_MASK;
if (file_type == BINARY_TYPE || file_type == TEXT_TYPE)
{
if (method == 0)
unstore();
else if (method == 1 || method == 2 || method == 3)
decode();
else if (method == 4)
decode_f();
}
if (no_output == 0)
{
if (file_type == BINARY_TYPE || file_type == TEXT_TYPE)
fclose(outfile);
if (host_os == OS)
set_fmode(filename, file_mode);
set_ftime(filename, time_stamp);
}
if ((crc ^ CRC_MASK) == file_crc)
printf(M_CRCOK);
else
{
printf(M_CRCERROR);
error_count++;
}
return 1;
}
static void
execute_cmd(void)
{
int file_count;
char date_str[22];
first_hdr_pos = 0;
time_stamp = 0;
first_hdr_size = FIRST_HDR_SIZE;
arcfile = fopen_msg(arc_name, "rb");
printf(M_PROCARC, arc_name);
first_hdr_pos = find_header(arcfile, arc_name);
fseek(arcfile, first_hdr_pos, SEEK_SET);
if (!read_header(1, arcfile, arc_name))
error(M_BADCOMNT);
get_date_str(date_str, time_stamp);
printf(M_ARCDATE, date_str);
file_count = 0;
while (read_header(0, arcfile, arc_name))
{
if (extract())
file_count++;
}
printf(M_NBRFILES, file_count);
fclose(arcfile);
}
int
main(int argc, char *argv[])
{
int i, j, lastc;
printf(M_VERSION);
no_output = 0;
if (argc == 3 && strcmp(argv[2], "NUL") == 0)
{
no_output = 1;
}
else if (argc != 2)
{
printf(M_USAGE);
return EXIT_SUCCESS;
}
strncopy(arc_name, argv[1], FNAME_MAX);
case_path(arc_name);
i = strlen(arc_name);
j = parse_path(arc_name, NULL, NULL);
lastc = arc_name[i - 1];
if (lastc == ARJ_DOT)
arc_name[i - 1] = NULL_CHAR;
else if (strchr(&arc_name[j], ARJ_DOT) == NULL)
strcat(arc_name, M_SUFFIX);
make_crctable();
error_count = 0;
arcfile = NULL;
outfile = NULL;
execute_cmd();
if (error_count > 0)
error(M_ERRORCNT, error_count);
return EXIT_SUCCESS;
}
/* end UNARJ.C */
[DECODE.C follows]
/* DECODE.C, UNARJ, R JUNG, 04/05/91
/* Decode ARJ archive
/* Copyright (c) 1991 by Robert K Jung. All rights reserved.
/*
/* This code may be freely used in programs that are NOT archivers.
/*
/* Modification history:
/* Date Programmer Description of modification.
/* 04/05/91 R. Jung Rewrote code.
/*
*/
#include "unarj.h"
#include
#include
#define THRESHOLD 3
#define DDICSIZ 26624
#define MAXDICBIT 16
#define MATCHBIT 8
#define MAXMATCH 256 /* not more than UCHAR_MAX + 1 */
#define NC (UCHAR_MAX + MAXMATCH + 2 - THRESHOLD)
#define NP (MAXDICBIT + 1)
#define CBIT 9
#define NT (CODE_BIT + 3)
#define PBIT 5 /* smallest integer such that (1U << PBIT) > NP */
#define TBIT 5 /* smallest integer such that (1U << TBIT) > NT */
#if NT > NP
#define NPT NT
#else
#define NPT NP
#endif
#define CTABLESIZE 4096
#define STRTP 9
#define STOPP 13
#define STRTL 0
#define STOPL 7
/* Local variables */
static short getlen;
static short getbuf;
static ushort left[2 * NC - 1];
static ushort right[2 * NC - 1];
static uchar c_len[NC];
static uchar pt_len[NPT];
static ushort c_table[CTABLESIZE];
static ushort pt_table[256];
static ushort blocksize;
/* Huffman decode routines */
static void
make_table(int nchar, uchar bitlen[], int tablebits, ushort table[])
{
ushort count[17], weight[17], start[18], *p;
uint i, k, len, ch, jutbits, avail, nextcode, mask;
for (i = 1; i <= 16; i++)
count[i] = 0;
for (i = 0; (int)i < nchar; i++)
count[bitlen[i]]++;
start[1] = 0;
for (i = 1; i <= 16; i++)
start[i + 1] = start[i] + (count[i] << (16 - i));
if (start[17] != (ushort) (1U << 16))
error(M_BADTABLE, 0);
jutbits = 16 - tablebits;
for (i = 1; (int)i <= tablebits; i++)
{
start[i] >>= jutbits;
weight[i] = 1U << (tablebits - i);
}
while (i <= 16)
weight[i++] = 1U << (16 - i);
i = start[tablebits + 1] >> jutbits;
if (i != (ushort) (1U << 16))
{
k = 1U << tablebits;
while (i != k)
table[i++] = 0;
}
avail = nchar;
mask = 1U << (15 - tablebits);
for (ch = 0; (int)ch < nchar; ch++)
{
if ((len = bitlen[ch]) == 0)
continue;
nextcode = start[len] + weight[len];
if ((int)len <= tablebits)
{
for (i = start[len]; i < nextcode; i++)
table[i] = ch;
}
else
{
k = start[len];
p = &table[k >> jutbits];
i = len - tablebits;
while (i != 0)
{
if (*p == 0)
{
right[avail] = left[avail] = 0;
*p = avail++;
}
if (k & mask)
p = &right[*p];
else
p = &left[*p];
k <<= 1;
i--;
}
*p = ch;
}
start[len] = nextcode;
}
}
static void
read_pt_len(short nn, short nbit, short i_special)
{
short i, c, n;
ushort mask;
n = getbits(nbit);
if (n == 0)
{
c = getbits(nbit);
for (i = 0; i < nn; i++)
pt_len[i] = 0;
for (i = 0; i < 256; i++)
pt_table[i] = c;
}
else
{
i = 0;
while (i < n)
{
c = bitbuf >> (16 - 3);
if (c == 7)
{
mask = 1U << (16 - 4);
while (mask & bitbuf)
{
mask >>= 1;
c++;
}
}
fillbuf((c < 7) ? 3 : c - 3);
pt_len[i++] = (uchar)c;
if (i == i_special)
{
c = getbits(2);
while (--c >= 0)
pt_len[i++] = 0;
}
}
while (i < nn)
pt_len[i++] = 0;
make_table(nn, pt_len, 8, pt_table);
}
}
static void
read_c_len(void)
{
short i, c, n;
ushort mask;
n = getbits(CBIT);
if (n == 0)
{
c = getbits(CBIT);
for (i = 0; i < NC; i++)
c_len[i] = 0;
for (i = 0; i < CTABLESIZE; i++)
c_table[i] = c;
}
else
{
i = 0;
while (i < n)
{
c = pt_table[bitbuf >> (16 - 8)];
if (c >= NT)
{
mask = 1U << (16 - 9);
do
{
if (bitbuf & mask)
c = right[c];
else
c = left[c];
mask >>= 1;
} while (c >= NT);
}
fillbuf(pt_len[c]);
if (c <= 2)
{
if (c == 0)
c = 1;
else if (c == 1)
c = getbits(4) + 3;
else
c = getbits(CBIT) + 20;
while (--c >= 0)
c_len[i++] = 0;
}
else
c_len[i++] = (uchar)(c - 2);
}
while (i < NC)
c_len[i++] = 0;
make_table(NC, c_len, 12, c_table);
}
}
static ushort
decode_c(void)
{
ushort j, mask;
if (blocksize == 0)
{
blocksize = getbits(16);
read_pt_len(NT, TBIT, 3);
read_c_len();
read_pt_len(NP, PBIT, -1);
}
blocksize--;
j = c_table[bitbuf >> 4];
if (j >= NC)
{
mask = 1U << (16 - 1 - 12);
do
{
if (bitbuf & mask)
j = right[j];
else
j = left[j];
mask >>= 1;
} while (j >= NC);
}
fillbuf(c_len[j]);
return j;
}
static ushort
decode_p(void)
{
ushort j, mask;
j = pt_table[bitbuf >> (16 - 8)];
if (j >= NP)
{
mask = 1U << (16 - 1 - 8);
do
{
if (bitbuf & mask)
j = right[j];
else
j = left[j];
mask >>= 1;
} while (j >= NP);
}
fillbuf(pt_len[j]);
if (j != 0)
{
j--;
j = (1U << j) + getbits(j);
}
return j;
}
static void
decode_start(void)
{
blocksize = 0;
init_getbits();
}
void
decode(void)
{
short i;
short j;
short c;
short r;
uchar *text;
long count;
text = (uchar *)malloc_msg(DDICSIZ);
decode_start();
count = 0;
r = 0;
while (count < origsize)
{
if ((c = decode_c()) <= UCHAR_MAX)
{
text[r] = (uchar) c;
count++;
if (++r >= DDICSIZ)
{
r = 0;
putc('.', stdout);
fwrite_txt_crc(text, DDICSIZ);
}
}
else
{
j = c - (UCHAR_MAX + 1 - THRESHOLD);
count += j;
i = decode_p();
if ((i = r - i - 1) < 0)
i += DDICSIZ;
if (r > i && r < DDICSIZ - MAXMATCH - 1)
{
while (--j >= 0)
text[r++] = text[i++];
}
else
{
while (--j >= 0)
{
text[r] = text[i];
if (++r >= DDICSIZ)
{
r = 0;
putc('.', stdout);
fwrite_txt_crc(text, DDICSIZ);
}
if (++i >= DDICSIZ)
i = 0;
}
}
}
}
if (r != 0)
fwrite_txt_crc(text, r);
exit:
free(text);
}
/* Macros */
#define GETBIT(c) \
{ \
if (getlen <= 0) \
{ \
getbuf |= bitbuf >> getlen; \
fillbuf(CODE_BIT-getlen); \
getlen = CODE_BIT; \
} \
c = getbuf < 0; \
getbuf <<= 1; \
getlen--; \
}
#define GETBITS(c, len) \
{ \
if (getlen < (len)) \
{ \
getbuf |= bitbuf >> getlen; \
fillbuf(CODE_BIT-getlen); \
getlen = CODE_BIT; \
} \
c = (ushort)getbuf>>(CODE_BIT-(len));\
getbuf <<= (len); \
getlen -= (len); \
}
static short
decode_ptr(void)
{
short c;
short width;
short plus;
short pwr;
plus = 0;
pwr = 1 << (STRTP);
for (width = (STRTP); width < (STOPP) ; width++)
{
GETBIT(c);
if (c == 0)
break;
plus += pwr;
pwr <<= 1;
}
if (width != 0)
GETBITS(c, width);
c += plus;
return c;
}
static short
decode_len(void)
{
short c;
short width;
short plus;
short pwr;
plus = 0;
pwr = 1 << (STRTL);
for (width = (STRTL); width < (STOPL) ; width++)
{
GETBIT(c);
if (c == 0)
break;
plus += pwr;
pwr <<= 1;
}
if (width != 0)
GETBITS(c, width);
c += plus;
return c;
}
void
decode_f(void)
{
short i;
short j;
short c;
short r;
short pos;
long count;
uchar *text;
text = (uchar *)malloc_msg(DDICSIZ);
init_getbits();
getlen = getbuf = 0;
count = 0;
r = 0;
while (count < origsize)
{
c = decode_len();
if (c == 0)
{
GETBITS(c, CHAR_BIT);
text[r] = (uchar)c;
count++;
if (++r >= DDICSIZ)
{
r = 0;
putc('.', stdout);
fwrite_txt_crc(text, DDICSIZ);
}
}
else
{
j = c - 1 + THRESHOLD;
count += j;
pos = decode_ptr();
if ((i = r - pos - 1) < 0)
i += DDICSIZ;
while (j-- > 0)
{
text[r] = text[i];
if (++r >= DDICSIZ)
{
r = 0;
putc('.', stdout);
fwrite_txt_crc(text, DDICSIZ);
}
if (++i >= DDICSIZ)
i = 0;
}
}
}
if (r != 0)
fwrite_txt_crc(text, r);
exit:
free(text);
}
/* end DECODE.C */
Discuss this article in the forums
Date this article was posted to GameDev.net: 7/15/1999
(Note that this date does not necessarily correspond to the date the article was written)
See Also:
Compression Algorithms
© 1999-2011 Gamedev.net. All rights reserved. Terms of Use Privacy Policy
Comments? Questions? Feedback? Click here!
|