xref: /isa-l/programs/igzip_cli.c (revision cf6105271ac59185062bede80b229854ffb2c0ed)
12104a77dSRoy Oursler /**********************************************************************
22104a77dSRoy Oursler   Copyright(c) 2011-2018 Intel Corporation All rights reserved.
32104a77dSRoy Oursler 
42104a77dSRoy Oursler   Redistribution and use in source and binary forms, with or without
52104a77dSRoy Oursler   modification, are permitted provided that the following conditions
62104a77dSRoy Oursler   are met:
72104a77dSRoy Oursler     * Redistributions of source code must retain the above copyright
82104a77dSRoy Oursler       notice, this list of conditions and the following disclaimer.
92104a77dSRoy Oursler     * Redistributions in binary form must reproduce the above copyright
102104a77dSRoy Oursler       notice, this list of conditions and the following disclaimer in
112104a77dSRoy Oursler       the documentation and/or other materials provided with the
122104a77dSRoy Oursler       distribution.
132104a77dSRoy Oursler     * Neither the name of Intel Corporation nor the names of its
142104a77dSRoy Oursler       contributors may be used to endorse or promote products derived
152104a77dSRoy Oursler       from this software without specific prior written permission.
162104a77dSRoy Oursler 
172104a77dSRoy Oursler   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
182104a77dSRoy Oursler   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
192104a77dSRoy Oursler   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
202104a77dSRoy Oursler   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
212104a77dSRoy Oursler   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
222104a77dSRoy Oursler   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
232104a77dSRoy Oursler   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
242104a77dSRoy Oursler   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
252104a77dSRoy Oursler   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
262104a77dSRoy Oursler   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
272104a77dSRoy Oursler   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
282104a77dSRoy Oursler **********************************************************************/
292104a77dSRoy Oursler 
302104a77dSRoy Oursler #define _FILE_OFFSET_BITS 64
312104a77dSRoy Oursler #include <stdio.h>
322104a77dSRoy Oursler #include <stdlib.h>
332104a77dSRoy Oursler #include <assert.h>
342104a77dSRoy Oursler #include <string.h>
352104a77dSRoy Oursler #include <getopt.h>
362104a77dSRoy Oursler #include <sys/stat.h>
372104a77dSRoy Oursler #include <utime.h>
382104a77dSRoy Oursler #include <unistd.h>
392104a77dSRoy Oursler #include <stdbool.h>
402104a77dSRoy Oursler #include <stdarg.h>
41722144eeSTomasz Kantecki #include <errno.h>
422104a77dSRoy Oursler #include "igzip_lib.h" /* Normally you use isa-l.h instead for external programs */
432104a77dSRoy Oursler 
440a7e3167SGreg Tucker #if defined(HAVE_THREADS)
450a7e3167SGreg Tucker #include <pthread.h>
460a7e3167SGreg Tucker #include "crc.h"
470a7e3167SGreg Tucker #endif
480a7e3167SGreg Tucker 
492104a77dSRoy Oursler #if !defined(VERSION)
502104a77dSRoy Oursler #if defined(ISAL_VERSION)
512104a77dSRoy Oursler #define VERSION ISAL_VERSION
522104a77dSRoy Oursler #else
532104a77dSRoy Oursler #define VERSION "unknown version"
542104a77dSRoy Oursler #endif
552104a77dSRoy Oursler #endif
562104a77dSRoy Oursler 
572104a77dSRoy Oursler #define BAD_OPTION       1
582104a77dSRoy Oursler #define BAD_LEVEL        1
592104a77dSRoy Oursler #define FILE_EXISTS      0
602104a77dSRoy Oursler #define MALLOC_FAILED    -1
612104a77dSRoy Oursler #define FILE_OPEN_ERROR  -2
622104a77dSRoy Oursler #define FILE_READ_ERROR  -3
632104a77dSRoy Oursler #define FILE_WRITE_ERROR -4
642104a77dSRoy Oursler 
652104a77dSRoy Oursler #define BUF_SIZE   1024
662104a77dSRoy Oursler #define BLOCK_SIZE (1024 * 1024)
672104a77dSRoy Oursler 
68cd5de57fSRoy Oursler #define MAX_FILEPATH_BUF 4096
69cd5de57fSRoy Oursler 
702104a77dSRoy Oursler #define UNIX 3
712104a77dSRoy Oursler 
72cd5de57fSRoy Oursler #define NAME_DEFAULT 0
73cd5de57fSRoy Oursler #define NO_NAME      1
74cd5de57fSRoy Oursler #define YES_NAME     2
75cd5de57fSRoy Oursler 
767177ff99SRoy Oursler #define NO_TEST 0
777177ff99SRoy Oursler #define TEST    1
787177ff99SRoy Oursler 
792104a77dSRoy Oursler #define LEVEL_DEFAULT      2
802104a77dSRoy Oursler #define DEFAULT_SUFFIX_LEN 3
812104a77dSRoy Oursler char *default_suffixes[] = { ".gz", ".z" };
822104a77dSRoy Oursler int default_suffixes_lens[] = { 3, 2 };
832104a77dSRoy Oursler 
842104a77dSRoy Oursler char stdin_file_name[] = "-";
852104a77dSRoy Oursler int stdin_file_name_len = 1;
862104a77dSRoy Oursler 
87*cf610527SMarcel Cornu enum compression_modes { COMPRESS_MODE, DECOMPRESS_MODE };
882104a77dSRoy Oursler 
89*cf610527SMarcel Cornu enum long_only_opt_val { RM };
902104a77dSRoy Oursler 
91*cf610527SMarcel Cornu enum log_types { INFORM, WARN, ERROR, VERBOSE };
922104a77dSRoy Oursler 
932104a77dSRoy Oursler int level_size_buf[10] = {
942104a77dSRoy Oursler #ifdef ISAL_DEF_LVL0_DEFAULT
952104a77dSRoy Oursler         ISAL_DEF_LVL0_DEFAULT,
962104a77dSRoy Oursler #else
972104a77dSRoy Oursler         0,
982104a77dSRoy Oursler #endif
992104a77dSRoy Oursler #ifdef ISAL_DEF_LVL1_DEFAULT
1002104a77dSRoy Oursler         ISAL_DEF_LVL1_DEFAULT,
1012104a77dSRoy Oursler #else
1022104a77dSRoy Oursler         0,
1032104a77dSRoy Oursler #endif
1042104a77dSRoy Oursler #ifdef ISAL_DEF_LVL2_DEFAULT
1052104a77dSRoy Oursler         ISAL_DEF_LVL2_DEFAULT,
1062104a77dSRoy Oursler #else
1072104a77dSRoy Oursler         0,
1082104a77dSRoy Oursler #endif
1092104a77dSRoy Oursler #ifdef ISAL_DEF_LVL3_DEFAULT
1102104a77dSRoy Oursler         ISAL_DEF_LVL3_DEFAULT,
1112104a77dSRoy Oursler #else
1122104a77dSRoy Oursler         0,
1132104a77dSRoy Oursler #endif
1142104a77dSRoy Oursler #ifdef ISAL_DEF_LVL4_DEFAULT
1152104a77dSRoy Oursler         ISAL_DEF_LVL4_DEFAULT,
1162104a77dSRoy Oursler #else
1172104a77dSRoy Oursler         0,
1182104a77dSRoy Oursler #endif
1192104a77dSRoy Oursler #ifdef ISAL_DEF_LVL5_DEFAULT
1202104a77dSRoy Oursler         ISAL_DEF_LVL5_DEFAULT,
1212104a77dSRoy Oursler #else
1222104a77dSRoy Oursler         0,
1232104a77dSRoy Oursler #endif
1242104a77dSRoy Oursler #ifdef ISAL_DEF_LVL6_DEFAULT
1252104a77dSRoy Oursler         ISAL_DEF_LVL6_DEFAULT,
1262104a77dSRoy Oursler #else
1272104a77dSRoy Oursler         0,
1282104a77dSRoy Oursler #endif
1292104a77dSRoy Oursler #ifdef ISAL_DEF_LVL7_DEFAULT
1302104a77dSRoy Oursler         ISAL_DEF_LVL7_DEFAULT,
1312104a77dSRoy Oursler #else
1322104a77dSRoy Oursler         0,
1332104a77dSRoy Oursler #endif
1342104a77dSRoy Oursler #ifdef ISAL_DEF_LVL8_DEFAULT
1352104a77dSRoy Oursler         ISAL_DEF_LVL8_DEFAULT,
1362104a77dSRoy Oursler #else
1372104a77dSRoy Oursler         0,
1382104a77dSRoy Oursler #endif
1392104a77dSRoy Oursler #ifdef ISAL_DEF_LVL9_DEFAULT
1402104a77dSRoy Oursler         ISAL_DEF_LVL9_DEFAULT,
1412104a77dSRoy Oursler #else
1422104a77dSRoy Oursler         0,
1432104a77dSRoy Oursler #endif
1442104a77dSRoy Oursler };
1452104a77dSRoy Oursler 
1462104a77dSRoy Oursler struct cli_options {
1472104a77dSRoy Oursler         char *infile_name;
1482104a77dSRoy Oursler         size_t infile_name_len;
1492104a77dSRoy Oursler         char *outfile_name;
1502104a77dSRoy Oursler         size_t outfile_name_len;
1512104a77dSRoy Oursler         char *suffix;
1522104a77dSRoy Oursler         size_t suffix_len;
1532104a77dSRoy Oursler         int level;
1542104a77dSRoy Oursler         int mode;
1552104a77dSRoy Oursler         int use_stdout;
1562104a77dSRoy Oursler         int remove;
1572104a77dSRoy Oursler         int force;
1582104a77dSRoy Oursler         int quiet_level;
1592104a77dSRoy Oursler         int verbose_level;
160cd5de57fSRoy Oursler         int name;
1617177ff99SRoy Oursler         int test;
1620a7e3167SGreg Tucker         int threads;
1630a7e3167SGreg Tucker         uint8_t *in_buf;
1640a7e3167SGreg Tucker         uint8_t *out_buf;
1650a7e3167SGreg Tucker         uint8_t *level_buf;
1660a7e3167SGreg Tucker         size_t in_buf_size;
1670a7e3167SGreg Tucker         size_t out_buf_size;
1680a7e3167SGreg Tucker         size_t level_buf_size;
1692104a77dSRoy Oursler };
1702104a77dSRoy Oursler 
1712104a77dSRoy Oursler struct cli_options global_options;
1722104a77dSRoy Oursler 
173*cf610527SMarcel Cornu void
init_options(struct cli_options * options)174*cf610527SMarcel Cornu init_options(struct cli_options *options)
1752104a77dSRoy Oursler {
1762104a77dSRoy Oursler         options->infile_name = NULL;
1772104a77dSRoy Oursler         options->infile_name_len = 0;
1782104a77dSRoy Oursler         options->outfile_name = NULL;
1792104a77dSRoy Oursler         options->outfile_name_len = 0;
1802104a77dSRoy Oursler         options->suffix = NULL;
1812104a77dSRoy Oursler         options->suffix_len = 0;
1822104a77dSRoy Oursler         options->level = LEVEL_DEFAULT;
1832104a77dSRoy Oursler         options->mode = COMPRESS_MODE;
1842104a77dSRoy Oursler         options->use_stdout = false;
1852104a77dSRoy Oursler         options->remove = false;
1862104a77dSRoy Oursler         options->force = false;
1872104a77dSRoy Oursler         options->quiet_level = 0;
1882104a77dSRoy Oursler         options->verbose_level = 0;
189cd5de57fSRoy Oursler         options->name = NAME_DEFAULT;
1907177ff99SRoy Oursler         options->test = NO_TEST;
1910a7e3167SGreg Tucker         options->in_buf = NULL;
1920a7e3167SGreg Tucker         options->out_buf = NULL;
1930a7e3167SGreg Tucker         options->level_buf = NULL;
1940a7e3167SGreg Tucker         options->in_buf_size = 0;
1950a7e3167SGreg Tucker         options->out_buf_size = 0;
1960a7e3167SGreg Tucker         options->level_buf_size = 0;
1970a7e3167SGreg Tucker         options->threads = 1;
1982104a77dSRoy Oursler };
1992104a77dSRoy Oursler 
200*cf610527SMarcel Cornu int
is_interactive(void)201*cf610527SMarcel Cornu is_interactive(void)
2022104a77dSRoy Oursler {
2032104a77dSRoy Oursler         int ret;
2042104a77dSRoy Oursler         ret = !global_options.force && !global_options.quiet_level && isatty(fileno(stdin));
2052104a77dSRoy Oursler         return ret;
2062104a77dSRoy Oursler }
2072104a77dSRoy Oursler 
208*cf610527SMarcel Cornu size_t
get_filesize(FILE * fp)209*cf610527SMarcel Cornu get_filesize(FILE *fp)
2102104a77dSRoy Oursler {
2112104a77dSRoy Oursler         size_t file_size;
2122104a77dSRoy Oursler         fpos_t pos, pos_curr;
2132104a77dSRoy Oursler 
2142104a77dSRoy Oursler         fgetpos(fp, &pos_curr); /* Save current position */
2152104a77dSRoy Oursler #if defined(_WIN32) || defined(_WIN64)
2162104a77dSRoy Oursler         _fseeki64(fp, 0, SEEK_END);
2172104a77dSRoy Oursler #else
2182104a77dSRoy Oursler         fseeko(fp, 0, SEEK_END);
2192104a77dSRoy Oursler #endif
2202104a77dSRoy Oursler         fgetpos(fp, &pos);
2212104a77dSRoy Oursler         file_size = *(size_t *) &pos;
2222104a77dSRoy Oursler         fsetpos(fp, &pos_curr); /* Restore position */
2232104a77dSRoy Oursler 
2242104a77dSRoy Oursler         return file_size;
2252104a77dSRoy Oursler }
2262104a77dSRoy Oursler 
227*cf610527SMarcel Cornu int
get_posix_filetime(FILE * fp,uint32_t * time)228*cf610527SMarcel Cornu get_posix_filetime(FILE *fp, uint32_t *time)
2292104a77dSRoy Oursler {
2302104a77dSRoy Oursler         struct stat file_stats;
231a139dd73STomasz Kantecki         const int ret = fstat(fileno(fp), &file_stats);
232a139dd73STomasz Kantecki         if (time != NULL && ret == 0)
233a139dd73STomasz Kantecki                 *time = file_stats.st_mtime;
234a139dd73STomasz Kantecki         return ret;
2352104a77dSRoy Oursler }
2362104a77dSRoy Oursler 
237*cf610527SMarcel Cornu uint32_t
set_filetime(char * file_name,uint32_t posix_time)238*cf610527SMarcel Cornu set_filetime(char *file_name, uint32_t posix_time)
2392104a77dSRoy Oursler {
2402104a77dSRoy Oursler         struct utimbuf new_time;
2412104a77dSRoy Oursler         new_time.actime = posix_time;
2422104a77dSRoy Oursler         new_time.modtime = posix_time;
2432104a77dSRoy Oursler         return utime(file_name, &new_time);
2442104a77dSRoy Oursler }
2452104a77dSRoy Oursler 
246*cf610527SMarcel Cornu void
log_print(int log_type,char * format,...)247*cf610527SMarcel Cornu log_print(int log_type, char *format, ...)
2482104a77dSRoy Oursler {
2492104a77dSRoy Oursler         va_list args;
2502104a77dSRoy Oursler         va_start(args, format);
2512104a77dSRoy Oursler 
2522104a77dSRoy Oursler         switch (log_type) {
2532104a77dSRoy Oursler         case INFORM:
2542104a77dSRoy Oursler                 vfprintf(stdout, format, args);
2552104a77dSRoy Oursler                 break;
2562104a77dSRoy Oursler         case WARN:
2572104a77dSRoy Oursler                 if (global_options.quiet_level <= 0)
2582104a77dSRoy Oursler                         vfprintf(stderr, format, args);
2592104a77dSRoy Oursler                 break;
2602104a77dSRoy Oursler         case ERROR:
2612104a77dSRoy Oursler                 if (global_options.quiet_level <= 1)
2622104a77dSRoy Oursler                         vfprintf(stderr, format, args);
2632104a77dSRoy Oursler                 break;
2642104a77dSRoy Oursler         case VERBOSE:
2652104a77dSRoy Oursler                 if (global_options.verbose_level > 0)
2662104a77dSRoy Oursler                         vfprintf(stderr, format, args);
2672104a77dSRoy Oursler                 break;
2682104a77dSRoy Oursler         }
2692104a77dSRoy Oursler 
2702104a77dSRoy Oursler         va_end(args);
2712104a77dSRoy Oursler }
2722104a77dSRoy Oursler 
273*cf610527SMarcel Cornu int
usage(int exit_code)274*cf610527SMarcel Cornu usage(int exit_code)
2752104a77dSRoy Oursler {
2762104a77dSRoy Oursler         int log_type = exit_code ? WARN : INFORM;
2772104a77dSRoy Oursler         log_print(log_type,
278391db331SGreg Tucker                   "Usage: igzip [options] [infiles]\n\n"
279391db331SGreg Tucker                   "Options:\n"
2802104a77dSRoy Oursler                   " -h, --help           help, print this message\n"
2812104a77dSRoy Oursler                   " -#                   use compression level # with 0 <= # <= %d\n"
2822104a77dSRoy Oursler                   " -o  <file>           output file\n"
2832104a77dSRoy Oursler                   " -c, --stdout         write to stdout\n"
2842104a77dSRoy Oursler                   " -d, --decompress     decompress file\n"
2852104a77dSRoy Oursler                   " -z, --compress       compress file (default)\n"
286391db331SGreg Tucker                   " -f, --force          overwrite output without prompting\n"
2872104a77dSRoy Oursler                   "     --rm             remove source files after successful (de)compression\n"
2882104a77dSRoy Oursler                   " -k, --keep           keep source files (default)\n"
289391db331SGreg Tucker                   " -S, --suffix <.suf>  suffix to use while (de)compressing\n"
290391db331SGreg Tucker                   " -V, --version        show version number\n"
291391db331SGreg Tucker                   " -v, --verbose        verbose mode\n"
292cd5de57fSRoy Oursler                   " -N, --name           save/use file name and timestamp in compress/decompress\n"
293*cf610527SMarcel Cornu                   " -n, --no-name        do not save/use file name and timestamp in "
294*cf610527SMarcel Cornu                   "compress/decompress\n"
2957177ff99SRoy Oursler                   " -t, --test           test compressed file integrity\n"
2960a7e3167SGreg Tucker                   " -T, --threads <n>    use n threads to compress if enabled\n"
297391db331SGreg Tucker                   " -q, --quiet          suppress warnings\n\n"
298391db331SGreg Tucker                   "with no infile, or when infile is - , read standard input\n\n",
2992104a77dSRoy Oursler                   ISAL_DEF_MAX_LEVEL);
3002104a77dSRoy Oursler 
3012104a77dSRoy Oursler         exit(exit_code);
3022104a77dSRoy Oursler }
3032104a77dSRoy Oursler 
304*cf610527SMarcel Cornu void
print_version(void)305*cf610527SMarcel Cornu print_version(void)
3062104a77dSRoy Oursler {
3072104a77dSRoy Oursler         log_print(INFORM, "igzip command line interface %s\n", VERSION);
3082104a77dSRoy Oursler }
3092104a77dSRoy Oursler 
310*cf610527SMarcel Cornu void *
malloc_safe(size_t size)311*cf610527SMarcel Cornu malloc_safe(size_t size)
3122104a77dSRoy Oursler {
3132104a77dSRoy Oursler         void *ptr = NULL;
3142104a77dSRoy Oursler         if (size == 0)
3152104a77dSRoy Oursler                 return ptr;
3162104a77dSRoy Oursler 
3172104a77dSRoy Oursler         ptr = malloc(size);
3182104a77dSRoy Oursler         if (ptr == NULL) {
3192104a77dSRoy Oursler                 log_print(ERROR, "igzip: Failed to allocate memory\n");
3202104a77dSRoy Oursler                 exit(MALLOC_FAILED);
3212104a77dSRoy Oursler         }
3222104a77dSRoy Oursler 
3232104a77dSRoy Oursler         return ptr;
3242104a77dSRoy Oursler }
3252104a77dSRoy Oursler 
326*cf610527SMarcel Cornu FILE *
fopen_safe(const char * file_name,const char * mode)327*cf610527SMarcel Cornu fopen_safe(const char *file_name, const char *mode)
3282104a77dSRoy Oursler {
3292104a77dSRoy Oursler         FILE *file;
3302104a77dSRoy Oursler 
3312104a77dSRoy Oursler         /* Assumes write mode always starts with w */
3322104a77dSRoy Oursler         if (mode[0] == 'w') {
3332104a77dSRoy Oursler                 if (access(file_name, F_OK) == 0) {
334722144eeSTomasz Kantecki                         int answer = 0, tmp;
335722144eeSTomasz Kantecki 
3362104a77dSRoy Oursler                         log_print(WARN, "igzip: %s already exists;", file_name);
3372104a77dSRoy Oursler                         if (is_interactive()) {
3382104a77dSRoy Oursler                                 log_print(WARN, " do you wish to overwrite (y/n)?");
3392104a77dSRoy Oursler                                 answer = getchar();
3402104a77dSRoy Oursler 
3412104a77dSRoy Oursler                                 tmp = answer;
3422104a77dSRoy Oursler                                 while (tmp != '\n' && tmp != EOF)
3432104a77dSRoy Oursler                                         tmp = getchar();
3442104a77dSRoy Oursler 
3452104a77dSRoy Oursler                                 if (answer != 'y' && answer != 'Y') {
3462104a77dSRoy Oursler                                         log_print(WARN, "       not overwritten\n");
3472104a77dSRoy Oursler                                         return NULL;
3482104a77dSRoy Oursler                                 }
3492104a77dSRoy Oursler                         } else if (!global_options.force) {
3502104a77dSRoy Oursler                                 log_print(WARN, "       not overwritten\n");
3512104a77dSRoy Oursler                                 return NULL;
3522104a77dSRoy Oursler                         }
3532104a77dSRoy Oursler                 }
3542104a77dSRoy Oursler         }
3552104a77dSRoy Oursler 
3562104a77dSRoy Oursler         file = fopen(file_name, mode);
3572104a77dSRoy Oursler         if (!file) {
358722144eeSTomasz Kantecki                 const char *error_str = strerror(errno);
359722144eeSTomasz Kantecki 
360722144eeSTomasz Kantecki                 log_print(ERROR, "igzip: Failed to open %s : %s\n", file_name, error_str);
3612104a77dSRoy Oursler                 return NULL;
3622104a77dSRoy Oursler         }
3632104a77dSRoy Oursler 
3642104a77dSRoy Oursler         return file;
3652104a77dSRoy Oursler }
3662104a77dSRoy Oursler 
367*cf610527SMarcel Cornu size_t
fread_safe(void * buf,size_t word_size,size_t buf_size,FILE * in,char * file_name)368*cf610527SMarcel Cornu fread_safe(void *buf, size_t word_size, size_t buf_size, FILE *in, char *file_name)
3692104a77dSRoy Oursler {
3702104a77dSRoy Oursler         size_t read_size;
3712104a77dSRoy Oursler         read_size = fread(buf, word_size, buf_size, in);
3722104a77dSRoy Oursler         if (ferror(in)) {
373*cf610527SMarcel Cornu                 log_print(ERROR, "igzip: Error encountered while reading file %s\n", file_name);
3742104a77dSRoy Oursler                 exit(FILE_READ_ERROR);
3752104a77dSRoy Oursler         }
3762104a77dSRoy Oursler         return read_size;
3772104a77dSRoy Oursler }
3782104a77dSRoy Oursler 
379*cf610527SMarcel Cornu size_t
fwrite_safe(void * buf,size_t word_size,size_t buf_size,FILE * out,char * file_name)380*cf610527SMarcel Cornu fwrite_safe(void *buf, size_t word_size, size_t buf_size, FILE *out, char *file_name)
3812104a77dSRoy Oursler {
3822104a77dSRoy Oursler         size_t write_size;
3832104a77dSRoy Oursler         write_size = fwrite(buf, word_size, buf_size, out);
3842104a77dSRoy Oursler         if (ferror(out)) {
385*cf610527SMarcel Cornu                 log_print(ERROR, "igzip: Error encountered while writing to file %s\n", file_name);
3862104a77dSRoy Oursler                 exit(FILE_WRITE_ERROR);
3872104a77dSRoy Oursler         }
3882104a77dSRoy Oursler         return write_size;
3892104a77dSRoy Oursler }
3902104a77dSRoy Oursler 
391*cf610527SMarcel Cornu void
open_in_file(FILE ** in,char * infile_name)392*cf610527SMarcel Cornu open_in_file(FILE **in, char *infile_name)
3932104a77dSRoy Oursler {
3942104a77dSRoy Oursler         *in = NULL;
3952104a77dSRoy Oursler         if (infile_name == NULL)
3962104a77dSRoy Oursler                 *in = stdin;
3972104a77dSRoy Oursler         else
3982104a77dSRoy Oursler                 *in = fopen_safe(infile_name, "rb");
3996f3599c1SRoy Oursler }
4002104a77dSRoy Oursler 
401*cf610527SMarcel Cornu void
open_out_file(FILE ** out,char * outfile_name)402*cf610527SMarcel Cornu open_out_file(FILE **out, char *outfile_name)
4036f3599c1SRoy Oursler {
4046f3599c1SRoy Oursler         *out = NULL;
4052104a77dSRoy Oursler         if (global_options.use_stdout)
4062104a77dSRoy Oursler                 *out = stdout;
4072104a77dSRoy Oursler         else if (outfile_name != NULL)
4082104a77dSRoy Oursler                 *out = fopen_safe(outfile_name, "wb");
4092104a77dSRoy Oursler         else if (!isatty(fileno(stdout)) || global_options.force)
4102104a77dSRoy Oursler                 *out = stdout;
4112104a77dSRoy Oursler         else {
4122104a77dSRoy Oursler                 log_print(WARN, "igzip: No output location. Use -c to output to terminal\n");
4132104a77dSRoy Oursler                 exit(FILE_OPEN_ERROR);
4142104a77dSRoy Oursler         }
4152104a77dSRoy Oursler }
4162104a77dSRoy Oursler 
4170a7e3167SGreg Tucker #if defined(HAVE_THREADS)
4180a7e3167SGreg Tucker 
4190a7e3167SGreg Tucker #define MAX_THREADS  8
4200a7e3167SGreg Tucker #define MAX_JOBQUEUE 16 /* must be a power of 2 */
4210a7e3167SGreg Tucker 
422*cf610527SMarcel Cornu enum job_status { JOB_UNALLOCATED = 0, JOB_ALLOCATED, JOB_SUCCESS, JOB_FAIL };
4230a7e3167SGreg Tucker 
4240a7e3167SGreg Tucker struct thread_job {
4250a7e3167SGreg Tucker         uint8_t *next_in;
4260a7e3167SGreg Tucker         uint32_t avail_in;
4270a7e3167SGreg Tucker         uint8_t *next_out;
4280a7e3167SGreg Tucker         uint32_t avail_out;
4290a7e3167SGreg Tucker         uint32_t total_out;
4300a7e3167SGreg Tucker         uint32_t type;
4310a7e3167SGreg Tucker         uint32_t status;
4320a7e3167SGreg Tucker };
4330a7e3167SGreg Tucker struct thread_pool {
4340a7e3167SGreg Tucker         pthread_t threads[MAX_THREADS];
4350a7e3167SGreg Tucker         struct thread_job job[MAX_JOBQUEUE];
4360a7e3167SGreg Tucker         pthread_mutex_t mutex;
4370a7e3167SGreg Tucker         pthread_cond_t cond;
4380a7e3167SGreg Tucker         int head;
4390a7e3167SGreg Tucker         int tail;
4400a7e3167SGreg Tucker         int queue;
4410a7e3167SGreg Tucker         int shutdown;
4420a7e3167SGreg Tucker };
4430a7e3167SGreg Tucker 
4440a7e3167SGreg Tucker // Globals for thread pool
4450a7e3167SGreg Tucker struct thread_pool pool;
4460a7e3167SGreg Tucker 
447*cf610527SMarcel Cornu static inline int
pool_has_space(void)448*cf610527SMarcel Cornu pool_has_space(void)
4490a7e3167SGreg Tucker {
4500a7e3167SGreg Tucker         return ((pool.head + 1) % MAX_JOBQUEUE) != pool.tail;
4510a7e3167SGreg Tucker }
4520a7e3167SGreg Tucker 
453*cf610527SMarcel Cornu static inline int
pool_has_work(void)454*cf610527SMarcel Cornu pool_has_work(void)
4550a7e3167SGreg Tucker {
4560a7e3167SGreg Tucker         return (pool.queue != pool.head);
4570a7e3167SGreg Tucker }
4580a7e3167SGreg Tucker 
459*cf610527SMarcel Cornu int
pool_get_work(void)460*cf610527SMarcel Cornu pool_get_work(void)
4610a7e3167SGreg Tucker {
4620a7e3167SGreg Tucker         assert(pool.queue != pool.head);
4630a7e3167SGreg Tucker         pool.queue = (pool.queue + 1) % MAX_JOBQUEUE;
4640a7e3167SGreg Tucker         return pool.queue;
4650a7e3167SGreg Tucker }
4660a7e3167SGreg Tucker 
467*cf610527SMarcel Cornu int
pool_put_work(struct isal_zstream * stream)468*cf610527SMarcel Cornu pool_put_work(struct isal_zstream *stream)
4690a7e3167SGreg Tucker {
4700a7e3167SGreg Tucker         pthread_mutex_lock(&pool.mutex);
4710a7e3167SGreg Tucker         if (!pool_has_space() || pool.shutdown) {
4720a7e3167SGreg Tucker                 pthread_mutex_unlock(&pool.mutex);
4730a7e3167SGreg Tucker                 return 1;
4740a7e3167SGreg Tucker         }
4750a7e3167SGreg Tucker         int idx = (pool.head + 1) % MAX_JOBQUEUE;
4760a7e3167SGreg Tucker         pool.job[idx].next_in = stream->next_in;
4770a7e3167SGreg Tucker         pool.job[idx].avail_in = stream->avail_in;
4780a7e3167SGreg Tucker         pool.job[idx].next_out = stream->next_out;
4790a7e3167SGreg Tucker         pool.job[idx].avail_out = stream->avail_out;
4800a7e3167SGreg Tucker         pool.job[idx].status = JOB_ALLOCATED;
4810a7e3167SGreg Tucker         pool.job[idx].type = stream->end_of_stream == 0 ? 0 : 1;
4820a7e3167SGreg Tucker         pool.head = idx;
4830a7e3167SGreg Tucker         pthread_cond_signal(&pool.cond);
4840a7e3167SGreg Tucker         pthread_mutex_unlock(&pool.mutex);
4850a7e3167SGreg Tucker         return 0;
4860a7e3167SGreg Tucker }
4870a7e3167SGreg Tucker 
488*cf610527SMarcel Cornu void *
thread_worker(void * none)489*cf610527SMarcel Cornu thread_worker(void *none)
4900a7e3167SGreg Tucker {
4910a7e3167SGreg Tucker         struct isal_zstream wstream;
4920a7e3167SGreg Tucker         int check;
4930a7e3167SGreg Tucker         int work_idx;
4940a7e3167SGreg Tucker         int level = global_options.level;
4950a7e3167SGreg Tucker         int level_size = level_size_buf[level];
4960a7e3167SGreg Tucker         uint8_t *level_buf = malloc_safe(level_size);
4970a7e3167SGreg Tucker         log_print(VERBOSE, "Start worker\n");
4980a7e3167SGreg Tucker 
4990a7e3167SGreg Tucker         while (!pool.shutdown) {
5000a7e3167SGreg Tucker                 pthread_mutex_lock(&pool.mutex);
5010a7e3167SGreg Tucker                 while (!pool_has_work() && !pool.shutdown) {
5020a7e3167SGreg Tucker                         pthread_cond_wait(&pool.cond, &pool.mutex);
5030a7e3167SGreg Tucker                 }
5040a7e3167SGreg Tucker                 if (pool.shutdown) {
5050a7e3167SGreg Tucker                         pthread_mutex_unlock(&pool.mutex);
5060a7e3167SGreg Tucker                         continue;
5070a7e3167SGreg Tucker                 }
5080a7e3167SGreg Tucker 
5090a7e3167SGreg Tucker                 work_idx = pool_get_work();
5100a7e3167SGreg Tucker                 pthread_cond_signal(&pool.cond);
5110a7e3167SGreg Tucker                 pthread_mutex_unlock(&pool.mutex);
5120a7e3167SGreg Tucker 
5130a7e3167SGreg Tucker                 isal_deflate_stateless_init(&wstream);
5140a7e3167SGreg Tucker                 wstream.next_in = pool.job[work_idx].next_in;
5150a7e3167SGreg Tucker                 wstream.next_out = pool.job[work_idx].next_out;
5160a7e3167SGreg Tucker                 wstream.avail_in = pool.job[work_idx].avail_in;
5170a7e3167SGreg Tucker                 wstream.avail_out = pool.job[work_idx].avail_out;
5180a7e3167SGreg Tucker                 wstream.end_of_stream = pool.job[work_idx].type;
5190a7e3167SGreg Tucker                 wstream.flush = FULL_FLUSH;
5200a7e3167SGreg Tucker                 wstream.level = global_options.level;
5210a7e3167SGreg Tucker                 wstream.level_buf = level_buf;
5220a7e3167SGreg Tucker                 wstream.level_buf_size = level_size;
5230a7e3167SGreg Tucker 
5240a7e3167SGreg Tucker                 check = isal_deflate_stateless(&wstream);
525*cf610527SMarcel Cornu                 log_print(VERBOSE, "Worker finished job %d, out=%d\n", work_idx, wstream.total_out);
5260a7e3167SGreg Tucker 
5270a7e3167SGreg Tucker                 pool.job[work_idx].total_out = wstream.total_out;
5280a7e3167SGreg Tucker                 pool.job[work_idx].status = JOB_SUCCESS + check; // complete or fail
5290a7e3167SGreg Tucker                 if (check)
5300a7e3167SGreg Tucker                         break;
5310a7e3167SGreg Tucker         }
5320a7e3167SGreg Tucker         free(level_buf);
5330a7e3167SGreg Tucker         log_print(VERBOSE, "Worker quit\n");
5340a7e3167SGreg Tucker         pthread_exit(NULL);
5350a7e3167SGreg Tucker }
5360a7e3167SGreg Tucker 
537*cf610527SMarcel Cornu int
pool_create(void)538*cf610527SMarcel Cornu pool_create(void)
5390a7e3167SGreg Tucker {
5400a7e3167SGreg Tucker         int i;
5410a7e3167SGreg Tucker         int nthreads = global_options.threads - 1;
5420a7e3167SGreg Tucker         pool.head = 0;
5430a7e3167SGreg Tucker         pool.tail = 0;
5440a7e3167SGreg Tucker         pool.queue = 0;
5450a7e3167SGreg Tucker         pool.shutdown = 0;
5460a7e3167SGreg Tucker         for (i = 0; i < nthreads; i++)
5470a7e3167SGreg Tucker                 pthread_create(&pool.threads[i], NULL, thread_worker, NULL);
5480a7e3167SGreg Tucker 
5490a7e3167SGreg Tucker         log_print(VERBOSE, "Created %d pool threads\n", nthreads);
5500a7e3167SGreg Tucker         return 0;
5510a7e3167SGreg Tucker }
5520a7e3167SGreg Tucker 
553*cf610527SMarcel Cornu void
pool_quit(void)554*cf610527SMarcel Cornu pool_quit(void)
5550a7e3167SGreg Tucker {
5560a7e3167SGreg Tucker         int i;
5570a7e3167SGreg Tucker         pthread_mutex_lock(&pool.mutex);
5580a7e3167SGreg Tucker         pool.shutdown = 1;
5590a7e3167SGreg Tucker         pthread_mutex_unlock(&pool.mutex);
5600a7e3167SGreg Tucker         pthread_cond_broadcast(&pool.cond);
5610a7e3167SGreg Tucker         for (i = 0; i < global_options.threads - 1; i++)
5620a7e3167SGreg Tucker                 pthread_join(pool.threads[i], NULL);
5630a7e3167SGreg Tucker         log_print(VERBOSE, "Deleted %d pool threads\n", i);
5640a7e3167SGreg Tucker }
5650a7e3167SGreg Tucker 
5660a7e3167SGreg Tucker #endif // defined(HAVE_THREADS)
5670a7e3167SGreg Tucker 
568*cf610527SMarcel Cornu int
compress_file(void)569*cf610527SMarcel Cornu compress_file(void)
5702104a77dSRoy Oursler {
5712104a77dSRoy Oursler         FILE *in = NULL, *out = NULL;
5722104a77dSRoy Oursler         unsigned char *inbuf = NULL, *outbuf = NULL, *level_buf = NULL;
5732104a77dSRoy Oursler         size_t inbuf_size, outbuf_size;
5742104a77dSRoy Oursler         int level_size = 0;
5752104a77dSRoy Oursler         struct isal_zstream stream;
5762104a77dSRoy Oursler         struct isal_gzip_header gz_hdr;
5772104a77dSRoy Oursler         int ret, success = 0;
5782104a77dSRoy Oursler 
5791ba280faSGreg Tucker         char *infile_name = global_options.infile_name;
5801ba280faSGreg Tucker         char *outfile_name = global_options.outfile_name;
5811ba280faSGreg Tucker         char *allocated_name = NULL;
5822104a77dSRoy Oursler         char *suffix = global_options.suffix;
5832104a77dSRoy Oursler         size_t infile_name_len = global_options.infile_name_len;
5846f3599c1SRoy Oursler         size_t outfile_name_len = global_options.outfile_name_len;
5852104a77dSRoy Oursler         size_t suffix_len = global_options.suffix_len;
5866f3599c1SRoy Oursler 
5872104a77dSRoy Oursler         int level = global_options.level;
5882104a77dSRoy Oursler 
5892104a77dSRoy Oursler         if (suffix == NULL) {
5902104a77dSRoy Oursler                 suffix = default_suffixes[0];
5912104a77dSRoy Oursler                 suffix_len = default_suffixes_lens[0];
5922104a77dSRoy Oursler         }
5932104a77dSRoy Oursler 
594*cf610527SMarcel Cornu         if (infile_name_len == stdin_file_name_len && infile_name != NULL &&
5952104a77dSRoy Oursler             memcmp(infile_name, stdin_file_name, infile_name_len) == 0) {
5962104a77dSRoy Oursler                 infile_name = NULL;
5972104a77dSRoy Oursler                 infile_name_len = 0;
5982104a77dSRoy Oursler         }
5992104a77dSRoy Oursler 
6002104a77dSRoy Oursler         if (outfile_name == NULL && infile_name != NULL && !global_options.use_stdout) {
6016f3599c1SRoy Oursler                 outfile_name_len = infile_name_len + suffix_len;
6021ba280faSGreg Tucker                 allocated_name = malloc_safe(outfile_name_len + 1);
6031ba280faSGreg Tucker                 outfile_name = allocated_name;
6041ba280faSGreg Tucker                 strncpy(outfile_name, infile_name, infile_name_len + 1);
60508f021c4STomasz Kantecki                 strncat(outfile_name, suffix, suffix_len);
6062104a77dSRoy Oursler         }
6072104a77dSRoy Oursler 
6086f3599c1SRoy Oursler         open_in_file(&in, infile_name);
6096f3599c1SRoy Oursler         if (in == NULL)
6106f3599c1SRoy Oursler                 goto compress_file_cleanup;
6116f3599c1SRoy Oursler 
612*cf610527SMarcel Cornu         if (infile_name_len != 0 && infile_name_len == outfile_name_len && infile_name != NULL &&
613*cf610527SMarcel Cornu             outfile_name != NULL && strncmp(infile_name, outfile_name, infile_name_len) == 0) {
6146f3599c1SRoy Oursler                 log_print(ERROR, "igzip: Error input and output file names must differ\n");
6156f3599c1SRoy Oursler                 goto compress_file_cleanup;
6166f3599c1SRoy Oursler         }
6176f3599c1SRoy Oursler 
6186f3599c1SRoy Oursler         open_out_file(&out, outfile_name);
6196f3599c1SRoy Oursler         if (out == NULL)
6202104a77dSRoy Oursler                 goto compress_file_cleanup;
6212104a77dSRoy Oursler 
6220a7e3167SGreg Tucker         inbuf_size = global_options.in_buf_size;
6230a7e3167SGreg Tucker         outbuf_size = global_options.out_buf_size;
6242104a77dSRoy Oursler 
6250a7e3167SGreg Tucker         inbuf = global_options.in_buf;
6260a7e3167SGreg Tucker         outbuf = global_options.out_buf;
6270a7e3167SGreg Tucker         level_size = global_options.level_buf_size;
6280a7e3167SGreg Tucker         level_buf = global_options.level_buf;
6292104a77dSRoy Oursler 
6302104a77dSRoy Oursler         isal_gzip_header_init(&gz_hdr);
631cd5de57fSRoy Oursler         if (global_options.name == NAME_DEFAULT || global_options.name == YES_NAME) {
632a139dd73STomasz Kantecki                 if (get_posix_filetime(in, &gz_hdr.time) != 0)
633a139dd73STomasz Kantecki                         goto compress_file_cleanup;
6342104a77dSRoy Oursler                 gz_hdr.name = infile_name;
635cd5de57fSRoy Oursler         }
636cd5de57fSRoy Oursler         gz_hdr.os = UNIX;
6372104a77dSRoy Oursler         gz_hdr.name_buf_len = infile_name_len + 1;
6382104a77dSRoy Oursler 
6392104a77dSRoy Oursler         isal_deflate_init(&stream);
640f7628833SRoy Oursler         stream.avail_in = 0;
6412104a77dSRoy Oursler         stream.flush = NO_FLUSH;
6422104a77dSRoy Oursler         stream.level = level;
6432104a77dSRoy Oursler         stream.level_buf = level_buf;
6442104a77dSRoy Oursler         stream.level_buf_size = level_size;
6452104a77dSRoy Oursler         stream.gzip_flag = IGZIP_GZIP_NO_HDR;
6462104a77dSRoy Oursler         stream.next_out = outbuf;
6472104a77dSRoy Oursler         stream.avail_out = outbuf_size;
6482104a77dSRoy Oursler 
6492104a77dSRoy Oursler         isal_write_gzip_header(&stream, &gz_hdr);
6502104a77dSRoy Oursler 
6510a7e3167SGreg Tucker         if (global_options.threads > 1) {
6520a7e3167SGreg Tucker #if defined(HAVE_THREADS)
6530a7e3167SGreg Tucker                 int q;
6540a7e3167SGreg Tucker                 int end_of_stream = 0;
6550a7e3167SGreg Tucker                 uint32_t crc = 0;
6560a7e3167SGreg Tucker                 uint64_t total_in = 0;
6570a7e3167SGreg Tucker 
6580a7e3167SGreg Tucker                 // Write the header
6590a7e3167SGreg Tucker                 fwrite_safe(outbuf, 1, stream.total_out, out, outfile_name);
6600a7e3167SGreg Tucker 
6610a7e3167SGreg Tucker                 do {
6620a7e3167SGreg Tucker                         size_t nread;
6630a7e3167SGreg Tucker                         size_t inbuf_used = 0;
6640a7e3167SGreg Tucker                         size_t outbuf_used = 0;
6650a7e3167SGreg Tucker                         uint8_t *iptr = inbuf;
6660a7e3167SGreg Tucker                         uint8_t *optr = outbuf;
6670a7e3167SGreg Tucker 
6680a7e3167SGreg Tucker                         for (q = 0; q < MAX_JOBQUEUE - 1; q++) {
6690a7e3167SGreg Tucker                                 inbuf_used += BLOCK_SIZE;
6700a7e3167SGreg Tucker                                 outbuf_used += 2 * BLOCK_SIZE;
6710a7e3167SGreg Tucker                                 if (inbuf_used > inbuf_size || outbuf_used > outbuf_size)
6720a7e3167SGreg Tucker                                         break;
6730a7e3167SGreg Tucker 
6740a7e3167SGreg Tucker                                 nread = fread_safe(iptr, 1, BLOCK_SIZE, in, infile_name);
6750a7e3167SGreg Tucker                                 crc = crc32_gzip_refl(crc, iptr, nread);
6760a7e3167SGreg Tucker                                 end_of_stream = feof(in);
6770a7e3167SGreg Tucker                                 total_in += nread;
6780a7e3167SGreg Tucker                                 stream.next_in = iptr;
6790a7e3167SGreg Tucker                                 stream.next_out = optr;
6800a7e3167SGreg Tucker                                 stream.avail_in = nread;
6810a7e3167SGreg Tucker                                 stream.avail_out = 2 * BLOCK_SIZE;
6820a7e3167SGreg Tucker                                 stream.end_of_stream = end_of_stream;
6830a7e3167SGreg Tucker                                 ret = pool_put_work(&stream);
6840a7e3167SGreg Tucker                                 if (ret || end_of_stream)
6850a7e3167SGreg Tucker                                         break;
6860a7e3167SGreg Tucker 
6870a7e3167SGreg Tucker                                 iptr += BLOCK_SIZE;
6880a7e3167SGreg Tucker                                 optr += 2 * BLOCK_SIZE;
6890a7e3167SGreg Tucker                         }
6900a7e3167SGreg Tucker 
6910a7e3167SGreg Tucker                         while (pool.tail != pool.head) { // Unprocessed jobs
6920a7e3167SGreg Tucker                                 int t = (pool.tail + 1) % MAX_JOBQUEUE;
6930a7e3167SGreg Tucker                                 if (pool.job[t].status >= JOB_SUCCESS) { // Finished next
6940a7e3167SGreg Tucker                                         if (pool.job[t].status > JOB_SUCCESS) {
6950a7e3167SGreg Tucker                                                 success = 0;
6960a7e3167SGreg Tucker                                                 log_print(ERROR,
697*cf610527SMarcel Cornu                                                           "igzip: Error encountered while "
698*cf610527SMarcel Cornu                                                           "compressing file %s\n",
6990a7e3167SGreg Tucker                                                           infile_name);
7000a7e3167SGreg Tucker                                                 goto compress_file_cleanup;
7010a7e3167SGreg Tucker                                         }
702*cf610527SMarcel Cornu                                         fwrite_safe(pool.job[t].next_out, 1, pool.job[t].total_out,
703*cf610527SMarcel Cornu                                                     out, outfile_name);
7040a7e3167SGreg Tucker 
7050a7e3167SGreg Tucker                                         pool.job[t].total_out = 0;
7060a7e3167SGreg Tucker                                         pool.job[t].status = 0;
7070a7e3167SGreg Tucker                                         pool.tail = t;
7080a7e3167SGreg Tucker                                         pthread_cond_broadcast(&pool.cond);
7090a7e3167SGreg Tucker                                 }
7100a7e3167SGreg Tucker                                 // Pick up a job while we wait
7110a7e3167SGreg Tucker                                 pthread_mutex_lock(&pool.mutex);
7120a7e3167SGreg Tucker                                 if (!pool_has_work()) {
7130a7e3167SGreg Tucker                                         pthread_mutex_unlock(&pool.mutex);
7140a7e3167SGreg Tucker                                         continue;
7150a7e3167SGreg Tucker                                 }
7160a7e3167SGreg Tucker 
7170a7e3167SGreg Tucker                                 int work_idx = pool_get_work();
7180a7e3167SGreg Tucker                                 pthread_cond_signal(&pool.cond);
7190a7e3167SGreg Tucker                                 pthread_mutex_unlock(&pool.mutex);
7200a7e3167SGreg Tucker 
7210a7e3167SGreg Tucker                                 isal_deflate_stateless_init(&stream);
7220a7e3167SGreg Tucker                                 stream.next_in = pool.job[work_idx].next_in;
7230a7e3167SGreg Tucker                                 stream.next_out = pool.job[work_idx].next_out;
7240a7e3167SGreg Tucker                                 stream.avail_in = pool.job[work_idx].avail_in;
7250a7e3167SGreg Tucker                                 stream.avail_out = pool.job[work_idx].avail_out;
7260a7e3167SGreg Tucker                                 stream.end_of_stream = pool.job[work_idx].type;
7270a7e3167SGreg Tucker                                 stream.flush = FULL_FLUSH;
7280a7e3167SGreg Tucker                                 stream.level = global_options.level;
7290a7e3167SGreg Tucker                                 stream.level_buf = level_buf;
7300a7e3167SGreg Tucker                                 stream.level_buf_size = level_size;
7310a7e3167SGreg Tucker                                 int check = isal_deflate_stateless(&stream);
732*cf610527SMarcel Cornu                                 log_print(VERBOSE, "Self   finished job %d, out=%d\n", work_idx,
733*cf610527SMarcel Cornu                                           stream.total_out);
7340a7e3167SGreg Tucker                                 pool.job[work_idx].total_out = stream.total_out;
7350a7e3167SGreg Tucker                                 pool.job[work_idx].status = JOB_SUCCESS + check; // complete or fail
7360a7e3167SGreg Tucker                         }
7370a7e3167SGreg Tucker                 } while (!end_of_stream);
7380a7e3167SGreg Tucker 
7390a7e3167SGreg Tucker                 // Write gzip trailer
7401ba280faSGreg Tucker                 fwrite_safe(&crc, sizeof(uint32_t), 1, out, outfile_name);
7411ba280faSGreg Tucker                 fwrite_safe(&total_in, sizeof(uint32_t), 1, out, outfile_name);
7420a7e3167SGreg Tucker 
7430a7e3167SGreg Tucker #else // No compiled threading support but asked for threads > 1
7440a7e3167SGreg Tucker                 assert(1);
7450a7e3167SGreg Tucker #endif
7460a7e3167SGreg Tucker         } else { // Single thread
7472104a77dSRoy Oursler                 do {
7482104a77dSRoy Oursler                         if (stream.avail_in == 0) {
7492104a77dSRoy Oursler                                 stream.next_in = inbuf;
7502104a77dSRoy Oursler                                 stream.avail_in =
7512104a77dSRoy Oursler                                         fread_safe(stream.next_in, 1, inbuf_size, in, infile_name);
7522104a77dSRoy Oursler                                 stream.end_of_stream = feof(in);
7532104a77dSRoy Oursler                         }
7542104a77dSRoy Oursler 
7552104a77dSRoy Oursler                         if (stream.next_out == NULL) {
7562104a77dSRoy Oursler                                 stream.next_out = outbuf;
7572104a77dSRoy Oursler                                 stream.avail_out = outbuf_size;
7582104a77dSRoy Oursler                         }
7592104a77dSRoy Oursler 
7602104a77dSRoy Oursler                         ret = isal_deflate(&stream);
7612104a77dSRoy Oursler 
7622104a77dSRoy Oursler                         if (ret != ISAL_DECOMP_OK) {
7632104a77dSRoy Oursler                                 log_print(ERROR,
7642104a77dSRoy Oursler                                           "igzip: Error encountered while compressing file %s\n",
7652104a77dSRoy Oursler                                           infile_name);
7662104a77dSRoy Oursler                                 goto compress_file_cleanup;
7672104a77dSRoy Oursler                         }
7682104a77dSRoy Oursler 
7692104a77dSRoy Oursler                         fwrite_safe(outbuf, 1, stream.next_out - outbuf, out, outfile_name);
7702104a77dSRoy Oursler                         stream.next_out = NULL;
7712104a77dSRoy Oursler 
7722104a77dSRoy Oursler                 } while (!feof(in) || stream.avail_out == 0);
7730a7e3167SGreg Tucker         }
7740a7e3167SGreg Tucker 
7752104a77dSRoy Oursler         success = 1;
7762104a77dSRoy Oursler 
7772104a77dSRoy Oursler compress_file_cleanup:
7782104a77dSRoy Oursler         if (out != NULL && out != stdout)
7792104a77dSRoy Oursler                 fclose(out);
7802104a77dSRoy Oursler 
7812104a77dSRoy Oursler         if (in != NULL && in != stdin) {
7822104a77dSRoy Oursler                 fclose(in);
7832104a77dSRoy Oursler                 if (success && global_options.remove)
7842104a77dSRoy Oursler                         remove(infile_name);
7852104a77dSRoy Oursler         }
7862104a77dSRoy Oursler 
7871ba280faSGreg Tucker         if (allocated_name != NULL)
7881ba280faSGreg Tucker                 free(allocated_name);
7892104a77dSRoy Oursler 
7902104a77dSRoy Oursler         return (success == 0);
7912104a77dSRoy Oursler }
7922104a77dSRoy Oursler 
793*cf610527SMarcel Cornu int
decompress_file(void)794*cf610527SMarcel Cornu decompress_file(void)
7952104a77dSRoy Oursler {
7962104a77dSRoy Oursler         FILE *in = NULL, *out = NULL;
7972104a77dSRoy Oursler         unsigned char *inbuf = NULL, *outbuf = NULL;
7982104a77dSRoy Oursler         size_t inbuf_size, outbuf_size;
7992104a77dSRoy Oursler         struct inflate_state state;
8002104a77dSRoy Oursler         struct isal_gzip_header gz_hdr;
801cd5de57fSRoy Oursler         const int terminal = 0, implicit = 1, stripped = 2;
802cd5de57fSRoy Oursler         int ret = 0, success = 0, outfile_type = terminal;
8032104a77dSRoy Oursler 
8041ba280faSGreg Tucker         char *infile_name = global_options.infile_name;
8051ba280faSGreg Tucker         char *outfile_name = global_options.outfile_name;
8061ba280faSGreg Tucker         char *allocated_name = NULL;
8072104a77dSRoy Oursler         char *suffix = global_options.suffix;
8082104a77dSRoy Oursler         size_t infile_name_len = global_options.infile_name_len;
8092104a77dSRoy Oursler         size_t outfile_name_len = global_options.outfile_name_len;
8102104a77dSRoy Oursler         size_t suffix_len = global_options.suffix_len;
8112104a77dSRoy Oursler         int suffix_index = 0;
8122104a77dSRoy Oursler         uint32_t file_time;
8132104a77dSRoy Oursler 
814ae45f60eSGreg Tucker         // Allocate mem and setup to hold gzip header info
815*cf610527SMarcel Cornu         if (infile_name_len == stdin_file_name_len && infile_name != NULL &&
8162104a77dSRoy Oursler             memcmp(infile_name, stdin_file_name, infile_name_len) == 0) {
8172104a77dSRoy Oursler                 infile_name = NULL;
8182104a77dSRoy Oursler                 infile_name_len = 0;
8192104a77dSRoy Oursler         }
8202104a77dSRoy Oursler 
821cd5de57fSRoy Oursler         if (outfile_name == NULL && !global_options.use_stdout) {
822cd5de57fSRoy Oursler                 if (infile_name != NULL) {
823cd5de57fSRoy Oursler                         outfile_type = stripped;
824cd5de57fSRoy Oursler                         while (suffix_index <
825cd5de57fSRoy Oursler                                sizeof(default_suffixes) / sizeof(*default_suffixes)) {
8262104a77dSRoy Oursler                                 if (suffix == NULL) {
8272104a77dSRoy Oursler                                         suffix = default_suffixes[suffix_index];
8282104a77dSRoy Oursler                                         suffix_len = default_suffixes_lens[suffix_index];
8292104a77dSRoy Oursler                                         suffix_index++;
8302104a77dSRoy Oursler                                 }
8312104a77dSRoy Oursler 
8322104a77dSRoy Oursler                                 outfile_name_len = infile_name_len - suffix_len;
833*cf610527SMarcel Cornu                                 if (infile_name_len >= suffix_len &&
834*cf610527SMarcel Cornu                                     memcmp(infile_name + outfile_name_len, suffix, suffix_len) == 0)
8352104a77dSRoy Oursler                                         break;
8362104a77dSRoy Oursler                                 suffix = NULL;
8372104a77dSRoy Oursler                                 suffix_len = 0;
8382104a77dSRoy Oursler                         }
8392104a77dSRoy Oursler 
8407177ff99SRoy Oursler                         if (suffix == NULL && global_options.test == NO_TEST) {
8412104a77dSRoy Oursler                                 log_print(ERROR, "igzip: %s: unknown suffix -- ignored\n",
8422104a77dSRoy Oursler                                           infile_name);
8432104a77dSRoy Oursler                                 return 1;
8442104a77dSRoy Oursler                         }
845cd5de57fSRoy Oursler                 }
846cd5de57fSRoy Oursler                 if (global_options.name == YES_NAME) {
847cd5de57fSRoy Oursler                         outfile_name_len = 0;
848cd5de57fSRoy Oursler                         outfile_type = implicit;
849cd5de57fSRoy Oursler                 }
8501ba280faSGreg Tucker                 if (outfile_type != terminal) {
851*cf610527SMarcel Cornu                         allocated_name = malloc_safe(outfile_name_len >= MAX_FILEPATH_BUF
852*cf610527SMarcel Cornu                                                              ? outfile_name_len + 1
853*cf610527SMarcel Cornu                                                              : MAX_FILEPATH_BUF);
8541ba280faSGreg Tucker                         outfile_name = allocated_name;
8551ba280faSGreg Tucker                 }
8562104a77dSRoy Oursler         }
8572104a77dSRoy Oursler 
8586f3599c1SRoy Oursler         open_in_file(&in, infile_name);
8596f3599c1SRoy Oursler         if (in == NULL)
8606f3599c1SRoy Oursler                 goto decompress_file_cleanup;
8616f3599c1SRoy Oursler 
862a139dd73STomasz Kantecki         if (get_posix_filetime(in, &file_time) != 0)
863a139dd73STomasz Kantecki                 goto decompress_file_cleanup;
8642104a77dSRoy Oursler 
8650a7e3167SGreg Tucker         inbuf_size = global_options.in_buf_size;
8660a7e3167SGreg Tucker         outbuf_size = global_options.out_buf_size;
8670a7e3167SGreg Tucker         inbuf = global_options.in_buf;
8680a7e3167SGreg Tucker         outbuf = global_options.out_buf;
8692104a77dSRoy Oursler 
8702104a77dSRoy Oursler         isal_gzip_header_init(&gz_hdr);
871cd5de57fSRoy Oursler         if (outfile_type == implicit) {
872cd5de57fSRoy Oursler                 gz_hdr.name = outfile_name;
873cd5de57fSRoy Oursler                 gz_hdr.name_buf_len = MAX_FILEPATH_BUF;
874cd5de57fSRoy Oursler         }
875cd5de57fSRoy Oursler 
8762104a77dSRoy Oursler         isal_inflate_init(&state);
877ebab4454SRoy Oursler         state.crc_flag = ISAL_GZIP_NO_HDR_VER;
8782104a77dSRoy Oursler         state.next_in = inbuf;
8792104a77dSRoy Oursler         state.avail_in = fread_safe(state.next_in, 1, inbuf_size, in, infile_name);
8802104a77dSRoy Oursler 
881ae45f60eSGreg Tucker         // Actually read and save the header info
8822104a77dSRoy Oursler         ret = isal_read_gzip_header(&state, &gz_hdr);
8832104a77dSRoy Oursler         if (ret != ISAL_DECOMP_OK) {
8842104a77dSRoy Oursler                 log_print(ERROR, "igzip: Error invalid gzip header found for file %s\n",
8852104a77dSRoy Oursler                           infile_name);
8862104a77dSRoy Oursler                 goto decompress_file_cleanup;
8872104a77dSRoy Oursler         }
8882104a77dSRoy Oursler 
889cd5de57fSRoy Oursler         if (outfile_type == implicit)
890cd5de57fSRoy Oursler                 file_time = gz_hdr.time;
891cd5de57fSRoy Oursler 
892*cf610527SMarcel Cornu         if (outfile_name != NULL && infile_name != NULL &&
893*cf610527SMarcel Cornu             (outfile_type == stripped || (outfile_type == implicit && outfile_name[0] == 0))) {
894cd5de57fSRoy Oursler                 outfile_name_len = infile_name_len - suffix_len;
895cd5de57fSRoy Oursler                 memcpy(outfile_name, infile_name, outfile_name_len);
896cd5de57fSRoy Oursler                 outfile_name[outfile_name_len] = 0;
897cd5de57fSRoy Oursler         }
898cd5de57fSRoy Oursler 
899*cf610527SMarcel Cornu         if (infile_name_len != 0 && infile_name_len == outfile_name_len && infile_name != NULL &&
900*cf610527SMarcel Cornu             outfile_name != NULL && strncmp(infile_name, outfile_name, infile_name_len) == 0) {
901cd5de57fSRoy Oursler                 log_print(ERROR, "igzip: Error input and output file names must differ\n");
902cd5de57fSRoy Oursler                 goto decompress_file_cleanup;
903cd5de57fSRoy Oursler         }
904cd5de57fSRoy Oursler 
9057177ff99SRoy Oursler         if (global_options.test == NO_TEST) {
906cd5de57fSRoy Oursler                 open_out_file(&out, outfile_name);
907cd5de57fSRoy Oursler                 if (out == NULL)
908cd5de57fSRoy Oursler                         goto decompress_file_cleanup;
9097177ff99SRoy Oursler         }
910cd5de57fSRoy Oursler 
911ae45f60eSGreg Tucker         // Start reading in compressed data and decompress
9122104a77dSRoy Oursler         do {
9132104a77dSRoy Oursler                 if (state.avail_in == 0) {
9142104a77dSRoy Oursler                         state.next_in = inbuf;
915*cf610527SMarcel Cornu                         state.avail_in = fread_safe(state.next_in, 1, inbuf_size, in, infile_name);
9162104a77dSRoy Oursler                 }
9172104a77dSRoy Oursler 
9182104a77dSRoy Oursler                 state.next_out = outbuf;
9192104a77dSRoy Oursler                 state.avail_out = outbuf_size;
9202104a77dSRoy Oursler 
9212104a77dSRoy Oursler                 ret = isal_inflate(&state);
9222104a77dSRoy Oursler                 if (ret != ISAL_DECOMP_OK) {
923*cf610527SMarcel Cornu                         log_print(ERROR, "igzip: Error encountered while decompressing file %s\n",
9242104a77dSRoy Oursler                                   infile_name);
9252104a77dSRoy Oursler                         goto decompress_file_cleanup;
9262104a77dSRoy Oursler                 }
9272104a77dSRoy Oursler 
9287177ff99SRoy Oursler                 if (out != NULL)
9292104a77dSRoy Oursler                         fwrite_safe(outbuf, 1, state.next_out - outbuf, out, outfile_name);
9302104a77dSRoy Oursler 
931ae45f60eSGreg Tucker         } while (state.block_state != ISAL_BLOCK_FINISH // while not done
932ae45f60eSGreg Tucker                  && (!feof(in) || state.avail_out == 0) // and work to do
933ae45f60eSGreg Tucker         );
934ae45f60eSGreg Tucker 
935ae45f60eSGreg Tucker         // Add the following to look for and decode additional concatenated files
936ae45f60eSGreg Tucker         if (!feof(in) && state.avail_in == 0) {
937ae45f60eSGreg Tucker                 state.next_in = inbuf;
938ae45f60eSGreg Tucker                 state.avail_in = fread_safe(state.next_in, 1, inbuf_size, in, infile_name);
939ae45f60eSGreg Tucker         }
940ae45f60eSGreg Tucker 
941ae45f60eSGreg Tucker         while (state.avail_in > 0 && state.next_in[0] == 31) {
942ae45f60eSGreg Tucker                 // Look for magic numbers for gzip header. Follows the gzread() decision
943ae45f60eSGreg Tucker                 // whether to treat as trailing junk
944ae45f60eSGreg Tucker                 if (state.avail_in > 1 && state.next_in[1] != 139)
945ae45f60eSGreg Tucker                         break;
946ae45f60eSGreg Tucker 
947ae45f60eSGreg Tucker                 isal_inflate_reset(&state);
948ae45f60eSGreg Tucker                 state.crc_flag = ISAL_GZIP; // Let isal_inflate() process extra headers
949ae45f60eSGreg Tucker                 do {
950ae45f60eSGreg Tucker                         if (state.avail_in == 0 && !feof(in)) {
951ae45f60eSGreg Tucker                                 state.next_in = inbuf;
952ae45f60eSGreg Tucker                                 state.avail_in =
953ae45f60eSGreg Tucker                                         fread_safe(state.next_in, 1, inbuf_size, in, infile_name);
954ae45f60eSGreg Tucker                         }
955ae45f60eSGreg Tucker 
956ae45f60eSGreg Tucker                         state.next_out = outbuf;
957ae45f60eSGreg Tucker                         state.avail_out = outbuf_size;
958ae45f60eSGreg Tucker 
959ae45f60eSGreg Tucker                         ret = isal_inflate(&state);
960ae45f60eSGreg Tucker                         if (ret != ISAL_DECOMP_OK) {
961ae45f60eSGreg Tucker                                 log_print(ERROR,
962ae45f60eSGreg Tucker                                           "igzip: Error while decompressing extra concatenated"
963*cf610527SMarcel Cornu                                           "gzip files on %s\n",
964*cf610527SMarcel Cornu                                           infile_name);
965ae45f60eSGreg Tucker                                 goto decompress_file_cleanup;
966ae45f60eSGreg Tucker                         }
967ae45f60eSGreg Tucker 
968ae45f60eSGreg Tucker                         if (out != NULL)
969*cf610527SMarcel Cornu                                 fwrite_safe(outbuf, 1, state.next_out - outbuf, out, outfile_name);
970ae45f60eSGreg Tucker 
971*cf610527SMarcel Cornu                 } while (state.block_state != ISAL_BLOCK_FINISH &&
972*cf610527SMarcel Cornu                          (!feof(in) || state.avail_out == 0));
973ae45f60eSGreg Tucker 
974ae45f60eSGreg Tucker                 if (!feof(in) && state.avail_in == 0) {
975ae45f60eSGreg Tucker                         state.next_in = inbuf;
976*cf610527SMarcel Cornu                         state.avail_in = fread_safe(state.next_in, 1, inbuf_size, in, infile_name);
977ae45f60eSGreg Tucker                 }
978ae45f60eSGreg Tucker         }
9792104a77dSRoy Oursler 
9802104a77dSRoy Oursler         if (state.block_state != ISAL_BLOCK_FINISH)
9812104a77dSRoy Oursler                 log_print(ERROR, "igzip: Error %s does not contain a complete gzip file\n",
9822104a77dSRoy Oursler                           infile_name);
9832104a77dSRoy Oursler         else
9842104a77dSRoy Oursler                 success = 1;
9852104a77dSRoy Oursler 
9862104a77dSRoy Oursler decompress_file_cleanup:
9872104a77dSRoy Oursler         if (out != NULL && out != stdout) {
9882104a77dSRoy Oursler                 fclose(out);
9892104a77dSRoy Oursler                 if (success)
9902104a77dSRoy Oursler                         set_filetime(outfile_name, file_time);
9912104a77dSRoy Oursler         }
9922104a77dSRoy Oursler 
9932104a77dSRoy Oursler         if (in != NULL && in != stdin) {
9942104a77dSRoy Oursler                 fclose(in);
9952104a77dSRoy Oursler                 if (success && global_options.remove)
9962104a77dSRoy Oursler                         remove(infile_name);
9972104a77dSRoy Oursler         }
9982104a77dSRoy Oursler 
9991ba280faSGreg Tucker         if (allocated_name != NULL)
10001ba280faSGreg Tucker                 free(allocated_name);
10012104a77dSRoy Oursler 
10022104a77dSRoy Oursler         return (success == 0);
10032104a77dSRoy Oursler }
10042104a77dSRoy Oursler 
1005*cf610527SMarcel Cornu int
main(int argc,char * argv[])1006*cf610527SMarcel Cornu main(int argc, char *argv[])
10072104a77dSRoy Oursler {
10082104a77dSRoy Oursler         int c;
10090a7e3167SGreg Tucker         char optstring[] = "hcdz0123456789o:S:kfqVvNntT:";
10102104a77dSRoy Oursler         int long_only_flag;
10112104a77dSRoy Oursler         int ret = 0;
10122104a77dSRoy Oursler         int bad_option = 0;
10132104a77dSRoy Oursler         int bad_level = 0;
10142104a77dSRoy Oursler         int bad_c = 0;
10152104a77dSRoy Oursler 
1016*cf610527SMarcel Cornu         struct option long_options[] = { { "help", no_argument, NULL, 'h' },
10172104a77dSRoy Oursler                                          { "stdout", no_argument, NULL, 'c' },
10182104a77dSRoy Oursler                                          { "to-stdout", no_argument, NULL, 'c' },
10192104a77dSRoy Oursler                                          { "compress", no_argument, NULL, 'z' },
10202104a77dSRoy Oursler                                          { "decompress", no_argument, NULL, 'd' },
10212104a77dSRoy Oursler                                          { "uncompress", no_argument, NULL, 'd' },
10222104a77dSRoy Oursler                                          { "keep", no_argument, NULL, 'k' },
10232104a77dSRoy Oursler                                          { "rm", no_argument, &long_only_flag, RM },
10242104a77dSRoy Oursler                                          { "suffix", no_argument, NULL, 'S' },
10252104a77dSRoy Oursler                                          { "fast", no_argument, NULL, '1' },
10262104a77dSRoy Oursler                                          { "best", no_argument, NULL, '0' + ISAL_DEF_MAX_LEVEL },
10272104a77dSRoy Oursler                                          { "force", no_argument, NULL, 'f' },
10282104a77dSRoy Oursler                                          { "quiet", no_argument, NULL, 'q' },
10292104a77dSRoy Oursler                                          { "version", no_argument, NULL, 'V' },
10302104a77dSRoy Oursler                                          { "verbose", no_argument, NULL, 'v' },
10312104a77dSRoy Oursler                                          { "no-name", no_argument, NULL, 'n' },
10322104a77dSRoy Oursler                                          { "name", no_argument, NULL, 'N' },
1033cd5de57fSRoy Oursler                                          { "test", no_argument, NULL, 't' },
10340a7e3167SGreg Tucker                                          { "threads", required_argument, NULL, 'T' },
10357177ff99SRoy Oursler                                          /* Possible future extensions
10362104a77dSRoy Oursler                                             {"recursive, no_argument, NULL, 'r'},
10372104a77dSRoy Oursler                                             {"list", no_argument, NULL, 'l'},
10382104a77dSRoy Oursler                                             {"benchmark", optional_argument, NULL, 'b'},
10392104a77dSRoy Oursler                                             {"benchmark_end", required_argument, NULL, 'e'},
10402104a77dSRoy Oursler                                           */
1041*cf610527SMarcel Cornu                                          { 0, 0, 0, 0 } };
10422104a77dSRoy Oursler 
10432104a77dSRoy Oursler         init_options(&global_options);
10442104a77dSRoy Oursler 
10452104a77dSRoy Oursler         opterr = 0;
10462104a77dSRoy Oursler         while ((c = getopt_long(argc, argv, optstring, long_options, NULL)) != -1) {
10472104a77dSRoy Oursler                 if (c >= '0' && c <= '9') {
10482104a77dSRoy Oursler                         if (c > '0' + ISAL_DEF_MAX_LEVEL)
10492104a77dSRoy Oursler                                 bad_level = 1;
10502104a77dSRoy Oursler                         else
10512104a77dSRoy Oursler                                 global_options.level = c - '0';
10522104a77dSRoy Oursler 
10532104a77dSRoy Oursler                         continue;
10542104a77dSRoy Oursler                 }
10552104a77dSRoy Oursler 
10562104a77dSRoy Oursler                 switch (c) {
10572104a77dSRoy Oursler                 case 0:
10582104a77dSRoy Oursler                         switch (long_only_flag) {
10592104a77dSRoy Oursler                         case RM:
10602104a77dSRoy Oursler                                 global_options.remove = true;
10612104a77dSRoy Oursler                                 break;
10622104a77dSRoy Oursler                         default:
10632104a77dSRoy Oursler                                 bad_option = 1;
10642104a77dSRoy Oursler                                 bad_c = c;
10652104a77dSRoy Oursler                                 break;
10662104a77dSRoy Oursler                         }
10672104a77dSRoy Oursler                         break;
10682104a77dSRoy Oursler                 case 'o':
10692104a77dSRoy Oursler                         global_options.outfile_name = optarg;
10702104a77dSRoy Oursler                         global_options.outfile_name_len = strlen(global_options.outfile_name);
10712104a77dSRoy Oursler                         break;
10722104a77dSRoy Oursler                 case 'c':
10732104a77dSRoy Oursler                         global_options.use_stdout = true;
10742104a77dSRoy Oursler                         break;
10752104a77dSRoy Oursler                 case 'z':
10762104a77dSRoy Oursler                         global_options.mode = COMPRESS_MODE;
10772104a77dSRoy Oursler                         break;
10782104a77dSRoy Oursler                 case 'd':
10792104a77dSRoy Oursler                         global_options.mode = DECOMPRESS_MODE;
10802104a77dSRoy Oursler                         break;
10812104a77dSRoy Oursler                 case 'S':
10822104a77dSRoy Oursler                         global_options.suffix = optarg;
10832104a77dSRoy Oursler                         global_options.suffix_len = strlen(global_options.suffix);
10842104a77dSRoy Oursler                         break;
10852104a77dSRoy Oursler                 case 'k':
10862104a77dSRoy Oursler                         global_options.remove = false;
10872104a77dSRoy Oursler                         break;
10882104a77dSRoy Oursler                 case 'f':
10892104a77dSRoy Oursler                         global_options.force = true;
10902104a77dSRoy Oursler                         break;
10912104a77dSRoy Oursler                 case 'q':
10922104a77dSRoy Oursler                         global_options.quiet_level++;
10932104a77dSRoy Oursler                         break;
10942104a77dSRoy Oursler                 case 'v':
10952104a77dSRoy Oursler                         global_options.verbose_level++;
10962104a77dSRoy Oursler                         break;
10972104a77dSRoy Oursler                 case 'V':
10982104a77dSRoy Oursler                         print_version();
10992104a77dSRoy Oursler                         return 0;
1100cd5de57fSRoy Oursler                 case 'N':
1101cd5de57fSRoy Oursler                         global_options.name = YES_NAME;
1102cd5de57fSRoy Oursler                         break;
1103cd5de57fSRoy Oursler                 case 'n':
1104cd5de57fSRoy Oursler                         global_options.name = NO_NAME;
1105cd5de57fSRoy Oursler                         break;
11067177ff99SRoy Oursler                 case 't':
11077177ff99SRoy Oursler                         global_options.test = TEST;
11087177ff99SRoy Oursler                         global_options.mode = DECOMPRESS_MODE;
11097177ff99SRoy Oursler                         break;
11100a7e3167SGreg Tucker                 case 'T':
11110a7e3167SGreg Tucker #if defined(HAVE_THREADS)
11120a7e3167SGreg Tucker                         c = atoi(optarg);
11130a7e3167SGreg Tucker                         c = c > MAX_THREADS ? MAX_THREADS : c;
11140a7e3167SGreg Tucker                         c = c < 1 ? 1 : c;
11150a7e3167SGreg Tucker                         global_options.threads = c;
11160a7e3167SGreg Tucker #endif
11170a7e3167SGreg Tucker                         break;
11182104a77dSRoy Oursler                 case 'h':
11192104a77dSRoy Oursler                         usage(0);
11202104a77dSRoy Oursler                 default:
11212104a77dSRoy Oursler                         bad_option = 1;
11222104a77dSRoy Oursler                         bad_c = optopt;
11232104a77dSRoy Oursler                         break;
11242104a77dSRoy Oursler                 }
11252104a77dSRoy Oursler         }
11262104a77dSRoy Oursler 
11272104a77dSRoy Oursler         if (bad_option) {
11282104a77dSRoy Oursler                 log_print(ERROR, "igzip: invalid option ");
11292104a77dSRoy Oursler                 if (bad_c)
11302104a77dSRoy Oursler                         log_print(ERROR, "-%c\n", bad_c);
11312104a77dSRoy Oursler 
11322104a77dSRoy Oursler                 else
11332104a77dSRoy Oursler                         log_print(ERROR, ("\n"));
11342104a77dSRoy Oursler 
11352104a77dSRoy Oursler                 usage(BAD_OPTION);
11362104a77dSRoy Oursler         }
11372104a77dSRoy Oursler 
11382104a77dSRoy Oursler         if (bad_level) {
11392104a77dSRoy Oursler                 log_print(ERROR, "igzip: invalid compression level\n");
11402104a77dSRoy Oursler                 usage(BAD_LEVEL);
11412104a77dSRoy Oursler         }
11422104a77dSRoy Oursler 
11432104a77dSRoy Oursler         if (global_options.outfile_name && optind < argc - 1) {
11442104a77dSRoy Oursler                 log_print(ERROR,
11452104a77dSRoy Oursler                           "igzip: An output file may be specified with only one input file\n");
11462104a77dSRoy Oursler                 return 0;
11472104a77dSRoy Oursler         }
11482104a77dSRoy Oursler 
11490a7e3167SGreg Tucker         global_options.in_buf_size = BLOCK_SIZE;
11500a7e3167SGreg Tucker         global_options.out_buf_size = BLOCK_SIZE;
11510a7e3167SGreg Tucker 
11520a7e3167SGreg Tucker #if defined(HAVE_THREADS)
11530a7e3167SGreg Tucker         if (global_options.threads > 1) {
11540a7e3167SGreg Tucker                 global_options.in_buf_size += (BLOCK_SIZE * MAX_JOBQUEUE);
11550a7e3167SGreg Tucker                 global_options.out_buf_size += (BLOCK_SIZE * MAX_JOBQUEUE * 2);
11560a7e3167SGreg Tucker                 pool_create();
11570a7e3167SGreg Tucker         }
11580a7e3167SGreg Tucker #endif
11590a7e3167SGreg Tucker         global_options.in_buf = malloc_safe(global_options.in_buf_size);
11600a7e3167SGreg Tucker         global_options.out_buf = malloc_safe(global_options.out_buf_size);
11610a7e3167SGreg Tucker         global_options.level_buf_size = level_size_buf[global_options.level];
11620a7e3167SGreg Tucker         global_options.level_buf = malloc_safe(global_options.level_buf_size);
11630a7e3167SGreg Tucker 
11642104a77dSRoy Oursler         if (global_options.mode == COMPRESS_MODE) {
11652104a77dSRoy Oursler                 if (optind >= argc)
11660a7e3167SGreg Tucker                         ret |= compress_file();
11672104a77dSRoy Oursler                 while (optind < argc) {
11682104a77dSRoy Oursler                         global_options.infile_name = argv[optind];
11692104a77dSRoy Oursler                         global_options.infile_name_len = strlen(global_options.infile_name);
11702104a77dSRoy Oursler                         ret |= compress_file();
11712104a77dSRoy Oursler                         optind++;
11722104a77dSRoy Oursler                 }
11732104a77dSRoy Oursler 
11742104a77dSRoy Oursler         } else if (global_options.mode == DECOMPRESS_MODE) {
11752104a77dSRoy Oursler                 if (optind >= argc)
11760a7e3167SGreg Tucker                         ret |= decompress_file();
11772104a77dSRoy Oursler                 while (optind < argc) {
11782104a77dSRoy Oursler                         global_options.infile_name = argv[optind];
11792104a77dSRoy Oursler                         global_options.infile_name_len = strlen(global_options.infile_name);
11802104a77dSRoy Oursler                         ret |= decompress_file();
11812104a77dSRoy Oursler                         optind++;
11822104a77dSRoy Oursler                 }
11832104a77dSRoy Oursler         }
11840a7e3167SGreg Tucker #if defined(HAVE_THREADS)
11850a7e3167SGreg Tucker         if (global_options.threads > 1)
11860a7e3167SGreg Tucker                 pool_quit();
11870a7e3167SGreg Tucker #endif
11882104a77dSRoy Oursler 
11890a7e3167SGreg Tucker         free(global_options.in_buf);
11900a7e3167SGreg Tucker         free(global_options.out_buf);
11910a7e3167SGreg Tucker         free(global_options.level_buf);
11922104a77dSRoy Oursler         return ret;
11932104a77dSRoy Oursler }
1194