/* lzop.c --
This file is part of the lzop file compressor.
Copyright (C) 1996-2010 Markus Franz Xaver Johannes Oberhumer
All Rights Reserved.
lzop and the LZO library are free software; you can redistribute them
and/or modify them 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; see the file COPYING.
If not, write to the Free Software Foundation, Inc.,
59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
Markus F.X.J. Oberhumer
http://www.oberhumer.com/opensource/lzop/
*/
#include "conf.h"
#include "version.h"
/*************************************************************************
// options
**************************************************************************/
int opt_cmd = CMD_NONE;
int opt_method = 0;
int opt_level = 0;
int opt_filter = 0;
int opt_checksum = -1;
int opt_console = CON_INIT;
lzo_bool opt_crc32 = 0;
lzo_bool opt_decompress_safe = 1;
lzo_bool opt_file = 0;
int opt_force = 0;
lzo_bool opt_ignorewarn = 0;
lzo_bool opt_keep = 0;
int opt_num_threads = 1; /* NOT YET IMPLEMENTED */
#ifdef MAINT
int opt_noheader = 0;
#endif
lzo_bool opt_nowarn = 0;
const char *opt_ls_flags = "";
int opt_name = -1;
const char *opt_output_name = NULL;
const char *opt_output_path = NULL;
lzo_bool opt_optimize = 0;
lzo_bool opt_path = 0;
lzo_bool opt_restore_mode = 1;
lzo_bool opt_restore_time = 1;
lzo_bool opt_shortname = 0;
int opt_stdin = 0;
lzo_bool opt_stdout = 0;
char opt_suffix[1+SUFFIX_MAX+1] = { 0 };
lzo_bool opt_unlink = 0;
int opt_verbose = 1;
static int done_output_name = 0;
static int done_output_path = 0;
static int done_suffix = 0;
/* invocation options */
enum {
PGM_LZOP,
PGM_UNLZOP,
PGM_OCAT
};
int opt_pgm = PGM_LZOP;
const char *argv0 = "";
const char *progname = "";
MODE_T u_mask = 0700;
time_t current_time;
FILE *con_term = NULL;
static int num_files = -1;
static int exit_code = EXIT_OK;
static file_t fi;
static file_t fo;
static unsigned long total_d_files = 0;
static unsigned long total_c_files = 0;
static lzop_ulong_t total_d_len = 0;
static lzop_ulong_t total_c_len = 0;
/* some statistics */
static lzop_ulong_t total_bytes_written = 0;
static lzop_ulong_t total_bytes_read = 0;
/*************************************************************************
// exit handlers
**************************************************************************/
static void do_exit(void)
{
static lzo_bool in_exit = 0;
if (in_exit)
exit(exit_code);
in_exit = 1;
fflush(con_term);
fflush(stderr);
exit(exit_code);
}
#define EXIT_FATAL 3
static lzo_bool set_eec(int ec, int *eec)
{
if (ec == EXIT_FATAL)
{
*eec = EXIT_ERROR;
return 1;
}
else if (ec < 0 || ec == EXIT_ERROR)
{
*eec = EXIT_ERROR;
}
else if (ec == EXIT_WARN)
{
if (!opt_ignorewarn)
if (*eec == EXIT_OK)
*eec = ec;
}
else if (ec == EXIT_OK)
{
/* do nothing */
}
else
{
assert(0);
}
return 0;
}
static lzo_bool set_ec(int ec)
{
return set_eec(ec,&exit_code);
}
void e_exit(int ec)
{
(void) set_ec(ec);
do_exit();
}
void e_usage(void)
{
usage();
e_exit(EXIT_USAGE);
}
void e_memory(void)
{
head();
fflush(con_term);
fprintf(stderr,"%s: out of memory\n", argv0);
e_exit(EXIT_MEMORY);
}
void e_method(int m)
{
fflush(con_term);
#if (UINT_MAX < LZO_0xffffffffL)
/* 16-bit DOS/Windows */
fprintf(stderr,"%s: 16-bit versions are not officially supported\n", argv0);
#endif
fprintf(stderr,"%s: illegal method option -- %c\n", argv0, m & 255);
e_usage();
}
#if defined(OPTIONS_VAR)
void e_envopt(const char *n)
{
fflush(con_term);
if (n)
fprintf(stderr,"%s: invalid string '%s' in environment variable '%s'\n",
argv0, n, OPTIONS_VAR);
else
fprintf(stderr,"%s: illegal option in environment variable '%s'\n",
argv0, OPTIONS_VAR);
e_exit(EXIT_USAGE);
}
#endif
RETSIGTYPE __acc_cdecl_sighandler e_sighandler(acc_signo_t signo)
{
UNUSED(signo);
e_exit(EXIT_FATAL);
}
/*************************************************************************
// error handlers
**************************************************************************/
static void do_error(file_t *ft, const char *n, const char *msg, int ec, int err)
{
const char *fn;
const char *errmsg;
fflush(con_term);
if (!(ec == EXIT_WARN && (opt_nowarn || opt_ignorewarn || opt_verbose == 0)))
{
fn = ft && ft->name[0] ? ft->name : UNKNOWN_NAME;
fprintf(stderr, "%s%s: %s: ", n, progname, fn);
if (ec == EXIT_WARN)
fprintf(stderr, "warning: ");
if (err != 0)
{
errmsg = strerror(err);
if (msg && msg[0])
fprintf(stderr, "%s: %s\n", msg, errmsg);
else
fprintf(stderr, "%s\n", errmsg);
}
else
fprintf(stderr, "%s\n", msg);
fflush(stderr);
}
if (set_ec(ec))
do_exit();
}
static const char *err_nl = "";
void set_err_nl(lzo_bool x)
{
err_nl = x ? "\n" : "";
}
void fatal(file_t *ft, const char *msg)
{
do_error(ft,err_nl,msg,EXIT_FATAL,0);
}
void error(file_t *ft, const char *msg)
{
do_error(ft,err_nl,msg,EXIT_ERROR,0);
}
void warn(file_t *ft, const char *msg)
{
do_error(ft,err_nl,msg,EXIT_WARN,0);
}
void info(file_t *ft, const char *msg)
{
do_error(ft,err_nl,msg,EXIT_OK,0);
}
void p_fatal(file_t *ft, const char *msg)
{
do_error(ft,err_nl,msg,EXIT_FATAL,errno);
}
void p_error(file_t *ft, const char *msg)
{
do_error(ft,err_nl,msg,EXIT_ERROR,errno);
}
void p_warn(file_t *ft, const char *msg)
{
do_error(ft,err_nl,msg,EXIT_WARN,errno);
}
void read_error(file_t *ft)
{
const char *fn = ft && ft->name[0] ? ft->name : UNKNOWN_NAME;
const char *errmsg = "unexpected end of file";
if (errno != 0)
errmsg = strerror(errno);
fflush(con_term);
fprintf(stderr, "%s%s: %s: %s\n", err_nl, progname, errmsg, fn);
e_exit(EXIT_FATAL);
}
void write_error(file_t *ft)
{
const char *fn = ft && ft->name[0] ? ft->name : UNKNOWN_NAME;
const char *errmsg = "write error";
if (errno != 0)
errmsg = strerror(errno);
fflush(con_term);
fprintf(stderr, "%s%s: %s: %s\n", err_nl, progname, errmsg, fn);
e_exit(EXIT_FATAL);
}
/*************************************************************************
// file_t
**************************************************************************/
void f_reset(file_t *ft)
{
ft->opt_name = opt_name;
ft->part = 0;
ft->bytes_read = 0;
ft->bytes_written = 0;
ft->warn_multipart = 0;
ft->warn_unknown_suffix = 0;
}
void f_init(void)
{
memset(&fi,0,sizeof(file_t));
memset(&fo,0,sizeof(file_t));
fi.fd = -1;
fo.fd = -1;
#if defined(USE_FOPEN)
fi.file = NULL;
fo.file = NULL;
#endif
f_reset(&fi);
f_reset(&fo);
}
int f_open(file_t *ft, lzo_bool r)
{
assert(ft->name[0]);
ft->fd = -1;
#if defined(O_BINARY)
ft->open_flags |= O_BINARY;
#endif
#if (ACC_OS_WIN32 || ACC_OS_WIN64) && defined(_O_SEQUENTIAL)
ft->open_flags |= _O_SEQUENTIAL;
#endif
#if defined(USE_FOPEN)
ft->file = NULL;
if (r)
ft->file = fopen(ft->name,"rb");
else if (ft->open_flags & O_EXCL)
{
if (file_exists(ft->name))
errno = EEXIST;
else
ft->file = fopen(ft->name,"wb");
}
else
ft->file = fopen(ft->name,"wb");
if (ft->file != NULL)
{
ft->fd = fileno(ft->file);
assert(ft->fd >= 0);
}
#else
if (r)
ft->fd = open(ft->name, ft->open_flags, 0);
else
{
#if defined(O_EXCL_BROKEN)
if ((ft->open_flags & O_EXCL) && file_exists(ft->name))
errno = EEXIST;
else
#endif
ft->fd = open(ft->name, ft->open_flags, ft->st.st_mode);
}
#endif
if (ft->fd >= 0 && (ft->fd == STDIN_FILENO || ft->fd == STDOUT_FILENO || ft->fd == STDERR_FILENO))
{
fatal(ft,"sanity check failed: f_open()");
ft->fd = -1;
}
return ft->fd;
}
int f_close(file_t *ft)
{
int r;
if (ft->fd < 0)
return 0;
if (ft->fd == STDIN_FILENO || ft->fd == STDOUT_FILENO || ft->fd == STDERR_FILENO)
return 0;
#if defined(USE_FOPEN)
assert(ft->file != NULL);
r = fclose(ft->file);
ft->file = NULL;
#else
r = close(ft->fd);
#endif
ft->fd = -1;
return r;
}
/*************************************************************************
// read and write
// handles partial pipe writes and interrupted system calls
**************************************************************************/
lzo_int read_buf(file_t *ft, lzo_voidp buffer, lzo_int cnt)
{
lzo_int n;
long l;
assert(cnt >= 0 && cnt < LONG_MAX);
l = acc_safe_hread(ft->fd, buffer, (long) cnt);
n = (lzo_int) l;
assert(n >= 0); assert(n == l);
ft->bytes_read += n;
total_bytes_read += n;
return n;
}
void write_buf(file_t *ft, const lzo_voidp buffer, lzo_int cnt)
{
lzo_int n;
long l;
assert(cnt >= 0 && cnt < LONG_MAX);
if (ft->fd < 0)
return;
l = acc_safe_hwrite(ft->fd, buffer, (long) cnt);
n = (lzo_int) l;
assert(n >= 0); assert(n == l);
ft->bytes_written += n;
total_bytes_written += n;
if (n != cnt)
write_error(ft);
}
/*************************************************************************
// misc IO
**************************************************************************/
static unsigned get_be16(const unsigned char *b)
{
unsigned v;
v = (unsigned) b[1] << 0;
v |= (unsigned) b[0] << 8;
return v;
}
static void set_be16(unsigned char *b, unsigned v)
{
b[1] = (unsigned char) (v >> 0);
b[0] = (unsigned char) (v >> 8);
}
static lzo_uint32 get_be32(const unsigned char *b)
{
lzo_uint32 v;
v = (lzo_uint32) b[3] << 0;
v |= (lzo_uint32) b[2] << 8;
v |= (lzo_uint32) b[1] << 16;
v |= (lzo_uint32) b[0] << 24;
return v;
}
static void set_be32(unsigned char *b, lzo_uint32 v)
{
b[3] = (unsigned char) (v >> 0);
b[2] = (unsigned char) (v >> 8);
b[1] = (unsigned char) (v >> 16);
b[0] = (unsigned char) (v >> 24);
}
#if 0 /* NOT USED */
static void write8(file_t *ft, int v)
{
unsigned char b = (unsigned char) v;
write_buf(ft,&b,1);
}
#endif
void read32(file_t *ft, lzo_uint32 *v)
{
unsigned char b[4];
if (read_buf(ft,b,4) != 4)
read_error(ft);
*v = get_be32(b);
}
void write32(file_t *ft, lzo_uint32 v)
{
unsigned char b[4];
set_be32(b,v);
write_buf(ft,b,4);
}
static int f_read8(file_t *ft, unsigned char *b)
{
unsigned char bb;
if (read_buf(ft,&bb,1) != 1)
read_error(ft);
ft->f_adler32 = lzo_adler32(ft->f_adler32,&bb,1);
ft->f_crc32 = lzo_crc32(ft->f_crc32,&bb,1);
if (b)
*b = bb;
return bb;
}
static void f_write8(file_t *ft, int v)
{
unsigned char b = (unsigned char) v;
write_buf(ft,&b,1);
ft->f_adler32 = lzo_adler32(ft->f_adler32,&b,1);
ft->f_crc32 = lzo_crc32(ft->f_crc32,&b,1);
}
static void f_read16(file_t *ft, unsigned *v)
{
unsigned char b[2];
if (read_buf(ft,b,2) != 2)
read_error(ft);
ft->f_adler32 = lzo_adler32(ft->f_adler32,b,2);
ft->f_crc32 = lzo_crc32(ft->f_crc32,b,2);
*v = get_be16(b);
}
static void f_write16(file_t *ft, unsigned v)
{
unsigned char b[2];
set_be16(b,v);
write_buf(ft,b,2);
ft->f_adler32 = lzo_adler32(ft->f_adler32,b,2);
ft->f_crc32 = lzo_crc32(ft->f_crc32,b,2);
}
static void f_read32(file_t *ft, lzo_uint32 *v)
{
unsigned char b[4];
if (read_buf(ft,b,4) != 4)
read_error(ft);
ft->f_adler32 = lzo_adler32(ft->f_adler32,b,4);
ft->f_crc32 = lzo_crc32(ft->f_crc32,b,4);
*v = get_be32(b);
}
static void f_write32(file_t *ft, lzo_uint32 v)
{
unsigned char b[4];
set_be32(b,v);
write_buf(ft,b,4);
ft->f_adler32 = lzo_adler32(ft->f_adler32,b,4);
ft->f_crc32 = lzo_crc32(ft->f_crc32,b,4);
}
static void f_write(file_t *ft, const lzo_voidp buf, lzo_int cnt)
{
if (cnt > 0)
{
write_buf(ft,buf,cnt);
ft->f_adler32 = lzo_adler32(ft->f_adler32,(const lzo_bytep)buf,cnt);
ft->f_crc32 = lzo_crc32(ft->f_crc32,(const lzo_bytep)buf,cnt);
}
}
static lzo_int f_read(file_t *ft, lzo_voidp buf, lzo_int cnt)
{
cnt = read_buf(ft,buf,cnt);
if (cnt > 0)
{
ft->f_adler32 = lzo_adler32(ft->f_adler32,(const lzo_bytep)buf,cnt);
ft->f_crc32 = lzo_crc32(ft->f_crc32,(const lzo_bytep)buf,cnt);
}
return cnt;
}
/***********************************************************************
// lzop file signature
************************************************************************/
/*
* The first nine bytes of a lzop file always contain the following values:
*
* 0 1 2 3 4 5 6 7 8
* --- --- --- --- --- --- --- --- ---
* (hex) 89 4c 5a 4f 00 0d 0a 1a 0a
* (decimal) 137 76 90 79 0 13 10 26 10
* (C notation - ASCII) \211 L Z O \0 \r \n \032 \n
*/
static const unsigned char lzop_magic[9] =
{ 0x89, 0x4c, 0x5a, 0x4f, 0x00, 0x0d, 0x0a, 0x1a, 0x0a };
static const char * const header_error[] = {
"[0]",
"not a " PACKAGE " file", /* 1 */
"header corrupted (checksum error)", /* 2 */
"header corrupted", /* 3 */
"header corrupted (DOS -> UNIX conversion ?)", /* 4 */
"header corrupted (DOS -> Mac conversion ?)", /* 5 */
"header corrupted (UNIX -> Mac conversion ?)", /* 6 */
"header corrupted (Mac -> UNIX conversion ?)", /* 7 */
"header corrupted (UNIX -> DOS conversion ?)", /* 8 */
"header corrupted (end of line conversion ?)", /* 9 */
"header corrupted (DOS EOF conversion ?)", /* 10 */
"header corrupted (transmitted through a 7-bit channel ?)", /* 11 */
"header corrupted (transmitted in text mode ?)", /* 12 */
"unknown header flags -- get a newer version of " PACKAGE, /* 13 */
"unknown compression method -- get a newer version of " PACKAGE, /* 14 */
"unknown compression level -- get a newer version of " PACKAGE, /* 15 */
"you need a newer version of " PACKAGE, /* 16 */
"compression method not supported -- recompile " PACKAGE, /* 17 */
"decompression method not supported -- recompile " PACKAGE, /* 18 */
NULL
};
static int check_magic(const unsigned char *magic)
{
const unsigned char *m;
if (memcmp(magic,lzop_magic,sizeof(lzop_magic)) == 0)
return 0;
/* We have a bad magic signature. Try to figure what possibly
* could have gone wrong. */
/* look at bytes 1-3: "LZO" in hex and local text format */
if (memcmp(&magic[1],&lzop_magic[1],3) != 0 &&
memcmp(&magic[1],"LZO",3) != 0)
return 1;
/* look at byte 4 */
if (magic[4] != lzop_magic[4])
return 1;
/* look at bytes 5-8 */
m = &magic[5];
if (memcmp(m,"\012\012\032",3) == 0)
return 7;
if (memcmp(m,"\012\012",2) == 0)
return 4;
if (memcmp(m,"\012\032",2) == 0)
return 4;
if (memcmp(m,"\015\012\012",3) == 0)
return 10;
if (memcmp(m,"\015\012\032\012",4) == 0)
return 9;
if (memcmp(m,"\015\012\032\015",4) == 0)
return 8;
if (memcmp(m,"\015\015\012\032",4) == 0)
return 8;
if (memcmp(m,"\015\015\032",3) == 0)
return 6;
if (memcmp(m,"\015\032",2) == 0)
return 5;
if (memcmp(m,&lzop_magic[5],4) != 0)
return 12;
/* look at byte 0 */
if (magic[0] == (unsigned char) (lzop_magic[0] & 0x7f))
return 11;
if (magic[0] != lzop_magic[0])
return 12;
return 3;
}
/*************************************************************************
// lzop file header
**************************************************************************/
void init_compress_header(header_t *h, const file_t *fip, const file_t *fop)
{
assert(opt_method > 0);
assert(opt_level > 0);
assert(fip->st.st_mode == 0 || S_ISREG(fip->st.st_mode));
memset(h,0,sizeof(header_t));
h->version = LZOP_VERSION & 0xffff;
h->version_needed_to_extract = opt_filter ? 0x0950: 0x0940;
h->lib_version = lzo_version() & 0xffff;
h->method = (unsigned char) opt_method;
h->level = (unsigned char) opt_level;
h->filter = opt_filter;
h->flags = 0;
h->flags |= F_OS & F_OS_MASK;
h->flags |= F_CS & F_CS_MASK;
if (opt_filter)
h->flags |= F_H_FILTER;
if (fip->fd == STDIN_FILENO)
h->flags |= F_STDIN;
if (fop->fd == STDOUT_FILENO)
h->flags |= F_STDOUT;
if (!opt_file && num_files > 1)
h->flags |= F_MULTIPART;
#ifdef OPT_NAME_DEFAULT
h->flags |= F_NAME_DEFAULT;
#endif
#ifdef DOSISH
h->flags |= F_DOSISH;
#endif
if (opt_crc32)
{
h->flags |= F_H_CRC32;
if (h->version_needed_to_extract < 0x1001)
h->version_needed_to_extract = 0x1001;
}
h->mode = fix_mode_for_header(fip->st.st_mode);
if (fip->st.st_mtime)
{
h->mtime_low = (lzo_uint32) (fip->st.st_mtime);
h->mtime_high = (lzo_uint32) (fip->st.st_mtime >> 16 >> 16);
if ((lzo_int32) h->mtime_high < 0)
h->mtime_high = 0;
}
if (fip->name[0] && fip->fd != STDIN_FILENO)
{
int r = 0;
if (opt_path)
{
char newname[255+1];
r = fn_cleanpath(fip->name, newname, sizeof(newname), 0);
if (r > 0 && newname[0] && strlen(newname) <= 255)
{
strcpy(h->name, newname);
h->flags |= F_H_PATH;
if (h->version_needed_to_extract < 0x1001)
h->version_needed_to_extract = 0x1001;
}
else
r = 0;
}
if (r == 0)
{
const char *n = fn_basename(fip->name);
if (n[0] && strlen(n) <= 255)
strcpy(h->name, n);
}
}
}
void write_header(file_t *ft, const header_t *h)
{
size_t l;
#ifdef MAINT
/* undocumented option '--no-header'. just for testing. */
if (opt_noheader > 0)
{
switch (opt_noheader)
{
case 1:
write32(ft,h->flags);
break;
case 2:
write32(ft,h->flags);
write8(ft,h->method);
write8(ft,h->level);
break;
default:
/* write no header at all */
break;
}
return;
}
#endif
write_buf(ft,lzop_magic,sizeof(lzop_magic));
ft->f_adler32 = ADLER32_INIT_VALUE;
ft->f_crc32 = CRC32_INIT_VALUE;
f_write16(ft,h->version);
f_write16(ft,h->lib_version);
f_write16(ft,h->version_needed_to_extract);
f_write8(ft,h->method);
f_write8(ft,h->level);
f_write32(ft,h->flags);
if (h->flags & F_H_FILTER)
f_write32(ft,h->filter);
f_write32(ft,h->mode);
f_write32(ft,h->mtime_low);
f_write32(ft,h->mtime_high);
l = strlen(h->name);
assert(l <= 255);
f_write8(ft,(int)l);
if (l > 0)
f_write(ft,h->name,(int)l);
if (h->flags & F_H_CRC32)
f_write32(ft,ft->f_crc32);
else
f_write32(ft,ft->f_adler32);
}
static int read_header(file_t *ft, header_t *h)
{
int r;
int l;
lzo_uint32 checksum;
memset(h,0,sizeof(header_t));
h->version_needed_to_extract = 0x0900; /* first public lzop version */
h->level = 0;
h->method_name = "unknown";
ft->f_adler32 = ADLER32_INIT_VALUE;
ft->f_crc32 = CRC32_INIT_VALUE;
f_read16(ft,&h->version);
if (h->version < 0x0900)
return 3;
f_read16(ft,&h->lib_version);
if (h->version >= 0x0940)
{
f_read16(ft,&h->version_needed_to_extract);
if (h->version_needed_to_extract > LZOP_VERSION)
return 16;
if (h->version_needed_to_extract < 0x0900)
return 3;
}
f_read8(ft,&h->method);
if (h->version >= 0x0940)
f_read8(ft,&h->level);
f_read32(ft,&h->flags);
if (h->flags & F_H_FILTER)
f_read32(ft,&h->filter);
f_read32(ft,&h->mode);
#if 1
if (h->flags & F_STDIN) /* do not use mode from stdin compression */
h->mode = 0;
#endif
f_read32(ft,&h->mtime_low);
if (h->version >= 0x0940)
f_read32(ft,&h->mtime_high);
if (h->version < 0x0120) {
if (h->mtime_low == 0xffffffffUL)
h->mtime_low = 0;
h->mtime_high = 0;
}
l = f_read8(ft,NULL);
if (l > 0) {
char name[255+1];
if (f_read(ft,name,l) != l)
read_error(ft);
name[l] = 0;
if (fn_cleanpath(name, h->name, 255+1, 1|2) < 0)
h->name[0] = 0;
}
checksum = (h->flags & F_H_CRC32) ? ft->f_crc32 : ft->f_adler32;
f_read32(ft,&h->header_checksum);
if (h->header_checksum != checksum)
return 2;
if (h->method <= 0)
return 14;
r = x_get_method(h);
if (r != 0)
return r;
/* check reserved flags */
if (h->flags & F_RESERVED)
return (opt_force >= 2) ? -13 : 13;
/* skip extra field [not used yet] */
if (h->flags & F_H_EXTRA_FIELD)
{
lzo_uint32 k;
/* note: the checksum also covers the length */
ft->f_adler32 = ADLER32_INIT_VALUE;
ft->f_crc32 = CRC32_INIT_VALUE;
f_read32(ft,&h->extra_field_len);
for (k = 0; k < h->extra_field_len; k++)
(void) f_read8(ft,NULL);
checksum = (h->flags & F_H_CRC32) ? ft->f_crc32 : ft->f_adler32;
f_read32(ft,&h->extra_field_checksum);
if (h->extra_field_checksum != checksum)
return 3;
if (opt_verbose >= 2)
info(ft,"ignoring extra field");
}
return 0;
}
/* return 0 for valid magic, -1 for EOF, or positive value for error */
static int p_magic(file_t *ft)
{
int r;
lzo_int l;
unsigned char magic[sizeof(lzop_magic)];
l = read_buf(ft,magic,sizeof(magic));
if (ft->part > 0 && l <= 0)
return -1;
if (l == (lzo_int) sizeof(magic))
r = check_magic(magic);
else
r = 1;
assert(r >= 0);
if (ft->part > 0 && r == 1)
{
#if 1
/* gzip: check for trailing zero bytes */
unsigned char b;
while (--l >= 0)
if (magic[(int)l] != '\0')
goto garbage;
while (read_buf(ft,&b,1) == 1)
if (b != '\0')
goto garbage;
if (opt_verbose >= 2)
warn(ft,"ignoring trailing zero bytes in " PACKAGE " file");
return -1;
garbage:
#endif
warn(ft,"ignoring trailing garbage in " PACKAGE " file");
return -1;
}
if (r != 0)
{
assert(r > 0 && r <= 18);
error(ft,header_error[r]);
}
return r;
}
static lzo_bool p_header(file_t *ft, header_t *h)
{
int r;
r = read_header(ft,h);
if (r == 0)
return 1;
if (r < 0)
{
r = -r;
assert(r > 0 && r <= 18);
error(ft,header_error[r]);
return 1;
}
else
{
assert(r > 0 && r <= 18);
error(ft,header_error[r]);
return 0;
}
}
/*************************************************************************
// test
**************************************************************************/
void do_test(const header_t *h, lzop_ulong_t d_len, lzop_ulong_t c_len)
{
total_d_files++;
total_c_len += c_len;
total_d_len += d_len;
UNUSED(h);
}
void do_test_total(void)
{
FILE *f;
if ((total_c_files < 2 && total_d_files < 2) || opt_verbose < 2)
return;
f = stderr;
fprintf(f,"%lu file%s successfully tested", total_c_files,
total_c_files == 1 ? " was" : "s were");
if (total_c_files != total_d_files)
fprintf(f," [containing %lu files]", total_d_files);
fprintf(f,"\n");
fflush(f);
}
/*************************************************************************
// list a file
**************************************************************************/
static unsigned long get_ratio(lzop_ulong_t d_len, lzop_ulong_t c_len)
{
unsigned long n1 = 1000L * 1000L;
unsigned long n2 = 1;
const lzop_ulong_t umax = ~((lzop_ulong_t)0);
if (d_len <= 0)
return c_len <= 0 ? 0ul : n1;
while (n1 > 1 && c_len > (umax / n1))
{
n1 /= 10;
n2 *= 10;
}
return (unsigned long) ((c_len * n1) / (d_len / n2));
}
static void pr_size(FILE *f, lzop_ulong_t a, lzop_ulong_t b, int flags)
{
unsigned long ratio, r1, r2, al, bl;
ratio = (flags & 1) ? get_ratio(a, b) : get_ratio(b, a);
ratio += 500; /* for rounding */
r1 = ratio / 10000;
r2 = (ratio % 10000) / 1000;
#if (SIZEOF_LONG >= 8) || !defined(acc_int64l_t)
al = (unsigned long) a;
bl = (unsigned long) b;
fprintf(f,"%9lu %9lu %3lu.%01lu%%", al, bl, r1, r2);
#else
al = (unsigned long) (a % 1000000000ul);
bl = (unsigned long) (b % 1000000000ul);
if (a == al && b == bl)
{
fprintf(f,"%9lu %9lu %3lu.%01lu%%", al, bl, r1, r2);
}
else if (a == al)
{
unsigned long bh = (unsigned long) (b / 1000000000ul);
fprintf(f,"%9lu %lu%09lu %3lu.%01lu%%", al, bh, bl, r1, r2);
}
else if (b == bl)
{
unsigned long ah = (unsigned long) (a / 1000000000ul);
fprintf(f,"%lu%09lu %9lu %3lu.%01lu%%", ah, al, bl, r1, r2);
}
else
{
unsigned long ah = (unsigned long) (a / 1000000000ul);
unsigned long bh = (unsigned long) (b / 1000000000ul);
fprintf(f,"%lu%09lu %lu%09lu %3lu.%01lu%%", ah, al, bh, bl, r1, r2);
}
#endif
}
static char *modestr(lzo_uint32 mode)
{
static char s[10+1];
mode_string(fix_mode_for_ls(mode),s);
s[0] = '-';
s[10] = 0;
return s;
}
void do_list(const header_t *h, lzop_ulong_t d_len, lzop_ulong_t c_len)
{
FILE *f;
char s[40];
time_t t;
f = stdout;
s[0] = 0;
t = get_mtime(h);
if (total_d_files == 0 && opt_verbose > 0)
{
if (opt_verbose >= 3)
((void)0);
else if (opt_verbose >= 2)
{
fprintf(f,"Method Length Packed Ratio ");
fprintf(f," Date Time Name\n");
fprintf(f,"------ ------ ------ ----- ");
fprintf(f," ---- ---- ----\n");
}
else
{
fprintf(f,"%-11s ", "method");
fprintf(f,"compressed uncompr. ratio");
fprintf(f," uncompressed_name\n");
}
fflush(f);
}
if (opt_verbose >= 3)
{
fprintf(f,"%-10s", modestr(h->mode));
if (t)
time2str(s,sizeof(s),&t);
fprintf(f," %-19s",s);
fprintf(f," %-20s",fi.name);
if (fo.name[0])
fprintf(f," %s", fo.name);
}
else if (opt_verbose >= 2)
{
fprintf(f,"%-11s ", h->method_name);
pr_size(f, d_len, c_len, 1);
if (t)
time2str(s,sizeof(s),&t);
s[16] = 0; /* cut off seconds */
fprintf(f," %-16s",s);
if (fo.name[0])
fprintf(f," %s", fo.name);
}
else
{
fprintf(f,"%-11s ", h->method_name);
pr_size(f, c_len, d_len, 0);
if (fo.name[0])
fprintf(f," %s", fo.name);
}
fprintf(f,"\n");
fflush(f);
total_d_files++;
total_c_len += c_len;
total_d_len += d_len;
}
void do_list_total(void)
{
FILE *f;
if (total_d_files < 2 || opt_verbose == 0)
return;
if (opt_verbose >= 3)
return;
f = stdout;
if (opt_verbose >= 2)
{
fprintf(f," ------- ------- ----- ");
fprintf(f," ----\n");
fprintf(f,"%-11s ", "");
pr_size(f, total_d_len, total_c_len, 1);
fprintf(f," %-16s", "");
fprintf(f," %lu files\n", total_d_files);
}
else
{
fprintf(f,"%-11s ", "");
pr_size(f, total_c_len, total_d_len, 0);
fprintf(f," (totals -- %lu files)\n", total_d_files);
}
fflush(f);
}
/*************************************************************************
// list a file similar to 'ls -ln'
**************************************************************************/
void do_ls(const header_t *h, lzop_ulong_t d_len, lzop_ulong_t c_len)
{
FILE *f;
char s[40];
time_t t;
const char *name = fo.name[0] ? fo.name : UNKNOWN_NAME;
f = stdout;
t = get_mtime(h);
if (t == 0)
time(&t);
fprintf(f,"%-10s 1", modestr(h->mode));
if (opt_stdin)
fprintf(f," %-8s", "user");
else
fprintf(f," %-8ld", (long) fi.st.st_uid);
if (!strchr(opt_ls_flags,'G'))
{
if (opt_stdin)
fprintf(f," %-8s", "group");
else
fprintf(f," %-8ld", (long) fi.st.st_gid);
}
#if (SIZEOF_LONG >= 8) || !defined(acc_int64l_t)
fprintf(f," %8lu", (unsigned long) d_len);
#else
{
unsigned long d0, d1, d2;
d0 = (unsigned long) (d_len % 100000000ul);
if (d0 == d_len)
fprintf(f," %8lu", d0);
else
{
d1 = (unsigned long) ((d_len / 100000000ul) % 100000000ul);
d2 = (unsigned long) ((d_len / 100000000ul) / 100000000ul);
if (d2 != 0)
fprintf(f,"%lu%08lu%08lu", d2, d1, d0);
else
fprintf(f,"%lu%08lu", d1, d0);
}
}
#endif
time2ls(s, sizeof(s), &t);
fprintf(f," %-12s",s);
if (strchr(opt_ls_flags,'Q'))
fprintf(f," \"%s\"", name);
else
fprintf(f," %s", name);
if (strchr(opt_ls_flags,'F'))
if (h->mode & 0111)
fprintf(f,"*");
fprintf(f,"\n");
fflush(f);
UNUSED(c_len);
}
/*************************************************************************
// header info
**************************************************************************/
static void print_version(FILE *f, unsigned v)
{
fprintf(f,"%1x.%03x", (v >> 12) & 0xf, v & 0xfff);
}
static void print_os(FILE *f, lzo_uint32 flags)
{
flags = (flags & F_OS_MASK) >> F_OS_SHIFT;
fprintf(f,"%2ld", (long) flags);
}
void do_info(const header_t *h, lzop_ulong_t d_len, lzop_ulong_t c_len)
{
int v = opt_verbose;
FILE *f;
f = stdout;
opt_verbose = 2;
++total_d_files; /* do not print the list-header */
do_list(h,d_len,c_len);
--total_d_files;
opt_verbose = v;
fprintf(f," ");
print_version(f,h->version);
fprintf(f," ");
print_version(f,h->lib_version);
fprintf(f," ");
print_version(f,h->version_needed_to_extract);
fprintf(f," Fl: 0x%08lx", (long) h->flags);
fprintf(f," Mo: 0%011lo", (long) h->mode);
fprintf(f," Me: %d/%d", h->method, h->level);
fprintf(f," OS: ");
print_os(f,h->flags);
if (h->filter)
fprintf(f," Fi: %3ld", (long) h->filter);
fprintf(f,"\n");
}
/*************************************************************************
// determine name of output file
**************************************************************************/
static lzo_bool can_restore_name(file_t *ft, const header_t *h)
{
if (h->name[0] == 0 || ft->opt_name == 0)
return 0;
else if (ft->opt_name > 0)
return 1;
#ifdef OPT_NAME_DEFAULT
#if 1
else
return 1;
#else
/* restore the name by default only when created on such a system */
else if ((h->flags & F_NAME_DEFAULT))
return 1;
else
return 0;
#endif
#else
/* do not restore the name by default */
else
return 0;
#endif /* OPT_NAME_DEFAULT */
}
static lzo_bool oname_error(void)
{
if (opt_force >= 2 || opt_cmd == CMD_TEST)
{
warn(&fi,"can't determine name of output file -- using default");
return 1;
}
if (opt_name != 1)
error(&fi,"can't determine name of output file (try option '-N')");
else
error(&fi,"can't determine name of output file (use option '-o')");
strcpy(fo.name,UNKNOWN_NAME);
return 0;
}
static lzo_bool p_set_oname(const header_t *h)
{
char *base;
char *ext;
int suff;
size_t l;
const char *err_name;
const char *s;
size_t sl;
fo.name[0] = 0;
if (opt_output_name)
{
/* name given on command line; perform no additional checks */
strcpy(fo.name,opt_output_name);
return 1;
}
assert(!opt_stdout);
assert(opt_file);
#if defined(NRVP)
err_name = (opt_cmd == CMD_COMPRESS) ? "nrvp.nrv" : "nrvp.raw";
s = opt_suffix[0] ? opt_suffix : ".nrv";
#else
err_name = (opt_cmd == CMD_COMPRESS) ? "lzop.lzo" : "lzop.raw";
s = opt_suffix[0] ? opt_suffix : ".lzo";
#endif
sl = strlen(s);
if (opt_output_path)
{
strcpy(fo.name,opt_output_path);
fn_addslash(fo.name,1);
if (!opt_stdin)
strcat(fo.name,fn_basename(fi.name));
}
else if (!opt_stdin)
strcpy(fo.name,fi.name);
l = strlen(fo.name);
if (l >= PATH_MAX)
{
error(&fo,"name too long (use option '-o')");
return 0;
}
base = fo.name + fn_baseindex(fo.name);
suff = fn_has_suffix(base);
ext = strchr(base,'.');
if (opt_cmd == CMD_COMPRESS)
{
assert(!opt_stdin);
assert(base[0]);
#if defined(DOSISH)
if (suff == SUFF_TAR)
{
#if defined(NRVP)
strcpy(ext, opt_suffix[0] ? opt_suffix : ".tnv");
#else
strcpy(ext, opt_suffix[0] ? opt_suffix : ".tzo");
#endif
}
else
#endif
if (opt_shortname && ext)
strcpy(ext,s);
else
strcat(fo.name,s);
}
else
{
lzo_bool u = 0;
if (can_restore_name(&fi,h))
{
if (opt_path)
strcpy(base,h->name);
else
strcpy(base,fn_basename(h->name));
}
else if (opt_stdin)
{
if (!oname_error())
return 0;
strcpy(base,err_name);
}
else if (suff == SUFF_LZO)
fo.name[l-4] = 0;
else if (suff == SUFF_LZOP)
fo.name[l-5] = 0;
else if (suff == SUFF_NRV)
fo.name[l-4] = 0;
else if (suff == SUFF_TZO)
strcpy(&fo.name[l-4],".tar");
else if (suff == SUFF_USER)
fo.name[l-sl] = 0;
else
{
u = 1;
if (opt_shortname && ext)
strcpy(ext,".raw");
else
strcat(fo.name,".raw");
}
if (u && opt_cmd == CMD_DECOMPRESS && !opt_stdin)
{
#if 1
if (!(opt_force >= 2))
{
if (fi.warn_unknown_suffix == 0)
error(&fi,"unknown suffix -- ignored");
fi.warn_unknown_suffix = 1;
return 0;
}
#else
/* gzip: '--force' doesn't override these checks */
if (fi.warn_unknown_suffix == 0)
error(&fi,"unknown suffix -- ignored");
fi.warn_unknown_suffix = 1;
return 0;
#endif
}
}
if (strlen(fo.name) >= PATH_MAX)
{
error(&fo,"name too long (use option '-o')");
return 0;
}
#if defined(DOSISH)
s = maybe_rename_file(fo.name);
if (s == NULL)
{
if (!oname_error())
return 0;
strcpy(base,err_name);
}
else if (s != fo.name)
{
if (strcmp(s,fo.name) != 0)
{
warn(&fo,"renaming output file to match OS conventions");
strcpy(fo.name,s);
}
}
#endif
UNUSED(ext);
return 1;
}
/*************************************************************************
// stdin/stdout
**************************************************************************/
static lzo_bool check_stdin(file_t *ft)
{
if (!opt_force && acc_isatty(STDIN_FILENO))
{
strcpy(ft->name,STDIN_NAME);
if (opt_cmd == CMD_COMPRESS)
fatal(ft,"uncompressed data not read from a terminal");
else
fatal(ft,"compressed data not read from a terminal");
return 0;
}
return 1;
}
static lzo_bool check_stdout(file_t *ft)
{
if (!(opt_cmd == CMD_COMPRESS || opt_cmd == CMD_DECOMPRESS))
return 1;
if (!opt_force && acc_isatty(STDOUT_FILENO))
{
strcpy(ft->name,STDOUT_NAME);
if (opt_cmd == CMD_COMPRESS)
fatal(ft,"compressed data not written to a terminal");
else
fatal(ft,"uncompressed data not written to a terminal");
return 0;
}
return 1;
}
static lzo_bool open_stdin(file_t *ft)
{
static lzo_bool setmode_done = 0;
assert(ft->fd == -1);
f_reset(ft);
strcpy(ft->name,STDIN_NAME);
ft->fd = STDIN_FILENO;
#if !defined(NO_SETMODE)
if (!setmode_done)
{
if (acc_set_binmode(ft->fd, 1) == -1)
{
p_fatal(ft,"acc_set_binmode(stdin) failed");
return 0;
}
}
#endif
setmode_done = 1;
ft->st.st_mtime = time(NULL);
#if 1 && defined(HAVE_FSTAT)
{
struct stat st;
if (fstat(ft->fd, &st) == 0 && S_ISREG(st.st_mode))
ft->st = st;
}
#endif
ft->st.st_atime = fix_time(ft->st.st_atime);
ft->st.st_mtime = fix_time(ft->st.st_mtime);
return 1;
}
static lzo_bool open_stdout(file_t *ft)
{
static lzo_bool setmode_done = 0;
assert(ft->fd == -1);
f_reset(ft);
strcpy(ft->name,STDOUT_NAME);
if (!(opt_cmd == CMD_COMPRESS || opt_cmd == CMD_DECOMPRESS))
{
ft->fd = -2; /* special file-handle for dummy output */
return 1;
}
ft->fd = STDOUT_FILENO;
#if !defined(NO_SETMODE)
if (!setmode_done)
{
if (acc_set_binmode(ft->fd, 1) == -1)
{
p_fatal(ft,"acc_set_binmode(stdout) failed");
return 0;
}
}
#endif
setmode_done = 1;
return 1;
}
/*************************************************************************
// open input file
**************************************************************************/
lzo_bool p_open_fi(const char *name)
{
int r, saved_errno;
#if defined(HAVE_LSTAT) && defined(S_ISLNK)
int r2;
#endif
if (fi.fd != -1)
return 1;
f_reset(&fi);
/* prepare file name */
assert(name != NULL);
if (strlen(name) >= PATH_MAX)
{
if (strlen(name) >= sizeof(fi.name))
strcpy(fi.name,UNKNOWN_NAME);
else
strcpy(fi.name,name);
error(&fi,"name too long");
return 0;
}
strcpy(fi.name,name);
fn_strlwr(fi.name);
if (opt_cmd == CMD_COMPRESS)
{
int suff = fn_has_suffix(fi.name);
#if 1
if (opt_stdout || opt_output_name)
suff = SUFF_NONE; /* do not warn */
#endif
#if 1
if (opt_force >= 2)
suff = SUFF_NONE; /* do not warn */
#else
/* gzip: '--force' doesn't override these checks */
#endif
if (suff == SUFF_LZO)
{
warn(&fi,"already has .lzo suffix -- unchanged");
return 0;
}
else if (suff == SUFF_LZOP)
{
warn(&fi,"already has .lzop suffix -- unchanged");
return 0;
}
else if (suff == SUFF_NRV)
{
warn(&fi,"already has .nrv suffix -- unchanged");
return 0;
}
else if (suff == SUFF_TZO)
{
warn(&fi,"already has .tzo suffix -- unchanged");
return 0;
}
else if (suff == SUFF_USER)
{
warn(&fi,"already has user suffix -- unchanged");
return 0;
}
}
/* open file */
errno = 0;
r = stat(fi.name, &fi.st);
saved_errno = errno;
if (r != 0)
memset(&fi.st, 0, sizeof(fi.st));
#if defined(HAVE_LSTAT) && defined(S_ISLNK)
r2 = lstat(fi.name, &fi.lst);
if (r2 != 0)
memset(&fi.lst, 0, sizeof(fi.lst));
if (r2 == 0 && S_ISLNK(fi.lst.st_mode))
{
if (r != 0)
{
errno = saved_errno;
#if 0
p_error(&fi,"can't open input file -- dangling symlink");
#else
do_error(&fi,err_nl,"can't open input file: Dangling symlink",EXIT_ERROR,0);
#endif
return 0;
}
}
#endif
if (r == 0 && !S_ISREG(fi.st.st_mode))
{
warn(&fi,"not a regular file -- skipped");
return 0;
}
fi.open_flags = O_RDONLY;
f_open(&fi,1);
#if 0 && defined(__DJGPP__)
/* try again without LFN */
if (fi.fd < 0 && errno == ENOENT && _USE_LFN)
{
if (!(_crt0_startup_flags & _CRT0_FLAG_NO_LFN))
{
int k = _crt0_startup_flags;
_crt0_startup_flags |= _CRT0_FLAG_NO_LFN;
r = stat(fi.name, &fi.st);
saved_errno = errno;
_crt0_startup_flags = k;
if (r == 0 && !S_ISREG(fi.st.st_mode))
{
warn(&fi,"not a regular file -- skipped");
return 0;
}
f_open(&fi,1);
}
}
#endif
if (fi.fd < 0)
{
p_error(&fi,"can't open input file");
return 0;
}
if (r != 0)
{
errno = saved_errno;
p_error(&fi,"can't stat input file");
(void) f_close(&fi);
return 0;
}
fi.st.st_atime = fix_time(fi.st.st_atime);
fi.st.st_mtime = fix_time(fi.st.st_mtime);
return 1;
}
/*************************************************************************
// open output file
**************************************************************************/
lzo_bool p_open_fo(const header_t *h)
{
if (fo.fd != -1)
return 1;
f_reset(&fo);
if (!p_set_oname(h))
return 0;
fn_strlwr(fo.name);
if (!(opt_cmd == CMD_COMPRESS || opt_cmd == CMD_DECOMPRESS))
{
fo.fd = opt_output_name ? -2 : -1;
return 1;
}
if (fn_is_same_file(fi.name,fo.name))
{
if (opt_cmd == CMD_COMPRESS)
error(&fi,"can't compress to same file");
else
error(&fi,"can't decompress to same file");
return 0;
}
fo.open_flags = O_CREAT | O_WRONLY;
if (opt_force)
fo.open_flags |= O_TRUNC;
else
fo.open_flags |= O_EXCL;
#if defined(__MINT__)
fo.open_flags |= O_TRUNC | O_DENYRW;
#endif
fo.st.st_mode = fix_mode_for_open(fi.st.st_mode);
if (opt_cmd == CMD_DECOMPRESS && opt_path && (h->flags & F_H_PATH))
{
/* create missing directories */
char *name;
int n = 0;
name = fo.name;
while (name[n])
{
while (name[n] && name[n] != '/')
n++;
if (name[n] == '/')
{
name[n] = 0;
(void) acc_mkdir(name, 0777);
name[n] = DIR_SEP[0];
n++;
}
}
}
f_open(&fo,0);
if (fo.fd < 0)
{
if ((fo.open_flags & O_EXCL) && errno == EEXIST)
error(&fo,"already exists; not overwritten");
else
p_error(&fo,"can't open output file");
return 0;
}
return 1;
}
/*************************************************************************
// close files
**************************************************************************/
static lzo_bool p_close(int i, int o)
{
int r = 1;
if (i && f_close(&fi) != 0)
{
p_error(&fi,"can't close input file");
r = 0;
}
if (o && f_close(&fo) != 0)
{
p_error(&fo,"can't close output file");
r = 0;
}
return r;
}
/*************************************************************************
// compress
**************************************************************************/
static void copy_perms(void)
{
#if defined(HAVE_UTIME)
/* copy the time stamp */
struct utimbuf u;
u.actime = fi.st.st_atime;
u.modtime = fi.st.st_mtime;
if (utime(fo.name,&u) != 0)
p_warn(&fo,"can't copy file time");
#endif
#if defined(HAVE_CHMOD)
/* copy the protection mode */
fo.st.st_mode = fi.st.st_mode;
if (chmod(fo.name, fo.st.st_mode) != 0)
p_warn(&fo,"can't copy file mode");
#endif
#if defined(HAVE_CHOWN)
/* copy the ownership */
if (chown(fo.name, fi.st.st_uid, fi.st.st_gid) != 0) {
/* ignore */
}
#endif
}
static lzo_bool do_compress(const char *name, lzo_bool handle_perms)
{
lzo_bool ok = 1;
header_t header;
if (!p_open_fi(name))
return 0;
if (!p_open_fo(NULL))
{
if (opt_output_name || opt_stdout)
e_exit(EXIT_ERROR);
return 0;
}
ok = x_compress(&fi,&fo,&header);
if (!ok)
return 0;
if (handle_perms)
{
if (!p_close(1,1))
return 0;
copy_perms();
}
return ok;
}
/*************************************************************************
// decompress
**************************************************************************/
static void restore_perms(const header_t *h)
{
#if defined(HAVE_UTIME)
/* restore or copy the time stamp */
struct utimbuf u;
if (opt_restore_time && (h->mtime_low || h->mtime_high))
{
u.actime = u.modtime = get_mtime(h);
if (u.actime)
if (utime(fo.name,&u) != 0)
p_warn(&fo,"can't restore file time");
}
else if (fi.st.st_atime && fi.st.st_mtime)
{
u.actime = fi.st.st_atime;
u.modtime = fi.st.st_mtime;
if (utime(fo.name,&u) != 0)
p_warn(&fo,"can't copy file time");
}
#endif
#if defined(HAVE_CHMOD)
/* restore or copy the protection mode */
if (opt_restore_mode && h->mode)
{
fo.st.st_mode = fix_mode_for_chmod(h->mode);
if (chmod(fo.name, fo.st.st_mode) != 0)
p_warn(&fo,"can't restore file mode");
}
else if (fi.st.st_mode > 0)
{
fo.st.st_mode = fi.st.st_mode;
if (chmod(fo.name, fo.st.st_mode) != 0)
p_warn(&fo,"can't copy file mode");
}
#endif
#if defined(HAVE_CHOWN)
/* copy the ownership */
if (!opt_stdin)
if (chown(fo.name, fi.st.st_uid, fi.st.st_gid) != 0) {
/* ignore */
}
#endif
UNUSED(h);
}
static lzo_bool warn_multipart(file_t *ft, const header_t *h)
{
if (!((ft->part > 0) || (h->flags & F_MULTIPART)))
return 1;
if (opt_stdin && opt_stdout && opt_cmd == CMD_TEST && can_restore_name(ft,h))
return 1;
if (opt_stdout || opt_output_name)
{
if (!ft->warn_multipart)
warn(&fi,"this is a multipart archive (try option '-N')");
ft->warn_multipart = 1;
}
else if (opt_file && !can_restore_name(ft,h))
{
ft->opt_name = 1;
if (opt_cmd == CMD_TEST)
{
if (!ft->warn_multipart)
warn(&fi,"this is a multipart archive (try option '-N')");
ft->warn_multipart = 1;
}
else if (can_restore_name(ft,h))
{
if (!ft->warn_multipart)
warn(&fi,"multipart archive -- restoring file names");
ft->warn_multipart = 1;
}
else
{
error(&fi,"multipart archive, but no filename stored (use option '-o')");
return 0;
}
}
return 1;
}
static lzo_bool do_decompress(const char *name, lzo_bool handle_perms)
{
lzo_bool ok = 1;
lzo_bool unlink_ok = 1;
lzo_bool skip = 0;
header_t header;
int r;
if (!p_open_fi(name))
return 0;
for ( ; ok; fi.part++)
{
r = p_magic(&fi);
if (r > 0)
return 0;
if (fi.part == 0)
total_c_files++;
if (fi.part > 0 && (opt_file || (r < 0 && handle_perms)))
{
if (!p_close(0,1))
return 0;
if (!skip && handle_perms && (opt_file || (r < 0 && fi.part == 1)))
restore_perms(&header);
}
if (r < 0)
{
assert(fi.part > 0);
break;
}
if (!p_header(&fi,&header))
{
/* use '--info -f -f' to try to list a corrupted header */
if (opt_cmd == CMD_INFO && opt_force >= 2)
{
(void) x_get_method(&header);
do_info(&header,0,0);
}
return 0;
}
#if 0
/* debug */
do_info(&header,0,0);
#endif
if (!warn_multipart(&fi,&header))
return 0;
skip = 0;
ok = p_open_fo(&header);
if (!ok)
{
unlink_ok = 0;
if (opt_output_name || opt_stdout)
e_exit(EXIT_ERROR);
if (opt_cmd != CMD_TEST)
skip = 1;
}
ok = x_decompress(&fi,&fo,&header,skip);
}
return ok && unlink_ok;
}
/*************************************************************************
// process files
**************************************************************************/
static lzo_bool do_one_file(const char *name, lzo_bool handle_perms)
{
lzo_bool ok;
if (opt_cmd == CMD_COMPRESS)
ok = do_compress(name,handle_perms);
else
ok = do_decompress(name,handle_perms);
if (!p_close(1,0))
ok = 0;
if (opt_file && !p_close(0,1))
ok = 0;
if (ok && opt_unlink)
{
#if defined(HAVE_CHMOD)
(void) chmod(fi.name, 0777);
#endif
if (unlink(fi.name) != 0)
p_warn(&fi,"can't unlink file");
}
if (fi.fd == -1)
fi.name[0] = 0;
if (fo.fd == -1)
fo.name[0] = 0;
return ok;
}
static void do_files(int i, int argc, char *argv[])
{
lzo_bool handle_perms;
if (opt_cmd == CMD_COMPRESS)
handle_perms = !opt_stdin;
else if (opt_cmd == CMD_DECOMPRESS)
handle_perms = 1;
else
handle_perms = 0;
if (opt_stdin)
{
assert(opt_stdout || opt_output_name || opt_output_path);
assert(i == argc);
assert(num_files == 0);
if (!check_stdin(&fi) || !open_stdin(&fi))
return;
}
if (opt_stdout)
{
assert(!opt_output_name);
if (!check_stdout(&fo) || !open_stdout(&fo))
return;
handle_perms = 0;
}
if (opt_output_name)
{
assert(!opt_stdout);
handle_perms &= (num_files == 1);
}
if (opt_stdin)
do_one_file(NULL,handle_perms);
else
{
for ( ; i < argc; i++)
do_one_file(argv[i],handle_perms);
}
(void) p_close(1,1);
if (opt_cmd == CMD_LIST)
do_list_total();
if (opt_cmd == CMD_TEST)
do_test_total();
}
/*************************************************************************
// check options
**************************************************************************/
static void check_not_both(lzo_bool e1, lzo_bool e2, int c1, int c2)
{
if (e1 && e2)
{
fprintf(stderr,"%s: ",argv0);
fprintf(stderr,"cannot use both '-%c' and '-%c'\n", c1, c2);
e_usage();
}
}
void check_options(int i, int argc)
{
assert(i <= argc);
if (opt_keep)
opt_unlink = 0;
if (!(opt_cmd == CMD_COMPRESS || opt_cmd == CMD_DECOMPRESS))
opt_unlink = 0;
if (opt_stdin == OPT_STDIN_GUESSED && i != argc)
opt_stdin = 0;
if (opt_stdin)
{
opt_unlink = 0;
#if 0
/* gzip: always use stdout */
opt_stdout = 1;
#else
if (!opt_output_name && !opt_output_path)
opt_stdout = 1;
#endif
}
if (opt_stdout)
{
check_not_both(1, opt_output_name != NULL, 'c', 'o');
check_not_both(1, opt_output_path != NULL, 'c', 'p');
check_not_both(1, opt_suffix[0] != 0, 'c', 'S');
opt_output_name = NULL;
opt_output_path = NULL;
opt_suffix[0] = 0;
if (opt_unlink && !opt_force)
{
fprintf(stderr,"%s: both '-c' and '-U' given (use '-f' to force)\n",argv0);
e_usage();
}
}
if (opt_output_name)
{
check_not_both(1, opt_output_path != NULL, 'o', 'p');
check_not_both(1, opt_suffix[0] != 0, 'o', 'S');
opt_output_path = NULL;
opt_suffix[0] = 0;
}
/* check number of remaining args */
if (opt_stdin)
{
if (opt_cmd == CMD_COMPRESS && opt_output_path)
{
fprintf(stderr,"%s: cannot use '-p' when compressing stdin\n",argv0);
e_usage();
}
/* No more args allowed */
if (i != argc)
{
fprintf(stderr,"%s: no filename allowed when reading from stdin\n",argv0);
e_usage();
}
}
else
{
if (i == argc)
{
fprintf(stderr,"%s: nothing to do !\n",argv0);
e_usage();
}
if (opt_stdout || opt_output_name)
{
#if 1
/* Allow multiple files */
if (i + 1 != argc)
{
opt_name = 1;
}
#else
/* Exactly one input file */
if (i + 1 != argc)
{
fprintf(stderr,"%s: only one file allowed\n",argv0);
e_usage();
}
#endif
}
}
opt_file = !opt_stdout && !opt_output_name;
}
/*************************************************************************
// misc
**************************************************************************/
void e_help(void)
{
if (opt_pgm == PGM_LZOP)
help();
else if (opt_pgm == PGM_UNLZOP)
help();
else if (opt_pgm == PGM_OCAT)
{
if (opt_stdin)
check_stdin(&fi);
if (opt_stdout)
check_stdout(&fo);
usage();
}
else
help();
e_exit(EXIT_USAGE);
}
void set_term(FILE *f)
{
if (f)
con_term = f;
else
con_term = acc_isatty(STDOUT_FILENO) ? stdout : stderr;
}
void set_cmd(int cmd)
{
if (cmd == CMD_COMPRESS && (opt_pgm == PGM_UNLZOP || opt_pgm == PGM_OCAT))
return;
#if 0
if (opt_cmd != CMD_NONE && cmd != opt_cmd)
{
fprintf(stderr,"%s: multiple commands given\n",argv0);
e_usage();
}
opt_cmd = cmd;
#else
/* gzip: commands have a certain priority */
if (cmd > opt_cmd)
opt_cmd = cmd;
#endif
}
lzo_bool set_method(int m, int l)
{
if (x_set_method(m,l) != 0)
return 0;
set_cmd(CMD_COMPRESS);
return 1;
}
void set_output_name(const char *n, lzo_bool allow_m)
{
#if 1
if (done_output_name > 0)
{
fprintf(stderr,"%s: option '-o' more than once given\n",argv0);
e_usage();
}
#endif
if (!n || !n[0] || (!allow_m && n[0] == '-'))
{
fprintf(stderr,"%s: missing output name\n",argv0);
e_usage();
}
if (strlen(n) >= PATH_MAX)
{
fprintf(stderr,"%s: output name too long\n",argv0);
e_usage();
}
opt_output_name = n;
done_output_name++;
}
void set_output_path(const char *n, lzo_bool allow_m)
{
int r;
struct stat st;
file_t f;
#if 1
if (done_output_path > 0)
{
fprintf(stderr,"%s: option '-p' more than once given\n",argv0);
e_usage();
}
#endif
if (!n || (!allow_m && n[0] == '-'))
{
fprintf(stderr,"%s: missing path\n",argv0);
e_usage();
}
if (strlen(n) >= PATH_MAX)
{
fprintf(stderr,"%s: path too long\n",argv0);
e_usage();
}
if (n[0])
{
r = stat(n, &st);
if (r != 0)
{
strcpy(f.name,n);
p_fatal(&f,"invalid path");
}
#if defined(S_ISDIR)
if (!S_ISDIR(st.st_mode))
{
strcpy(f.name,n);
fatal(&f,"invalid path - must be a directory");
}
#endif
}
#if defined(HAVE_ACCESS) && defined(W_OK)
{
const char *p = n[0] ? n : ".";
if (access(p,W_OK) != 0)
{
strcpy(f.name,p);
p_fatal(&f,"can't write to path");
}
}
#endif
opt_output_path = n;
done_output_path++;
}
lzo_bool set_suffix(const char *n)
{
size_t l;
const char *p;
static const char * const invalid_suffixes[] =
{ "ace", "arc", "arj", "bz", "bz2", "gz", "lha", "lzh",
#if !defined(NRVP)
"nrv", "tnv",
#endif
"rar", "raw", "sz", "tar", "taz", "tbz", "tgz", "tsz",
"upx", "Z", "zip", "zoo", NULL };
const char * const *is = invalid_suffixes;
#if 1
if (done_suffix > 0)
{
fprintf(stderr,"%s: option '-S' more than once given\n",argv0);
e_usage();
return 0;
}
#endif
while (n && *n == '.')
n++;
if (!n || *n == 0 || *n == '-')
return 0;
#if 1 || defined(DOSISH)
if (strchr(n,'.'))
return 0;
#endif
for (p = n; *p; p++)
if (strchr("+*?=/\\ \t\n\r\a", *p))
return 0;
for ( ; *is; is++)
if (strcasecmp(n,*is) == 0)
return 0;
l = strlen(n);
if (l + 1 > SUFFIX_MAX || (opt_shortname && l > 3))
{
fprintf(stderr,"%s: suffix '%s' is too long\n",argv0,n);
e_usage();
return 0;
}
opt_suffix[0] = '.';
strcpy(opt_suffix + 1, n);
done_suffix++;
return 1;
}
/*************************************************************************
// get options
**************************************************************************/
static
char* prepare_shortopts(char *buf, const char *n,
const struct acc_getopt_longopt_t *longopts)
{
char *o = buf;
for ( ; n && *n; n++)
if (*n != ' ')
*o++ = *n;
*o = 0;
for ( ; longopts && longopts->name; longopts++)
{
int v = longopts->val;
if (v > 0 && v < 256 && strchr(buf,v) == NULL)
{
*o++ = (char) v;
if (longopts->has_arg >= 1)
*o++ = ':';
if (longopts->has_arg >= 2)
*o++ = ':';
*o = 0;
}
}
return buf;
}
static int do_option(acc_getopt_p g, int optc)
{
#define mfx_optarg g->optarg
int i = 0;
int m = -1;
switch (optc)
{
case 'c':
opt_stdout = 1;
break;
case 'C':
opt_checksum = (opt_checksum >= 1) ? opt_checksum + 1 : 1;
opt_decompress_safe = 1;
break;
case 'd':
set_cmd(CMD_DECOMPRESS);
break;
case 'f':
opt_force++;
break;
case 'F':
opt_checksum = 0;
opt_decompress_safe = 0;
break;
case 'h':
case 'H':
case '?':
set_cmd(CMD_HELP);
break;
case 'h'+256:
/* according to GNU standards */
set_term(stdout);
opt_console = CON_NONE;
help();
e_exit(EXIT_OK);
break;
case 'i':
case 'i'+256:
set_cmd(CMD_INFO);
break;
case 'I':
set_cmd(CMD_SYSINFO);
break;
case 'k':
opt_keep = 1;
break;
case 'l':
set_cmd(CMD_LIST);
break;
case 'L':
set_cmd(CMD_LICENSE);
break;
case 'n':
opt_name = 0;
opt_path = 0;
break;
case 'N':
opt_name = 1;
break;
case 'o':
set_output_name(mfx_optarg,1);
break;
case 'p':
case 'p'+256:
if (mfx_optarg && mfx_optarg[0])
set_output_path(mfx_optarg,0);
else if (optc == 'p')
set_output_path("",0);
else
set_output_path(NULL,0);
break;
case 'P':
opt_path = 1;
opt_name = 1;
break;
case 'q':
opt_verbose = 0;
break;
case 'S':
if (!set_suffix(mfx_optarg))
{
fprintf(stderr,"%s: invalid suffix '%s'\n",argv0,mfx_optarg);
e_usage();
}
break;
case 't':
set_cmd(CMD_TEST);
break;
case 'T':
if (!(mfx_optarg && isdigit(mfx_optarg[0])))
{
fprintf(stderr,"%s: invalid '--threads=' args: '%s'\n",argv0,mfx_optarg);
e_usage();
}
opt_num_threads = atoi(mfx_optarg);
if (opt_num_threads < 1 || opt_num_threads > MAX_NUM_THREADS)
{
fprintf(stderr,"%s: invalid number of threads: %d\n",argv0,opt_num_threads);
e_usage();
}
#if !defined(WITH_THREADS)
opt_num_threads = 1;
#endif
break;
case 'U':
opt_unlink = 1;
break;
case 'v':
opt_verbose = (opt_verbose < 2) ? 2 : opt_verbose + 1;
break;
case 'V':
set_cmd(CMD_VERSION);
break;
case 'V'+256:
/* according to GNU standards */
set_term(stdout);
opt_console = CON_NONE;
fprintf(stdout,"lzop %s\n",LZOP_VERSION_STRING);
fprintf(stdout,"LZO library %s\n",lzo_version_string());
fprintf(stdout,"Copyright (C) 1996-2010 Markus Franz Xaver Johannes Oberhumer\n");
e_exit(EXIT_OK);
break;
case 'x':
set_cmd(CMD_DECOMPRESS);
opt_name = 1;
opt_path = 1;
opt_restore_mode = 1;
opt_restore_time = 1;
if (!opt_output_name && !opt_output_path)
{
set_output_path("",0);
--done_output_path;
}
opt_unlink = 0;
break;
case 'Z'+256:
set_cmd(CMD_LS);
if (mfx_optarg && mfx_optarg[0])
{
opt_ls_flags = mfx_optarg;
for (i = 0; opt_ls_flags[i]; i++)
if (!strchr("FGQ",opt_ls_flags[i]))
{
fprintf(stderr,"%s: invalid '--ls' flags: '%s'\n",argv0,mfx_optarg);
e_usage();
}
}
break;
#ifdef MAINT
case 520:
opt_noheader++;
break;
#endif
case 522:
opt_stdin = 0;
break;
case 523:
opt_restore_mode = 0;
break;
case 524:
opt_restore_time = 0;
break;
case 525:
opt_nowarn = 1;
break;
case 526:
opt_ignorewarn = 1;
break;
case 527:
opt_crc32 = 1;
break;
case 512:
opt_console = CON_NONE;
break;
case 513:
opt_console = CON_ANSI_MONO;
break;
case 514:
opt_console = CON_ANSI_COLOR;
break;
case 515:
set_cmd(CMD_INTRO);
break;
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
if (!set_method(0,optc - '0'))
e_method(optc);
break;
#if defined(WITH_NRV)
case 811:
if (m < 0) m = M_NRV1A;
/* fallthrough */
case 812:
if (m < 0) m = M_NRV1B;
/* fallthrough */
case 813:
if (m < 0) m = M_NRV2A;
/* fallthrough */
case 814:
if (m < 0) m = M_NRV2B;
i = 1;
if (mfx_optarg && isdigit(mfx_optarg[0]) && !mfx_optarg[1])
i = mfx_optarg[0] - '0';
if (!set_method(m,i))
e_usage();
break;
#endif
#if defined(WITH_ZLIB)
case 801:
i = 6;
if (mfx_optarg && mfx_optarg[0] && !mfx_optarg[1])
i = mfx_optarg[0] - '0';
if (!set_method(M_ZLIB,i))
e_usage();
break;
#endif
case 521:
if (!(mfx_optarg && isdigit(mfx_optarg[0])))
{
fprintf(stderr,"%s: invalid '--filter=' args: '%s'\n",argv0,mfx_optarg);
e_usage();
}
if (strcmp(mfx_optarg,"0") == 0)
{
opt_filter = 0;
break;
}
opt_filter = atoi(mfx_optarg);
if (opt_filter < 1 || opt_filter > 16)
{
fprintf(stderr,"%s: invalid filter: %d\n",argv0,opt_filter);
e_usage();
}
break;
case '\0':
return -1;
case ':':
return -2;
default:
fprintf(stderr,"%s: internal error in getopt (%d)\n",argv0,optc);
return -3;
}
UNUSED(i);
UNUSED(m);
return 0;
#undef mfx_optarg
}
static void handle_opterr(acc_getopt_p g, const char *f, void *v)
{
struct A { va_list ap; };
struct A *a = (struct A *) v;
fprintf( stderr, "%s: ", g->progname);
if (a)
vfprintf(stderr, f, a->ap);
else
fprintf( stderr, "UNKNOWN GETOPT ERROR");
fprintf( stderr, "\n");
}
static int get_options(int argc, char **argv)
{
static const struct acc_getopt_longopt_t longopts[] =
{
{"best", 0, 0, '9'}, /* compress better */
{"decompress", 0, 0, 'd'}, /* decompress */
{"fast", 0, 0, '1'}, /* compress faster */
{"help", 0, 0, 'h'+256}, /* give help */
{"info", 0, 0, 'i'+256},
{"license", 0, 0, 'L'}, /* display software license */
{"list", 0, 0, 'l'}, /* list .lzo file contents */
{"ls", 2, 0, 'Z'+256}, /* list .lzo file contents */
{"sysinfo", 0, 0, 'I'},
{"test", 0, 0, 't'}, /* test compressed file integrity */
{"uncompress", 0, 0, 'd'}, /* decompress */
{"version", 0, 0, 'V'+256}, /* display version number */
#if defined(WITH_NRV)
{"nrv1a", 0x22, 0, 811},
{"nrv2a", 0x22, 0, 813},
{"nrv2b", 0x22, 0, 814},
#endif
#if defined(WITH_ZLIB)
{"zlib", 0x22, 0, 801},
#endif
{"checksum", 0, 0, 'C'},
{"crc32", 0x10, 0, 527}, /* use a crc32 checksum instead of adler32 */
{"delete", 0, 0, 'U'},
{"extract", 0, 0, 'x'},
{"filter", 1, 0, 521},
{"force", 0, 0, 'f'}, /* force overwrite of output file */
{"ignore-warn",0, 0, 526}, /* ignore any warnings */
{"keep", 0, 0, 'k'},
{"name", 0, 0, 'N'}, /* restore original name */
{"no-checksum",0, 0, 'F'},
#ifdef MAINT
{"no-header", 0, 0, 520},
#endif
{"no-mode", 0, 0, 523}, /* don't restore original mode */
{"no-name", 0, 0, 'n'}, /* don't restore original name */
{"no-stdin", 0, 0, 522},
{"no-time", 0, 0, 524}, /* don't restore original time */
{"no-warn", 0, 0, 525}, /* do not display any warnings */
{"output", 1, 0, 'o'},
{"path", 1, 0, 'p'+256},
{"quiet", 0, 0, 'q'}, /* quiet mode */
{"silent", 0, 0, 'q'}, /* quiet mode */
{"stdout", 0, 0, 'c'}, /* write output on standard output */
{"suffix", 1, 0, 'S'}, /* use given suffix instead of .lzo */
{"threads", 0x21, 0, 'T'}, /* number of threads */
{"to-stdout", 0, 0, 'c'}, /* write output on standard output */
{"unlink", 0, 0, 'U'},
{"verbose", 0, 0, 'v'}, /* verbose mode */
{"no-color", 0, 0, 512},
{"mono", 0, 0, 513},
{"color", 0, 0, 514},
{"intro", 0, 0, 515},
{ 0, 0, 0, 0 }
};
acc_getopt_t mfx_getopt;
int optc;
int i;
char shortopts[128];
prepare_shortopts(shortopts, "123456789hH?PVp::", longopts),
acc_getopt_init(&mfx_getopt, 1, argc, argv);
mfx_getopt.progname = argv0;
mfx_getopt.opterr = handle_opterr;
while ((optc = acc_getopt(&mfx_getopt, shortopts, longopts, NULL)) >= 0)
{
if (do_option(&mfx_getopt, optc) != 0)
e_usage();
}
/* accept "-" as synonym for stdin */
for (i = mfx_getopt.optind; i < argc; i++)
if (strcmp(argv[i], "-") == 0)
opt_stdin = OPT_STDIN_REQUESTED;
for (i = mfx_getopt.optind; i < argc; i++)
if (strcmp(argv[i], "-") != 0)
break;
return i;
}
#if defined(OPTIONS_VAR)
static void get_envoptions(int argc, char **argv)
{
/* only some options are allowed in the environment variable */
static const struct acc_getopt_longopt_t longopts[] =
{
{"best", 0, 0, '9'}, /* compress better */
{"checksum", 0, 0, 'C'},
{"crc32", 0x10, 0, 527}, /* use a crc32 checksum instead of adler32 */
{"delete", 0, 0, 'U'},
{"fast", 0, 0, '1'}, /* compress faster */
{"ignore-warn",0, 0, 526}, /* ignore any warnings */
{"keep", 0, 0, 'k'},
{"name", 0, 0, 'N'}, /* restore original name */
{"no-checksum",0, 0, 'F'},
{"no-mode", 0, 0, 523}, /* don't restore original mode */
{"no-name", 0, 0, 'n'}, /* don't restore original name */
{"no-time", 0, 0, 524}, /* don't restore original time */
{"no-stdin", 0, 0, 522},
{"no-warn", 0, 0, 525}, /* do not display any warnings */
{"quiet", 0, 0, 'q'}, /* quiet mode */
{"silent", 0, 0, 'q'}, /* quiet mode */
{"threads", 0x21, 0, 'T'}, /* number of threads */
{"unlink", 0, 0, 'U'},
{"verbose", 0, 0, 'v'}, /* verbose mode */
{"no-color", 0, 0, 512},
{"mono", 0, 0, 513},
{"color", 0, 0, 514},
{ 0, 0, 0, 0 }
};
char *env, *p;
int i, optc;
int nargc;
char **nargv = NULL;
static const char sep[] = " \t";
acc_getopt_t mfx_getopt;
char shortopts[128];
env = (char *) getenv(OPTIONS_VAR);
if (env == NULL || !env[0])
return;
p = (char *) malloc(strlen(env)+1);
if (p == NULL)
return;
strcpy(p,env);
env = p;
nargc = 1;
for (;;)
{
while (*p && strchr(sep,*p))
p++;
if (*p == '\0')
break;
nargc++;
while (*p && !strchr(sep,*p))
p++;
if (*p == '\0')
break;
p++;
}
if (nargc > 1)
nargv = (char **) calloc(nargc+1,sizeof(char *));
if (nargv == NULL)
{
free(env);
return;
}
nargv[0] = argv[0];
p = env;
nargc = 1;
for (;;)
{
while (*p && strchr(sep,*p))
p++;
if (*p == '\0')
break;
nargv[nargc++] = p;
while (*p && !strchr(sep,*p))
p++;
if (*p == '\0')
break;
*p++ = '\0';
}
nargv[nargc] = NULL;
#if 0
/* debug */
fprintf(stderr,"%3d\n",nargc);
for (i = 0; i <= nargc; i++)
fprintf(stderr,"%3d '%s'\n",i,nargv[i]);
#endif
for (i = 1; i < nargc; i++)
if (nargv[i][0] != '-' || !nargv[i][1] || strcmp(nargv[i],"--") == 0)
e_envopt(nargv[i]);
prepare_shortopts(shortopts, "123456789P", longopts);
acc_getopt_init(&mfx_getopt, 1, nargc, nargv);
mfx_getopt.progname = argv0;
mfx_getopt.opterr = handle_opterr;
while ((optc = acc_getopt(&mfx_getopt, shortopts, longopts, NULL)) >= 0)
{
if (do_option(&mfx_getopt, optc) != 0)
e_envopt(NULL);
}
if (mfx_getopt.optind < nargc)
e_envopt(nargv[mfx_getopt.optind]);
free(nargv);
free(env);
UNUSED(argc);
if (opt_checksum)
opt_checksum = -1; /* reset to default */
}
#endif /* defined(OPTIONS_VAR) */
#define ACC_WANT_ACC_CHK_CH 1
#undef ACCCHK_ASSERT
#include "miniacc.h"
#undef ACCCHK_ASSERT
static void sanity_check(void)
{
#if (ACC_CC_MSC && ((_MSC_VER) < 700))
#else
#define ACCCHK_ASSERT(expr) ACC_COMPILE_TIME_ASSERT(expr)
#include "miniacc.h"
#endif
#undef ACCCHK_ASSERT
#undef ACC_WANT_ACC_CHK_CH
}
/*************************************************************************
// main entry point
**************************************************************************/
int __acc_cdecl_main main(int argc, char *argv[])
{
int i;
lzo_bool foreground = 0;
static char default_argv0[] = "lzop";
int cmdline_cmd = CMD_NONE;
sanity_check();
#if defined(__MINT__)
__binmode(1);
__set_binmode(stdout, 0);
__set_binmode(stderr, 0);
#endif
acc_wildargv(&argc, &argv);
#if defined(__DJGPP__)
opt_shortname = !_USE_LFN;
#elif (ACC_OS_DOS16 || ACC_OS_WIN16 || ACC_OS_DOS32)
opt_shortname = 1;
#endif
current_time = fix_time(time(NULL));
if (!argv[0] || !argv[0][0])
argv[0] = default_argv0;
argv0 = argv[0];
progname = fn_basename(argv0);
#if defined(DOSISH)
if (strcasecmp(progname,"lzop.exe") == 0)
progname = default_argv0;
else if (strcasecmp(progname,"lzop.ttp") == 0)
progname = default_argv0;
#endif
/* For compatibility with gzip, use program name as an option. */
if (strncasecmp(progname, "un", 2) == 0) /* unlzop */
opt_pgm = PGM_UNLZOP;
#if 0
if (progname[0] && strcasecmp(progname+1, "cat") == 0) /* ocat */
opt_pgm = PGM_OCAT;
#endif
set_term(stderr);
opt_stdin = acc_isatty(STDIN_FILENO) ? 0 : OPT_STDIN_GUESSED;
UNUSED(foreground);
#ifdef SIGINT
foreground = signal(SIGINT, SIG_IGN) != SIG_IGN;
if (foreground)
(void) signal(SIGINT, e_sighandler);
#endif
#ifdef SIGBREAK
if (foreground)
(void) signal(SIGBREAK, e_sighandler);
#endif
#ifdef SIGTERM
if (signal(SIGTERM, SIG_IGN) != SIG_IGN)
(void) signal(SIGTERM, e_sighandler);
#endif
#ifdef SIGHUP
if (signal(SIGHUP, SIG_IGN) != SIG_IGN)
(void) signal(SIGHUP, e_sighandler);
#endif
#if defined(HAVE_UMASK)
u_mask = (MODE_T) umask(0700);
(void) umask(u_mask);
#endif
u_mask &= 0777;
#if defined(WITH_LZO)
if (lzo_init() != LZO_E_OK)
{
head();
fprintf(stderr,"lzo_init() failed - check your LZO installation !\n");
if (LZO_VERSION != lzo_version())
fprintf(stderr,"library version conflict (%x, %x) - check your LZO installation !\n", LZO_VERSION, lzo_version());
e_exit(EXIT_LZO_INIT);
}
#if 0
if (lzo_version() < LZO_VERSION)
{
head();
fprintf(stderr,"library version conflict (%x, %x) - check your LZO installation !\n", LZO_VERSION, lzo_version());
e_exit(EXIT_LZO_INIT);
}
#endif
#endif
#if defined(WITH_NRV)
if (nrv_init() != NRV_E_OK)
{
e_exit(EXIT_LZO_INIT);
}
#endif
ACC_COMPILE_TIME_ASSERT(sizeof(lzo_uint32) >= 4)
#if defined(SIZEOF_SIZE_T)
ACC_COMPILE_TIME_ASSERT(sizeof(size_t) == SIZEOF_SIZE_T)
#endif
ACC_COMPILE_TIME_ASSERT(sizeof(fi.name) >= 2*(PATH_MAX))
assert(STDIN_FILENO >= 0);
assert(STDOUT_FILENO >= 0);
assert(STDERR_FILENO >= 0);
f_init();
#if defined(OPTIONS_VAR)
get_envoptions(argc,argv);
#endif
assert(cmdline_cmd == CMD_NONE);
i = get_options(argc,argv);
assert(i <= argc);
set_term(NULL);
cmdline_cmd = opt_cmd;
switch (opt_cmd)
{
case CMD_NONE:
/* For compatibility with gzip, use program name as an option. */
if (opt_pgm == PGM_UNLZOP)
{
set_cmd(CMD_DECOMPRESS);
break;
}
#if 0
if (opt_pgm == PGM_OCAT)
{
set_cmd(CMD_DECOMPRESS);
if (i == argc)
opt_stdin = OPT_STDIN_REQUESTED;
if (!opt_output_name)
opt_stdout = 1;
break;
}
#endif
/* default - compress */
if (!set_method(0,3))
e_method('3');
break;
case CMD_COMPRESS:
break;
case CMD_DECOMPRESS:
break;
case CMD_TEST:
opt_checksum = 1;
opt_decompress_safe = 1;
break;
case CMD_LIST:
break;
case CMD_LS:
break;
case CMD_INFO:
break;
case CMD_SYSINFO:
sysinfo();
e_exit(EXIT_OK);
break;
case CMD_LICENSE:
license();
e_exit(EXIT_OK);
break;
case CMD_HELP:
help();
e_exit(EXIT_OK);
break;
case CMD_INTRO:
opt_console = CON_SCREEN;
(void) ((con_intro(con_term) || (help(), 0)));
e_exit(EXIT_OK);
case CMD_VERSION:
version();
e_exit(EXIT_OK);
break;
default:
/* ??? */
break;
}
if (opt_cmd != CMD_COMPRESS)
{
opt_method = 0;
opt_level = 0;
opt_filter = 0;
}
if (!opt_stdin && !opt_stdout && opt_verbose > 0)
{
if (argc == 1)
{
/* no arguments */
(void) (opt_pgm == PGM_LZOP && con_intro(con_term));
e_help();
}
#if 0
else if (cmdline_cmd == CMD_NONE && i == argc)
{
/* no command and no file */
e_help();
}
#endif
}
set_term(stderr);
check_options(i,argc);
num_files = argc - i;
if (!x_enter(NULL))
e_memory();
do_files(i,argc,argv);
x_leave(NULL);
do_exit();
return exit_code;
}
/*
vi:ts=4:et
*/