lrzip/main.c

745 lines
24 KiB
C
Raw Normal View History

2010-03-29 01:07:08 +02:00
/*
Copyright (C) 2006-2011 Con Kolivas
Copyright (C) 2011 Peter Hyman
Copyright (C) 1998-2003 Andrew Tridgell
2010-03-29 01:07:08 +02:00
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
2010-03-29 01:07:08 +02:00
*/
/* lrzip compression - main program */
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <signal.h>
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif
#ifdef HAVE_CTYPE_H
# include <ctype.h>
#endif
#ifdef HAVE_SYS_TIME_H
# include <sys/time.h>
#endif
#ifdef HAVE_SYS_RESOURCE_H
# include <sys/resource.h>
#endif
2010-03-29 01:07:08 +02:00
#include "rzip.h"
#include "lrzip.h"
#include "util.h"
/* needed for CRC routines */
#include "lzma/C/7zCrc.h"
/* Macros for testing parameters */
#define isparameter( parmstring, value ) (!strcasecmp( parmstring, value ))
#define iscaseparameter( parmvalue, value ) (!strcmp( parmvalue, value ))
/* main() defines, different from liblrzip defines */
#define FLAG_VERBOSE (FLAG_VERBOSITY | FLAG_VERBOSITY_MAX)
#define FLAG_NOT_LZMA (FLAG_NO_COMPRESS | FLAG_LZO_COMPRESS | FLAG_BZIP2_COMPRESS | FLAG_ZLIB_COMPRESS | FLAG_ZPAQ_COMPRESS)
#define LZMA_COMPRESS (!(control.flags & FLAG_NOT_LZMA))
#define SHOW_PROGRESS (control.flags & FLAG_SHOW_PROGRESS)
#define KEEP_FILES (control.flags & FLAG_KEEP_FILES)
#define TEST_ONLY (control.flags & FLAG_TEST_ONLY)
#define FORCE_REPLACE (control.flags & FLAG_FORCE_REPLACE)
#define DECOMPRESS (control.flags & FLAG_DECOMPRESS)
#define NO_COMPRESS (control.flags & FLAG_NO_COMPRESS)
#define LZO_COMPRESS (control.flags & FLAG_LZO_COMPRESS)
#define BZIP2_COMPRESS (control.flags & FLAG_BZIP2_COMPRESS)
#define ZLIB_COMPRESS (control.flags & FLAG_ZLIB_COMPRESS)
#define ZPAQ_COMPRESS (control.flags & FLAG_ZPAQ_COMPRESS)
#define VERBOSE (control.flags & FLAG_VERBOSE)
#define VERBOSITY (control.flags & FLAG_VERBOSITY)
#define MAX_VERBOSE (control.flags & FLAG_VERBOSITY_MAX)
#define STDIN (control.flags & FLAG_STDIN)
#define STDOUT (control.flags & FLAG_STDOUT)
#define INFO (control.flags & FLAG_INFO)
#define UNLIMITED (control.flags & FLAG_UNLIMITED)
#define HASH_CHECK (control.flags & FLAG_HASH)
#define HAS_MD5 (control.flags & FLAG_MD5)
#define CHECK_FILE (control.flags & FLAG_CHECK)
#define KEEP_BROKEN (control.flags & FLAG_KEEP_BROKEN)
#define LZO_TEST (control.flags & FLAG_THRESHOLD)
#define TMP_OUTBUF (control.flags & FLAG_TMP_OUTBUF)
2011-03-14 04:56:59 +01:00
#define TMP_INBUF (control.flags & FLAG_TMP_INBUF)
#define print_output(format, args...) do {\
fprintf(control.msgout, format, ##args); \
fflush(control.msgout); \
} while (0)
#define print_progress(format, args...) do {\
if (SHOW_PROGRESS) \
print_output(format, ##args); \
} while (0)
#define print_verbose(format, args...) do {\
if (VERBOSE) \
print_output(format, ##args); \
} while (0)
#define print_maxverbose(format, args...) do {\
if (MAX_VERBOSE) \
print_output(format, ##args); \
} while (0)
2010-03-29 01:07:08 +02:00
#if defined(NOTHREAD) || !defined(_SC_NPROCESSORS_ONLN)
# define PROCESSORS (1)
#else
# define PROCESSORS (sysconf(_SC_NPROCESSORS_ONLN))
#endif
#ifdef _SC_PAGE_SIZE
# define PAGE_SIZE (sysconf(_SC_PAGE_SIZE))
#else
# define PAGE_SIZE (4096)
#endif
#ifdef __APPLE__
# include <sys/sysctl.h>
static inline i64 get_ram(void)
{
int mib[2];
size_t len;
i64 *p, ramsize;
mib[0] = CTL_HW;
mib[1] = HW_MEMSIZE;
sysctl(mib, 2, NULL, &len, NULL, 0);
p = malloc(len);
sysctl(mib, 2, p, &len, NULL, 0);
ramsize = *p;
return ramsize;
}
#else /* __APPLE__ */
static inline i64 get_ram(void)
{
i64 ramsize;
FILE *meminfo;
char aux[256];
char *ignore;
ramsize = (i64)sysconf(_SC_PHYS_PAGES) * PAGE_SIZE;
if (ramsize > 0)
return ramsize;
/* Workaround for uclibc which doesn't properly support sysconf */
if(!(meminfo = fopen("/proc/meminfo", "r")))
fatal("fopen\n");
while(!feof(meminfo) && !fscanf(meminfo, "MemTotal: %Lu kB", &ramsize))
ignore = fgets(aux, sizeof(aux), meminfo);
if (fclose(meminfo) == -1)
fatal("fclose");
ramsize *= 1000;
return ramsize;
}
#endif
2010-03-29 01:07:08 +02:00
static rzip_control control;
2010-03-29 01:07:08 +02:00
static void usage(void)
{
print_output("lrzip version %d.%d%d\n", LRZIP_MAJOR_VERSION, LRZIP_MINOR_VERSION, LRZIP_MINOR_SUBVERSION);
print_output("Copyright (C) Con Kolivas 2006-2011\n");
print_output("Based on rzip ");
print_output("Copyright (C) Andrew Tridgell 1998-2003\n\n");
print_output("Usage: lrzip [options] <file...>\n");
2011-02-21 02:49:44 +01:00
print_output("General options:\n");
print_output(" -c check integrity of file written on decompression\n");
print_output(" -d decompress\n");
print_output(" -h|-? show help\n");
2011-02-21 02:49:44 +01:00
print_output(" -H display md5 hash integrity information\n");
print_output(" -i show compressed file information\n");
print_output(" -q don't show compression progress\n");
print_output(" -t test compressed file integrity\n");
print_output(" -v[v] Increase verbosity\n");
print_output(" -V show version\n");
print_output("Options affecting output:\n");
print_output(" -D delete existing files\n");
print_output(" -f force overwrite of any existing files\n");
print_output(" -k keep broken or damaged output files\n");
print_output(" -o filename specify the output file name and/or path\n");
print_output(" -O directory specify the output directory when -o is not used\n");
print_output(" -S suffix specify compressed suffix (default '.lrz')\n");
2011-02-21 02:49:44 +01:00
print_output("Options affecting compression:\n");
print_output(" -b bzip2 compression\n");
print_output(" -g gzip compression using zlib\n");
2011-02-21 02:49:44 +01:00
print_output(" -l lzo compression (ultra fast)\n");
print_output(" -n no backend compression - prepare for other compressor\n");
print_output(" -z zpaq compression (best, extreme compression, extremely slow)\n");
2011-02-21 02:49:44 +01:00
print_output("Low level options:\n");
print_output(" -L level set lzma/bzip2/gzip compression level (1-9, default 7)\n");
print_output(" -N value Set nice value to value (default 19)\n");
print_output(" -p value Set processor count to override number of threads\n");
print_output(" -T Disable LZO compressibility testing\n");
2011-02-21 02:49:44 +01:00
print_output(" -U Use unlimited window size beyond ramsize (potentially much slower)\n");
print_output(" -w size maximum compression window in hundreds of MB\n");
print_output(" default chosen by heuristic dependent on ram and chosen compression\n");
2011-02-21 02:06:49 +01:00
print_output("\nLRZIP=NOCONFIG environment variable setting can be used to bypass lrzip.conf.\n");
2011-02-22 15:29:17 +01:00
print_output("TMP environment variable will be used for storage of temporary files when needed.\n");
print_output("TMPDIR may also be stored in lrzip.conf file.\n");
2011-02-21 02:49:44 +01:00
print_output("\nIf no filenames or \"-\" is specified, stdin/out will be used.\n");
2010-03-29 01:07:08 +02:00
}
static void sighandler(int sig __UNUSED__)
{
unlink_files();
exit(0);
}
static void show_summary(void)
{
/* OK, if verbosity set, print summary of options selected */
if (!INFO) {
if (!TEST_ONLY)
print_verbose("The following options are in effect for this %s.\n",
DECOMPRESS ? "DECOMPRESSION" : "COMPRESSION");
print_verbose("Threading is %s. Number of CPUs detected: %d\n", control.threads > 1? "ENABLED" : "DISABLED",
control.threads);
print_verbose("Detected %lld bytes ram\n", control.ramsize);
print_verbose("Compression level %d\n", control.compression_level);
print_verbose("Nice Value: %d\n", control.nice_val);
print_verbose("Show Progress\n");
print_maxverbose("Max ");
print_verbose("Verbose\n");
if (FORCE_REPLACE)
print_verbose("Overwrite Files\n");
if (!KEEP_FILES)
print_verbose("Remove input files on completion\n");
if (control.outdir)
print_verbose("Output Directory Specified: %s\n", control.outdir);
else if (control.outname)
print_verbose("Output Filename Specified: %s\n", control.outname);
if (TEST_ONLY)
print_verbose("Test file integrity\n");
if (control.tmpdir)
print_verbose("Temporary Directory set as: %s\n", control.tmpdir);
/* show compression options */
if (!DECOMPRESS && !TEST_ONLY) {
print_verbose("Compression mode is: ");
if (LZMA_COMPRESS)
print_verbose("LZMA. LZO Compressibility testing %s\n", (LZO_TEST? "enabled" : "disabled"));
else if (LZO_COMPRESS)
print_verbose("LZO\n");
else if (BZIP2_COMPRESS)
print_verbose("BZIP2. LZO Compressibility testing %s\n", (LZO_TEST? "enabled" : "disabled"));
else if (ZLIB_COMPRESS)
print_verbose("GZIP\n");
else if (ZPAQ_COMPRESS)
print_verbose("ZPAQ. LZO Compressibility testing %s\n", (LZO_TEST? "enabled" : "disabled"));
else if (NO_COMPRESS)
print_verbose("RZIP pre-processing only\n");
2011-03-08 22:50:46 +01:00
if (control.window)
print_verbose("Compression Window: %lld = %lldMB\n", control.window, control.window * 100ull);
/* show heuristically computed window size */
if (!control.window && !UNLIMITED) {
i64 temp_chunk, temp_window;
if (STDOUT || STDIN)
temp_chunk = control.maxram;
else
temp_chunk = control.maxram * 2;
2011-03-08 22:50:46 +01:00
temp_window = temp_chunk / (100 * 1024 * 1024);
print_verbose("Heuristically Computed Compression Window: %lld = %lldMB\n", temp_window, temp_window * 100ull);
}
if (UNLIMITED)
print_verbose("Using Unlimited Window size\n");
}
}
}
static void read_config( struct rzip_control *control )
{
/* check for lrzip.conf in ., $HOME/.lrzip and /etc/lrzip */
char *HOME, *homeconf;
2011-03-09 03:32:37 +01:00
char *parametervalue;
char *parameter;
char *line;
FILE *fp;
line = malloc(255);
homeconf = malloc(255);
if (line == NULL || homeconf == NULL)
fatal("Fatal Memory Error in read_config");
fp = fopen("lrzip.conf", "r");
if (fp)
fprintf(control->msgout, "Using configuration file ./lrzip.conf\n");
if (fp == NULL) {
fp = fopen("/etc/lrzip/lrzip.conf", "r");
if (fp)
fprintf(control->msgout, "Using configuration file /etc/lrzip/lrzip.conf\n");
}
if (fp == NULL) {
HOME=getenv("HOME");
if (HOME) {
strcpy(homeconf, HOME);
strcat(homeconf,"/.lrzip/lrzip.conf");
fp = fopen(homeconf, "r");
if (fp)
fprintf(control->msgout, "Using configuration file %s\n", homeconf);
}
}
if (fp == NULL)
return;
/* if we get here, we have a file. read until no more. */
2011-03-09 03:32:37 +01:00
while ((fgets(line, 255, fp)) != NULL) {
if (strlen(line))
line[strlen(line) - 1] = '\0';
parameter = strtok(line, " =");
if (parameter == NULL)
continue;
/* skip if whitespace or # */
if (isspace(*parameter))
continue;
if (*parameter == '#')
continue;
parametervalue = strtok(NULL, " =");
if (parametervalue == NULL)
continue;
/* have valid parameter line, now assign to control */
if (isparameter(parameter, "window"))
control->window = atoi(parametervalue);
else if (isparameter(parameter, "unlimited")) {
if (isparameter(parametervalue, "yes"))
control->flags |= FLAG_UNLIMITED;
} else if (isparameter(parameter, "compressionlevel")) {
control->compression_level = atoi(parametervalue);
if ( control->compression_level < 1 || control->compression_level > 9 )
failure("CONF.FILE error. Compression Level must between 1 and 9");
} else if (isparameter(parameter, "compressionmethod")) {
/* valid are rzip, gzip, bzip2, lzo, lzma (default), and zpaq */
if (control->flags & FLAG_NOT_LZMA)
failure("CONF.FILE error. Can only specify one compression method");
if (isparameter(parametervalue, "bzip2"))
control->flags |= FLAG_BZIP2_COMPRESS;
else if (isparameter(parametervalue, "gzip"))
control->flags |= FLAG_ZLIB_COMPRESS;
else if (isparameter(parametervalue, "lzo"))
control->flags |= FLAG_LZO_COMPRESS;
else if (isparameter(parametervalue, "rzip"))
control->flags |= FLAG_NO_COMPRESS;
else if (isparameter(parametervalue, "zpaq"))
control->flags |= FLAG_ZPAQ_COMPRESS;
else if (!isparameter(parametervalue, "lzma")) /* oops, not lzma! */
failure("CONF.FILE error. Invalid compression method %s specified\n",parametervalue);
} else if (isparameter(parameter, "lzotest")) {
/* default is yes */
if (isparameter(parametervalue, "no"))
control->flags &= ~FLAG_THRESHOLD;
} else if (isparameter(parameter, "hashcheck")) {
if (isparameter(parametervalue, "yes")) {
control->flags |= FLAG_CHECK;
control->flags |= FLAG_HASH;
}
} else if (isparameter(parameter, "showhash")) {
if (isparameter(parametervalue, "yes"))
control->flags |= FLAG_HASH;
} else if (isparameter(parameter, "outputdirectory")) {
control->outdir = malloc(strlen(parametervalue) + 2);
if (!control->outdir)
fatal("Fatal Memory Error in read_config");
strcpy(control->outdir, parametervalue);
if (strcmp(parametervalue + strlen(parametervalue) - 1, "/"))
strcat(control->outdir, "/");
} else if (isparameter(parameter,"verbosity")) {
if (control->flags & FLAG_VERBOSE)
failure("CONF.FILE error. Verbosity already defined.");
if (isparameter(parametervalue, "yes"))
control->flags |= FLAG_VERBOSITY;
else if (isparameter(parametervalue,"max"))
control->flags |= FLAG_VERBOSITY_MAX;
else /* oops, unrecognized value */
print_err("lrzip.conf: Unrecognized verbosity value %s. Ignored.\n", parametervalue);
} else if (isparameter(parameter, "showprogress")) {
/* Yes by default */
if (isparameter(parametervalue, "NO"))
control->flags &= ~FLAG_SHOW_PROGRESS;
} else if (isparameter(parameter,"nice")) {
control->nice_val = atoi(parametervalue);
if (control->nice_val < -20 || control->nice_val > 19)
failure("CONF.FILE error. Nice must be between -20 and 19");
} else if (isparameter(parameter, "keepbroken")) {
if (isparameter(parametervalue, "yes" ))
control->flags |= FLAG_KEEP_BROKEN;
} else if (iscaseparameter(parameter, "DELETEFILES")) {
/* delete files must be case sensitive */
if (iscaseparameter(parametervalue, "YES"))
control->flags &= ~FLAG_KEEP_FILES;
} else if (iscaseparameter(parameter, "REPLACEFILE")) {
/* replace lrzip file must be case sensitive */
if (iscaseparameter(parametervalue, "YES"))
control->flags |= FLAG_FORCE_REPLACE;
} else if (isparameter(parameter, "tmpdir")) {
control->tmpdir = realloc(NULL, strlen(parametervalue) + 2);
if (!control->tmpdir)
fatal("Fatal Memory Error in read_config");
strcpy(control->tmpdir, parametervalue);
if (strcmp(parametervalue + strlen(parametervalue) - 1, "/"))
strcat(control->tmpdir, "/");
} else
/* oops, we have an invalid parameter, display */
print_err("lrzip.conf: Unrecognized parameter value, %s = %s. Continuing.\n",\
parameter, parametervalue);
}
/* clean up */
free(line);
free(homeconf);
2011-03-09 03:41:16 +01:00
if (unlikely(fclose(fp)))
fatal("Failed to fclose fp in read_config\n");
/* fprintf(stderr, "\nWindow = %d \
\nCompression Level = %d \
\nThreshold = %1.2f \
\nOutput Directory = %s \
\nFlags = %d\n", control->window,control->compression_level, control->threshold, control->outdir, control->flags);
*/
}
2010-03-29 01:07:08 +02:00
int main(int argc, char *argv[])
{
struct timeval start_time, end_time;
struct sigaction handler;
double seconds,total_time; // for timers
Huge rewrite of buffer reading in rzip.c. We use a wrapper instead of accessing the buffer directly, thus allowing us to have window sizes larger than available ram. This is implemented through the use of a "sliding mmap" implementation. Sliding mmap uses two mmapped buffers, one large one as previously, and one page sized smaller one. When an attempt is made to read beyond the end of the large buffer, the small buffer is remapped to the file area that's being accessed. While this implementation is 100x slower than direct mmapping, it allows us to implement unlimited sized compression windows. Implement the -U option with unlimited sized windows. Rework the selection of compression windows. Instead of trying to guess how much ram the machine might be able to access, we try to safely buffer as much ram as we can, and then use that to determine the file buffer size. Do not choose an arbitrary upper window limit unless -w is specified. Rework the -M option to try to buffer the entire file, reducing the buffer size until we succeed. Align buffer sizes to page size. Clean up lots of unneeded variables. Fix lots of minor logic issues to do with window sizes accepted/passed to rzip and the compression backends. More error handling. Change -L to affect rzip compression level directly as well as backend compression level and use 9 by default now. More cleanups of information output. Use 3 point release numbering in case one minor version has many subversions. Numerous minor cleanups and tidying. Updated docs and manpages.
2010-11-04 11:14:55 +01:00
int c, i;
2010-10-31 05:17:04 +01:00
int hours,minutes;
extern int optind;
2010-03-29 01:07:08 +02:00
char *eptr; /* for environment */
memset(&control, 0, sizeof(control));
control.msgout = stderr;
register_outputfile(control.msgout);
control.flags = FLAG_SHOW_PROGRESS | FLAG_KEEP_FILES | FLAG_THRESHOLD;
2010-03-29 01:07:08 +02:00
control.suffix = ".lrz";
control.outdir = NULL;
control.tmpdir = NULL;
2010-03-29 01:07:08 +02:00
if (strstr(argv[0], "lrunzip"))
2010-03-29 01:07:08 +02:00
control.flags |= FLAG_DECOMPRESS;
control.compression_level = 7;
Huge rewrite of buffer reading in rzip.c. We use a wrapper instead of accessing the buffer directly, thus allowing us to have window sizes larger than available ram. This is implemented through the use of a "sliding mmap" implementation. Sliding mmap uses two mmapped buffers, one large one as previously, and one page sized smaller one. When an attempt is made to read beyond the end of the large buffer, the small buffer is remapped to the file area that's being accessed. While this implementation is 100x slower than direct mmapping, it allows us to implement unlimited sized compression windows. Implement the -U option with unlimited sized windows. Rework the selection of compression windows. Instead of trying to guess how much ram the machine might be able to access, we try to safely buffer as much ram as we can, and then use that to determine the file buffer size. Do not choose an arbitrary upper window limit unless -w is specified. Rework the -M option to try to buffer the entire file, reducing the buffer size until we succeed. Align buffer sizes to page size. Clean up lots of unneeded variables. Fix lots of minor logic issues to do with window sizes accepted/passed to rzip and the compression backends. More error handling. Change -L to affect rzip compression level directly as well as backend compression level and use 9 by default now. More cleanups of information output. Use 3 point release numbering in case one minor version has many subversions. Numerous minor cleanups and tidying. Updated docs and manpages.
2010-11-04 11:14:55 +01:00
control.ramsize = get_ram();
2010-03-29 01:07:08 +02:00
/* for testing single CPU */
control.threads = PROCESSORS; /* get CPUs for LZMA */
control.page_size = PAGE_SIZE;
2010-03-29 01:07:08 +02:00
control.nice_val = 19;
/* generate crc table */
CrcGenerateTable();
/* Get Temp Dir */
eptr = getenv("TMP");
if (eptr != NULL) {
control.tmpdir = malloc(strlen(eptr)+2);
if (control.tmpdir == NULL)
fatal("Failed to allocate for tmpdir\n");
strcpy(control.tmpdir, eptr);
if (strcmp(eptr+strlen(eptr) - 1, "/")) /* need a trailing slash */
strcat(control.tmpdir, "/");
}
2010-03-29 01:07:08 +02:00
/* Get Preloaded Defaults from lrzip.conf
* Look in ., $HOME/.lrzip/, /etc/lrzip.
* If LRZIP=NOCONFIG is set, then ignore config
*/
eptr = getenv("LRZIP");
if (eptr == NULL)
read_config(&control);
else if (!strstr(eptr,"NOCONFIG"))
read_config(&control);
while ((c = getopt(argc, argv, "L:h?dS:tVvDfqo:w:nlbUO:TN:p:gziHck")) != -1) {
2010-03-29 01:07:08 +02:00
switch (c) {
case 'b':
if (control.flags & FLAG_NOT_LZMA)
failure("Can only use one of -l, -b, -g, -z or -n\n");
control.flags |= FLAG_BZIP2_COMPRESS;
2010-03-29 01:07:08 +02:00
break;
case 'c':
control.flags |= FLAG_CHECK;
control.flags |= FLAG_HASH;
2010-03-29 01:07:08 +02:00
break;
case 'd':
control.flags |= FLAG_DECOMPRESS;
break;
case 'D':
control.flags &= ~FLAG_KEEP_FILES;
2010-03-29 01:07:08 +02:00
break;
case 'f':
control.flags |= FLAG_FORCE_REPLACE;
break;
case 'g':
if (control.flags & FLAG_NOT_LZMA)
failure("Can only use one of -l, -b, -g, -z or -n\n");
control.flags |= FLAG_ZLIB_COMPRESS;
2010-03-29 01:07:08 +02:00
break;
case 'h':
case '?':
usage();
return -1;
case 'H':
control.flags |= FLAG_HASH;
2010-03-29 01:07:08 +02:00
break;
case 'i':
control.flags |= FLAG_INFO;
2010-03-29 01:07:08 +02:00
break;
case 'k':
control.flags |= FLAG_KEEP_BROKEN;
2010-03-29 01:07:08 +02:00
break;
case 'l':
if (control.flags & FLAG_NOT_LZMA)
failure("Can only use one of -l, -b, -g, -z or -n\n");
2010-03-29 01:07:08 +02:00
control.flags |= FLAG_LZO_COMPRESS;
break;
case 'L':
control.compression_level = atoi(optarg);
if (control.compression_level < 1 || control.compression_level > 9)
failure("Invalid compression level (must be 1-9)\n");
break;
2010-03-29 01:07:08 +02:00
case 'n':
if (control.flags & FLAG_NOT_LZMA)
failure("Can only use one of -l, -b, -g, -z or -n\n");
2010-03-29 01:07:08 +02:00
control.flags |= FLAG_NO_COMPRESS;
break;
case 'N':
control.nice_val = atoi(optarg);
if (control.nice_val < -20 || control.nice_val > 19)
failure("Invalid nice value (must be -20..19)\n");
Huge rewrite of buffer reading in rzip.c. We use a wrapper instead of accessing the buffer directly, thus allowing us to have window sizes larger than available ram. This is implemented through the use of a "sliding mmap" implementation. Sliding mmap uses two mmapped buffers, one large one as previously, and one page sized smaller one. When an attempt is made to read beyond the end of the large buffer, the small buffer is remapped to the file area that's being accessed. While this implementation is 100x slower than direct mmapping, it allows us to implement unlimited sized compression windows. Implement the -U option with unlimited sized windows. Rework the selection of compression windows. Instead of trying to guess how much ram the machine might be able to access, we try to safely buffer as much ram as we can, and then use that to determine the file buffer size. Do not choose an arbitrary upper window limit unless -w is specified. Rework the -M option to try to buffer the entire file, reducing the buffer size until we succeed. Align buffer sizes to page size. Clean up lots of unneeded variables. Fix lots of minor logic issues to do with window sizes accepted/passed to rzip and the compression backends. More error handling. Change -L to affect rzip compression level directly as well as backend compression level and use 9 by default now. More cleanups of information output. Use 3 point release numbering in case one minor version has many subversions. Numerous minor cleanups and tidying. Updated docs and manpages.
2010-11-04 11:14:55 +01:00
break;
case 'o':
if (control.outdir)
failure("Cannot have -o and -O together\n");
control.outname = optarg;
2010-03-29 01:07:08 +02:00
break;
case 'O':
if (control.outname) /* can't mix -o and -O */
failure("Cannot have options -o and -O together\n");
2010-03-29 01:07:08 +02:00
control.outdir = malloc(strlen(optarg) + 2);
if (control.outdir == NULL)
fatal("Failed to allocate for outdir\n");
strcpy(control.outdir,optarg);
if (strcmp(optarg+strlen(optarg) - 1, "/")) /* need a trailing slash */
strcat(control.outdir, "/");
break;
case 'p':
control.threads = atoi(optarg);
if (control.threads < 1)
failure("Must have at least one thread\n");
break;
case 'q':
control.flags &= ~FLAG_SHOW_PROGRESS;
break;
case 'S':
control.suffix = optarg;
break;
case 't':
if (control.outname)
failure("Cannot specify an output file name when just testing.\n");
if (!KEEP_FILES)
failure("Doubt that you want to delete a file when just testing.\n");
control.flags |= FLAG_TEST_ONLY;
break;
2010-03-29 01:07:08 +02:00
case 'T':
control.flags &= ~FLAG_THRESHOLD;
2010-03-29 01:07:08 +02:00
break;
case 'U':
control.flags |= FLAG_UNLIMITED;
2010-03-29 01:07:08 +02:00
break;
case 'v':
/* set verbosity flag */
if (!(control.flags & FLAG_VERBOSITY) && !(control.flags & FLAG_VERBOSITY_MAX))
control.flags |= FLAG_VERBOSITY;
else if ((control.flags & FLAG_VERBOSITY)) {
control.flags &= ~FLAG_VERBOSITY;
control.flags |= FLAG_VERBOSITY_MAX;
}
2010-03-29 01:07:08 +02:00
break;
case 'V':
print_output("lrzip version %d.%d%d\n",
LRZIP_MAJOR_VERSION, LRZIP_MINOR_VERSION, LRZIP_MINOR_SUBVERSION);
exit(0);
break;
case 'w':
control.window = atol(optarg);
2010-03-29 01:07:08 +02:00
break;
case 'z':
if (control.flags & FLAG_NOT_LZMA)
failure("Can only use one of -l, -b, -g, -z or -n\n");
2010-03-29 01:07:08 +02:00
control.flags |= FLAG_ZPAQ_COMPRESS;
break;
}
}
argc -= optind;
argv += optind;
if (control.outname && argc > 1)
failure("Cannot specify output filename with more than 1 file\n");
2010-03-29 01:07:08 +02:00
if (VERBOSE && !SHOW_PROGRESS) {
print_err("Cannot have -v and -q options. -v wins.\n");
2010-03-29 01:07:08 +02:00
control.flags |= FLAG_SHOW_PROGRESS;
}
if (UNLIMITED && control.window) {
print_err("If -U used, cannot specify a window size with -w.\n");
control.window = 0;
}
if (argc < 1)
control.flags |= FLAG_STDIN;
if (UNLIMITED && STDIN) {
print_err("Cannot have -U and stdin, unlimited mode disabled.\n");
control.flags &= ~FLAG_UNLIMITED;
}
2011-02-22 15:22:54 +01:00
/* Work out the compression overhead per compression thread for the
* compression back-ends that need a lot of ram */
if (LZMA_COMPRESS) {
int level = control.compression_level * 7 / 9 ? : 1;
i64 dictsize = (level <= 5 ? (1 << (level * 2 + 14)) :
(level == 6 ? (1 << 25) : (1 << 26)));
2011-02-22 15:22:54 +01:00
control.overhead = (dictsize * 23 / 2) + (4 * 1024 * 1024);
} else if (ZPAQ_COMPRESS)
control.overhead = 112 * 1024 * 1024;
/* Set the main nice value to half that of the backend threads since
* the rzip stage is usually the rate limiting step */
if (control.nice_val > 0 && !NO_COMPRESS) {
if (unlikely(setpriority(PRIO_PROCESS, 0, control.nice_val / 2) == -1))
print_err("Warning, unable to set nice value\n");
} else {
if (unlikely(setpriority(PRIO_PROCESS, 0, control.nice_val) == -1))
print_err("Warning, unable to set nice value\n");
}
2010-03-29 01:07:08 +02:00
/* One extra iteration for the case of no parameters means we will default to stdin/out */
for (i = 0; i <= argc; i++) {
if (i < argc)
control.infile = argv[i];
else if (!(i == 0 && STDIN))
2010-03-29 01:07:08 +02:00
break;
if (control.infile && (strcmp(control.infile, "-") == 0))
control.flags |= FLAG_STDIN;
if (control.outname && (strcmp(control.outname, "-") == 0)) {
2010-03-29 01:07:08 +02:00
control.flags |= FLAG_STDOUT;
control.msgout = stderr;
register_outputfile(control.msgout);
}
2010-03-29 01:07:08 +02:00
/* If we're using stdin and no output filename, use stdout */
if (STDIN && !control.outname) {
2010-03-29 01:07:08 +02:00
control.flags |= FLAG_STDOUT;
control.msgout = stderr;
register_outputfile(control.msgout);
}
2010-03-29 01:07:08 +02:00
if (!STDOUT) {
control.msgout = stdout;
register_outputfile(control.msgout);
}
2010-03-29 01:07:08 +02:00
/* Implement signal handler only once flags are set */
handler.sa_handler = &sighandler;
sigaction(SIGTERM, &handler, 0);
sigaction(SIGINT, &handler, 0);
if (!FORCE_REPLACE) {
if (STDIN && isatty(fileno((FILE *)stdin))) {
print_err("Will not read stdin from a terminal. Use -f to override.\n");
usage();
exit (1);
}
if (!TEST_ONLY && STDOUT && isatty(fileno((FILE *)stdout))) {
print_err("Will not write stdout to a terminal. Use -f to override.\n");
usage();
exit (1);
}
}
if (CHECK_FILE) {
if (!DECOMPRESS) {
print_err("Can only check file written on decompression.\n");
control.flags &= ~FLAG_CHECK;
} else if (STDOUT) {
print_err("Can't check file written when writing to stdout. Checking disabled.\n");
control.flags &= ~FLAG_CHECK;
}
}
/* Use less ram when using STDOUT to store the temporary output
* file. */
if (STDOUT && ((STDIN && DECOMPRESS) || !(DECOMPRESS || TEST_ONLY)))
control.maxram = control.ramsize * 2 / 9;
else
control.maxram = control.ramsize / 3;
if (BITS32) {
i64 usable_ram;
/* Decrease usable ram size on 32 bits due to kernel /
* userspace split. Cannot allocate larger than a 2
* gigabyte chunk due to 32 bit signed long being
* used in alloc */
usable_ram = MAX(control.ramsize - 900000000ll, 900000000ll);
control.maxram = MIN(control.maxram, usable_ram);
control.maxram = MIN(control.maxram, two_gig);
}
2011-03-12 12:58:39 +01:00
round_to_page(&control.maxram);
show_summary();
2010-03-29 01:07:08 +02:00
gettimeofday(&start_time, NULL);
2011-03-13 12:22:46 +01:00
if (DECOMPRESS || TEST_ONLY)
decompress_file(&control);
else if (INFO)
get_fileinfo(&control);
2010-03-29 01:07:08 +02:00
else
compress_file(&control);
2010-03-29 01:07:08 +02:00
/* compute total time */
gettimeofday(&end_time, NULL);
total_time = (end_time.tv_sec + (double)end_time.tv_usec / 1000000) -
(start_time.tv_sec + (double)start_time.tv_usec / 1000000);
hours = (int)total_time / 3600;
minutes = (int)(total_time / 60) % 60;
seconds = total_time - hours * 3600 - minutes * 60;
if (!INFO)
print_progress("Total time: %02d:%02d:%05.2f\n", hours, minutes, seconds);
2010-03-29 01:07:08 +02:00
}
return 0;
}