diff --git a/AUTHORS b/AUTHORS index 6b1c0dd..6db2128 100644 --- a/AUTHORS +++ b/AUTHORS @@ -1,4 +1,4 @@ Pdlzip was written by Antonio Diaz Diaz. -Pdlzip includes public domain (de)compression code from the LZMA SDK -(Software Development Kit) written by Igor Pavlov. +Pdlzip includes public domain compression/decompression code from the LZMA +SDK (Software Development Kit) written by Igor Pavlov. diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..57f0e34 --- /dev/null +++ b/COPYING @@ -0,0 +1,19 @@ + Pdlzip - LZMA lossless data compressor + Copyright (C) Antonio Diaz Diaz. + Pdlzip includes public domain (de)compression code from the LZMA SDK + (Software Development Kit) written by Igor Pavlov. + + This program is free software. Redistribution and use in source and + binary forms, with or without modification, are permitted provided + that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions, and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions, and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + 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. diff --git a/ChangeLog b/ChangeLog index d4fcfeb..74c2b8c 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,39 +1,107 @@ +2025-01-07 Antonio Diaz Diaz + + * Version 1.14 released. + * main.c (decompress): Return 2 if empty member in multimember file. + (Pp_free): New function. + * check.sh: Use 'cp' instead of 'cat'. + +2024-01-21 Antonio Diaz Diaz + + * Version 1.13 released. + * main.c: Reformat file diagnostics as 'PROGRAM: FILE: MESSAGE'. + (show_option_error): New function showing argument and option name. + (main): Make -o preserve date/mode/owner if 1 input file. + * lzip.h: Rename verify_* to check_*. + * configure, Makefile.in: New variable 'MAKEINFO'. + * INSTALL: Document use of CFLAGS+='--std=c99 -D_XOPEN_SOURCE=500'. + +2022-01-21 Antonio Diaz Diaz + + * Version 1.12 released. + * main.c (getnum): Show option name and valid range if error. + +2021-01-01 Antonio Diaz Diaz + + * Version 1.11 released. + * main.c (main): Report an error if a file name is empty. + Make '-o' behave like '-c', but writing to file instead of stdout. + Make '-c' and '-o' check whether the output is a terminal only once. + Do not open output if input is a terminal. + * Don't allow mixing different operations (-d and -t). + * Replace 'decompressed', 'compressed' with 'out', 'in' in output. + * Document extraction from tar.lz in '--help' output and man page. + * main.c: Set a valid invocation_name even if argc == 0. + * testsuite: Add 9 new test files. + +2019-01-01 Antonio Diaz Diaz + + * Version 1.10 released. + * Rename File_* to Lzip_*. + * main.c: Document option -0 and make it use a 64 KiB dict size. + * main.c (main): Check return value of close( infd ). + * main.c: Compile on DOS with DJGPP. + * configure: Accept appending to CFLAGS; 'CFLAGS+=OPTIONS'. + * INSTALL: Document use of CFLAGS+='-D __USE_MINGW_ANSI_STDIO'. + +2018-02-04 Antonio Diaz Diaz + + * Version 1.9 released. + * New option '--loose-trailing'. + * Replace 'bits/byte' with inverse compression ratio in output. + * main.c (decompress): Improve corrupt header detection to HD=3. + (main): Show final diagnostic when testing multiple files. + (set_c_outname): Do not add a second '.lz' to the arg of '-o'. + (lzip_decode): Show stored sizes also in hex. + Show dictionary size at verbosity level 4 (-vvvv). + +2017-04-12 Antonio Diaz Diaz + + * Version 1.8 released. + * main.c: Continue testing if any input file is a terminal. + * main.c (decompress): Improve detection of trailing data. + +2016-05-16 Antonio Diaz Diaz + + * Version 1.7 released. + * main.c: New option '-a, --trailing-error'. + * main.c (main): Delete '--output' file if infd is a terminal. + (main): Don't use stdin more than once. + * configure: Avoid warning on some shells when testing for gcc. + * check.sh: Require a POSIX shell. Don't check error messages. + 2015-05-26 Antonio Diaz Diaz * Version 1.6 released. * main.c (close_and_set_permissions): Behave like 'cp -p'. - * Makefile.in: Added new targets 'install*-compress'. + * Makefile.in: New targets 'install*-compress'. 2013-09-14 Antonio Diaz Diaz * Version 1.5 released. - * main.c (show_header): Do not show header version in lzip mode. + * main.c (show_header): Don't show header version in lzip mode. * Minor fixes. 2013-05-27 Antonio Diaz Diaz * Version 1.4 released. - * main.c: Added new option '-f, --force'. - * main.c: Added new option '-F, --recompress'. - * main.c: Added new option '-k, --keep'. - * main.c: Added new option '-o, --output'. - * main.c: Accept more than one file in command line. * Decompression time has been reduced by 5%. - * main.c: '--test' no longer needs '/dev/null'. - * Fixed return value of '-d' and '-t' in case of data error. - * main.c: Changed info shown at verbosity levels 2 and 3. - * Ignore option '-n, --threads' for compatibility with plzip. + * main.c: New options '-f, --force', '-F, --recompress', + '-k, --keep', and '-o, --output'. + Accept more than one file in command line. + (main): '--test' no longer needs '/dev/null'. + (main): Fix return value of '-d' and '-t' in case of data error. + Change info shown at verbosity levels 2 and 3. + (main): Ignore option '-n, --threads' for compatibility with plzip. * configure: Options now accept a separate argument. - * configure: 'datadir' renamed to 'datarootdir'. - * Makefile.in: Added new target 'install-as-lzip'. - * Makefile.in: Added new target 'install-bin'. + Rename 'datadir' to 'datarootdir'. + * Makefile.in: New targets 'install-as-lzip' and 'install-bin'. 2012-01-03 Antonio Diaz Diaz * Version 1.3 released. * Small change in '--help' output and man page. - * Changed quote characters in messages as advised by GNU Standards. - * main.c: Set stdin/stdout in binary mode on OS2. + * Change quote characters in messages as advised by GNU Standards. + * main.c (main): Set stdin/stdout in binary mode on OS2. 2011-01-05 Antonio Diaz Diaz @@ -43,7 +111,7 @@ 2010-08-19 Antonio Diaz Diaz * Version 1.1 released. - * main.c: Added support for decompression of lzma-alone files. + * main.c: Add support for decompression of lzma-alone files. * main.c: Match length limit set by options -1 to -8 has been reduced to extend range of use towards gzip. Lower numbers now compress less but faster. (-1 now takes 43% less time for only @@ -57,8 +125,7 @@ * Using LZMA SDK 9.10 (public domain) from Igor Pavlov. -Copyright (C) 2010-2015 Antonio Diaz Diaz. +Copyright (C) 2010-2025 Antonio Diaz Diaz. -This file is a collection of facts, and thus it is not copyrightable, -but just in case, you have unlimited permission to copy, distribute and -modify it. +This file is a collection of facts, and thus it is not copyrightable, but just +in case, you have unlimited permission to copy, distribute, and modify it. diff --git a/INSTALL b/INSTALL index 489393d..e500eb9 100644 --- a/INSTALL +++ b/INSTALL @@ -1,9 +1,14 @@ Requirements ------------ -You will need a C compiler. -I use gcc 4.9.1 and 4.1.2, but the code should compile with any -standards compliant compiler. -Gcc is available at http://gcc.gnu.org. +You will need a C99 compiler. (gcc 3.3.6 or newer is recommended). +I use gcc 6.1.0 and 3.3.6, but the code should compile with any standards +compliant compiler. +Gcc is available at http://gcc.gnu.org +Lzip is available at http://www.nongnu.org/lzip/lzip.html + +The operating system must allow signal handlers read access to objects with +static storage duration so that the cleanup handler for Control-C can delete +the partial output file. Procedure @@ -14,8 +19,8 @@ Procedure or lzip -cd pdlzip[version].tar.lz | tar -xf - -This creates the directory ./pdlzip[version] containing the source from -the main archive. +This creates the directory ./pdlzip[version] containing the source code +extracted from the archive. 2. Change to pdlzip directory and run configure. (Try 'configure --help' for usage instructions). @@ -23,6 +28,14 @@ the main archive. cd pdlzip[version] ./configure + If you choose a C standard, enable the POSIX features explicitly: + + ./configure CFLAGS+='--std=c99 -D_XOPEN_SOURCE=500' + + If you are compiling on MinGW, use: + + ./configure CFLAGS+='-D __USE_MINGW_ANSI_STDIO' + 3. Run make. make @@ -30,14 +43,15 @@ the main archive. 4. Optionally, type 'make check' to run the tests that come with pdlzip. 5. Type 'make install' to install the program and any data files and - documentation. + documentation. You need root privileges to install into a prefix owned + by root. Or type 'make install-compress', which additionally compresses the - man page after installation. (Installing compressed docs may become - the default in the future). + man page after installation. + (Installing compressed docs may become the default in the future). - You can install only the program or the man page by typing 'make - install-bin' or 'make install-man' respectively. + You can install only the program or the man page by typing + 'make install-bin' or 'make install-man' respectively. Instead of 'make install', you can type 'make install-as-lzip' to install the program and any data files and documentation, and link @@ -47,21 +61,21 @@ the main archive. Another way ----------- You can also compile pdlzip into a separate directory. -To do this, you must use a version of 'make' that supports the 'VPATH' -variable, such as GNU 'make'. 'cd' to the directory where you want the +To do this, you must use a version of 'make' that supports the variable +'VPATH', such as GNU 'make'. 'cd' to the directory where you want the object files and executables to go and run the 'configure' script. -'configure' automatically checks for the source code in '.', in '..' and +'configure' automatically checks for the source code in '.', in '..', and in the directory that 'configure' is in. -'configure' recognizes the option '--srcdir=DIR' to control where to -look for the sources. Usually 'configure' can determine that directory +'configure' recognizes the option '--srcdir=DIR' to control where to look +for the source code. Usually 'configure' can determine that directory automatically. After running 'configure', you can run 'make' and 'make install' as explained above. -Copyright (C) 2010-2015 Antonio Diaz Diaz. +Copyright (C) 2010-2025 Antonio Diaz Diaz. This file is free documentation: you have unlimited permission to copy, -distribute and modify it. +distribute, and modify it. diff --git a/LzFind.c b/LzFind.c index 8e660f3..c312927 100644 --- a/LzFind.c +++ b/LzFind.c @@ -4,6 +4,7 @@ #define _FILE_OFFSET_BITS 64 #include +#include #include #include #include diff --git a/LzmaDec.c b/LzmaDec.c index cc4279d..bd2a45a 100644 --- a/LzmaDec.c +++ b/LzmaDec.c @@ -4,6 +4,7 @@ #define _FILE_OFFSET_BITS 64 #include +#include #include #include #include @@ -715,8 +716,8 @@ static bool LzmaDec_DecodeToDic(CLzmaDec *p, uint32_t dicLimit, *status = LZMA_STATUS_NEEDS_MORE_INPUT; return true; } - if (p->tempBuf[0] != 0) - return false; + /* check first byte of the LZMA stream */ + if (p->tempBuf[0] != 0) return false; LzmaDec_InitRc(p, p->tempBuf); p->tempBufSize = 0; diff --git a/LzmaEnc.c b/LzmaEnc.c index fb5df7a..f59c001 100644 --- a/LzmaEnc.c +++ b/LzmaEnc.c @@ -3,6 +3,7 @@ #define _FILE_OFFSET_BITS 64 +#include #include #include #include @@ -1351,7 +1352,7 @@ static uint32_t GetOptimumFast(CLzmaEnc *p, uint32_t *backRes) static void LZe_full_flush(CLzmaEnc *p, uint32_t posState) { const uint32_t len = LZMA_MATCH_LEN_MIN; - File_trailer trailer; + Lzip_trailer trailer; RangeEnc_EncodeBit(&p->rc, &p->isMatch[p->state][posState], 1); RangeEnc_EncodeBit(&p->rc, &p->isRep[p->state], 0); p->state = kMatchNextStates[p->state]; @@ -1361,23 +1362,23 @@ static void LZe_full_flush(CLzmaEnc *p, uint32_t posState) RcTree_ReverseEncode(&p->rc, p->posAlignEncoder, kNumAlignBits, kAlignMask); RangeEnc_FlushData(&p->rc); RangeEnc_FlushStream(&p->rc); - Ft_set_data_crc( trailer, p->matchFinderBase.crc ^ 0xFFFFFFFFU ); - Ft_set_data_size( trailer, p->nowPos64 ); - Ft_set_member_size( trailer, p->rc.processed + Fh_size + Ft_size ); - if( writeblock( p->rc.outfd, trailer, Ft_size ) != Ft_size ) + Lt_set_data_crc( trailer, p->matchFinderBase.crc ^ 0xFFFFFFFFU ); + Lt_set_data_size( trailer, p->nowPos64 ); + Lt_set_member_size( trailer, p->rc.processed + Lh_size + Lt_size ); + if( writeblock( p->rc.outfd, trailer, Lt_size ) != Lt_size ) p->rc.res = SZ_ERROR_WRITE; if( verbosity >= 1 ) { unsigned long long in_size = p->nowPos64; - unsigned long long out_size = p->rc.processed + Fh_size + Ft_size; - if( in_size <= 0 || out_size <= 0 ) - fprintf( stderr, " no data compressed.\n" ); + unsigned long long out_size = p->rc.processed + Lh_size + Lt_size; + if( in_size == 0 || out_size == 0 ) + fputs( " no data compressed.\n", stderr ); else - fprintf( stderr, "%6.3f:1, %6.3f bits/byte, " - "%5.2f%% saved, %llu in, %llu out.\n", + fprintf( stderr, "%6.3f:1, %5.2f%% ratio, %5.2f%% saved, " + "%llu in, %llu out.\n", (double)in_size / out_size, - ( 8.0 * out_size ) / in_size, - 100.0 * ( 1.0 - ( (double)out_size / in_size ) ), + ( 100.0 * out_size ) / in_size, + 100.0 - ( ( 100.0 * out_size ) / in_size ), in_size, out_size ); } } diff --git a/LzmaEnc.h b/LzmaEnc.h index 1b18df7..b607be8 100644 --- a/LzmaEnc.h +++ b/LzmaEnc.h @@ -5,7 +5,6 @@ /* ---------- CLzmaEncHandle Interface ---------- */ /* LzmaEnc_* functions can return the following exit codes: -Returns: SZ_OK - OK SZ_ERROR_WRITE - Write callback error. */ diff --git a/Makefile.in b/Makefile.in index 1b4ab5b..fc59256 100644 --- a/Makefile.in +++ b/Makefile.in @@ -2,9 +2,10 @@ DISTNAME = $(pkgname)-$(pkgversion) INSTALL = install INSTALL_PROGRAM = $(INSTALL) -m 755 -INSTALL_DATA = $(INSTALL) -m 644 INSTALL_DIR = $(INSTALL) -d -m 755 +INSTALL_DATA = $(INSTALL) -m 644 SHELL = /bin/sh +CAN_RUN_INSTALLINFO = $(SHELL) -c "install-info --version" > /dev/null 2>&1 objs = carg_parser.o LzFind.o LzmaEnc.o LzmaDec.o main.o @@ -12,13 +13,14 @@ objs = carg_parser.o LzFind.o LzmaEnc.o LzmaDec.o main.o .PHONY : all install install-bin install-info install-man \ install-strip install-compress install-strip-compress \ install-bin-strip install-info-compress install-man-compress \ - install-as-lzip uninstall uninstall-bin uninstall-info uninstall-man \ + install-as-lzip \ + uninstall uninstall-bin uninstall-info uninstall-man \ doc info man check dist clean distclean all : $(progname) $(progname) : $(objs) - $(CC) $(LDFLAGS) $(CFLAGS) -o $@ $(objs) + $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(objs) main.o : main.c $(CC) $(CPPFLAGS) $(CFLAGS) -DPROGVERSION=\"$(pkgversion)\" -c -o $@ $< @@ -26,6 +28,11 @@ main.o : main.c %.o : %.c $(CC) $(CPPFLAGS) $(CFLAGS) -c -o $@ $< +# prevent 'make' from trying to remake source files +$(VPATH)/configure $(VPATH)/Makefile.in $(VPATH)/doc/$(pkgname).texi : ; +MAKEFLAGS += -r +.SUFFIXES : + $(objs) : Makefile carg_parser.o : carg_parser.h LzmaDec.o : lzip.h LzmaDec.h @@ -33,13 +40,12 @@ LzFind.o : lzip.h LzFind.h LzmaEnc.o : lzip.h LzFind.h LzmaEnc.h main.o : carg_parser.h lzip.h LzmaDec.h LzmaEnc.h - doc : man info : $(VPATH)/doc/$(pkgname).info $(VPATH)/doc/$(pkgname).info : $(VPATH)/doc/$(pkgname).texi - cd $(VPATH)/doc && makeinfo $(pkgname).texi + cd $(VPATH)/doc && $(MAKEINFO) $(pkgname).texi man : $(VPATH)/doc/$(progname).1 @@ -68,7 +74,9 @@ install-info : if [ ! -d "$(DESTDIR)$(infodir)" ] ; then $(INSTALL_DIR) "$(DESTDIR)$(infodir)" ; fi -rm -f "$(DESTDIR)$(infodir)/$(pkgname).info"* $(INSTALL_DATA) $(VPATH)/doc/$(pkgname).info "$(DESTDIR)$(infodir)/$(pkgname).info" - -install-info --info-dir="$(DESTDIR)$(infodir)" "$(DESTDIR)$(infodir)/$(pkgname).info" + -if $(CAN_RUN_INSTALLINFO) ; then \ + install-info --info-dir="$(DESTDIR)$(infodir)" "$(DESTDIR)$(infodir)/$(pkgname).info" ; \ + fi install-info-compress : install-info lzip -v -9 "$(DESTDIR)$(infodir)/$(pkgname).info" @@ -91,7 +99,9 @@ uninstall-bin : -rm -f "$(DESTDIR)$(bindir)/$(progname)" uninstall-info : - -install-info --info-dir="$(DESTDIR)$(infodir)" --remove "$(DESTDIR)$(infodir)/$(pkgname).info" + -if $(CAN_RUN_INSTALLINFO) ; then \ + install-info --info-dir="$(DESTDIR)$(infodir)" --remove "$(DESTDIR)$(infodir)/$(pkgname).info" ; \ + fi -rm -f "$(DESTDIR)$(infodir)/$(pkgname).info"* uninstall-man : @@ -101,6 +111,7 @@ dist : doc ln -sf $(VPATH) $(DISTNAME) tar -Hustar --owner=root --group=root -cvf $(DISTNAME).tar \ $(DISTNAME)/AUTHORS \ + $(DISTNAME)/COPYING \ $(DISTNAME)/ChangeLog \ $(DISTNAME)/INSTALL \ $(DISTNAME)/Makefile.in \ @@ -108,12 +119,14 @@ dist : doc $(DISTNAME)/README \ $(DISTNAME)/configure \ $(DISTNAME)/doc/$(progname).1 \ + $(DISTNAME)/*.h \ + $(DISTNAME)/*.c \ $(DISTNAME)/testsuite/check.sh \ $(DISTNAME)/testsuite/test.txt \ + $(DISTNAME)/testsuite/fox.lz \ + $(DISTNAME)/testsuite/fox_*.lz \ $(DISTNAME)/testsuite/test.txt.lz \ - $(DISTNAME)/testsuite/test.txt.lzma \ - $(DISTNAME)/*.h \ - $(DISTNAME)/*.c + $(DISTNAME)/testsuite/test.txt.lzma rm -f $(DISTNAME) lzip -v -9 $(DISTNAME).tar diff --git a/NEWS b/NEWS index 12715c0..e715e5e 100644 --- a/NEWS +++ b/NEWS @@ -1,8 +1,4 @@ -Changes in version 1.6: +Changes in version 1.14: -Copying of file dates, permissions, and ownership now behaves like "cp -p". -(If the user ID or the group ID can't be duplicated, the file permission -bits S_ISUID and S_ISGID are cleared). - -The targets "install-compress", "install-strip-compress" and -"install-man-compress" have been added to the Makefile. +pdlzip now exits with error status 2 if any empty member is found in a +multimember file. diff --git a/README b/README index a7cbbd2..1e4ae50 100644 --- a/README +++ b/README @@ -1,59 +1,64 @@ +See the file INSTALL for compilation and installation instructions. + Description -Pdlzip is a lossless data compressor with a user interface similar to -the one of lzip, bzip2 or gzip. +Pdlzip is a permissively licensed implementation of the lzip data +compressor, intended for those who can't distribute (or even use) GPL +licensed Free Software. The name of pdlzip comes from 'public domain lzip'. +Pdlzip is written in C. -Pdlzip uses the lzip file format; the files produced by pdlzip are -(hope)fully compatible with lzip-1.4 or newer. Pdlzip is in fact a -"public domain" version of the lzip data compressor, intended for those -who can't distribute (or even use) GPL licensed Free Software. +Lzip is a lossless data compressor with a user interface similar to the one +of gzip or bzip2. Lzip uses a simplified form of LZMA (Lempel-Ziv-Markov +chain-Algorithm) designed to achieve complete interoperability between +implementations. The maximum dictionary size is 512 MiB so that any lzip +file can be decompressed on 32-bit machines. Lzip provides accurate and +robust 3-factor integrity checking. 'lzip -0' compresses about as fast as +gzip, while 'lzip -9' compresses most files more than bzip2. Decompression +speed is intermediate between gzip and bzip2. Lzip provides better data +recovery capabilities than gzip and bzip2. Lzip has been designed, written, +and tested with great care to replace gzip and bzip2 as general-purpose +compressed format for Unix-like systems. -The lzip file format is designed for data sharing and long-term -archiving, taking into account both data integrity and decoder -availability: +The lzip file format is designed for data sharing and long-term archiving, +taking into account both data integrity and decoder availability: - * The lzip format provides very safe integrity checking and some data - recovery means. The lziprecover program can repair bit-flip errors - (one of the most common forms of data corruption) in lzip files, - and provides data recovery capabilities, including error-checked - merging of damaged copies of a file. + * The program lziprecover can repair bit flip errors (one of the most + common forms of data corruption) in lzip files, and provides data + recovery capabilities, including error-checked merging of damaged + copies of a file. - * The lzip format is as simple as possible (but not simpler). The - lzip manual provides the code of a simple decompressor along with a - detailed explanation of how it works, so that with the only help of - the lzip manual it would be possible for a digital archaeologist to - extract the data from a lzip file long after quantum computers - eventually render LZMA obsolete. + * The lzip format is as simple as possible (but not simpler). The lzip + manual provides the source code of a simple decompressor along with a + detailed explanation of how it works, so that with the only help of the + lzip manual it would be possible for a digital archaeologist to extract + the data from a lzip file long after quantum computers eventually + render LZMA obsolete. * Additionally the lzip reference implementation is copylefted, which guarantees that it will remain free forever. -A nice feature of the lzip format is that a corrupt byte is easier to -repair the nearer it is from the beginning of the file. Therefore, with -the help of lziprecover, losing an entire archive just because of a -corrupt byte near the beginning is a thing of the past. +A nice feature of the lzip format is that a corrupt byte is easier to repair +the nearer it is from the beginning of the file. Therefore, with the help of +lziprecover, losing an entire archive just because of a corrupt byte near +the beginning is a thing of the past. Pdlzip is also able to decompress legacy lzma-alone (.lzma) files. -Lzma-alone is a very bad format. If you keep any lzma-alone files, it is -advisable to recompress them to lzip format. +Lzma-alone is a very bad format; it is essentially a raw LZMA stream. +If you keep any lzma-alone files, it is advisable to recompress them to +lzip format. Lziprecover can convert some lzma-alone files to lzip format +without recompressing. -Pdlzip is written in C. +Pdlzip includes public domain compression/decompression code from the LZMA +SDK (Software Development Kit) written by Igor Pavlov. -Pdlzip includes public domain (de)compression code from the LZMA SDK -(Software Development Kit) written by Igor Pavlov. - -I would not write non-copylefted software unless it is too simple to be -worth copylefting it, but one of the uses of the lzip format is the -interchange of information, and it is therefore desirable that even the -users of the most non-free platforms can share lzip files with everybody -else. +Pdlzip uses Arg_parser for command-line argument parsing: +http://www.nongnu.org/arg-parser/arg_parser.html -Copyright (C) 2010-2015 Antonio Diaz Diaz. +Copyright (C) 2010-2025 Antonio Diaz Diaz. This file is free documentation: you have unlimited permission to copy, -distribute and modify it. +distribute, and modify it. -The file Makefile.in is a data file used by configure to produce the -Makefile. It has the same copyright owner and permissions that configure -itself. +The file Makefile.in is a data file used by configure to produce the Makefile. +It has the same copyright owner and permissions that configure itself. diff --git a/carg_parser.c b/carg_parser.c index 8d74ea6..20b8a16 100644 --- a/carg_parser.c +++ b/carg_parser.c @@ -1,20 +1,20 @@ -/* Arg_parser - POSIX/GNU command line argument parser. (C version) - Copyright (C) 2006-2015 Antonio Diaz Diaz. +/* Arg_parser - POSIX/GNU command-line argument parser. (C version) + Copyright (C) 2006-2025 Antonio Diaz Diaz. - This library is free software. Redistribution and use in source and - binary forms, with or without modification, are permitted provided - that the following conditions are met: + This library is free software. Redistribution and use in source and + binary forms, with or without modification, are permitted provided + that the following conditions are met: - 1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions, and the following disclaimer. - 2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions, and the following disclaimer in the + documentation and/or other materials provided with the distribution. - This library 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. + This library 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. */ #include @@ -32,28 +32,46 @@ static void * ap_resize_buffer( void * buf, const int min_size ) } -static char push_back_record( struct Arg_parser * const ap, - const int code, const char * const argument ) +static char push_back_record( Arg_parser * const ap, const int code, + const char * const long_name, + const char * const argument ) { - const int len = strlen( argument ); - struct ap_Record * p; + ap_Record * p; void * tmp = ap_resize_buffer( ap->data, - ( ap->data_size + 1 ) * sizeof (struct ap_Record) ); + ( ap->data_size + 1 ) * sizeof (ap_Record) ); if( !tmp ) return 0; - ap->data = (struct ap_Record *)tmp; + ap->data = (ap_Record *)tmp; p = &(ap->data[ap->data_size]); p->code = code; - p->argument = 0; - tmp = ap_resize_buffer( p->argument, len + 1 ); - if( !tmp ) return 0; - p->argument = (char *)tmp; - strncpy( p->argument, argument, len + 1 ); + if( long_name ) + { + const int len = strlen( long_name ); + p->parsed_name = (char *)malloc( len + 2 + 1 ); + if( !p->parsed_name ) return 0; + p->parsed_name[0] = p->parsed_name[1] = '-'; + strncpy( p->parsed_name + 2, long_name, len + 1 ); + } + else if( code > 0 && code < 256 ) + { + p->parsed_name = (char *)malloc( 2 + 1 ); + if( !p->parsed_name ) return 0; + p->parsed_name[0] = '-'; p->parsed_name[1] = code; p->parsed_name[2] = 0; + } + else p->parsed_name = 0; + if( argument ) + { + const int len = strlen( argument ); + p->argument = (char *)malloc( len + 1 ); + if( !p->argument ) { free( p->parsed_name ); return 0; } + strncpy( p->argument, argument, len + 1 ); + } + else p->argument = 0; ++ap->data_size; return 1; } -static char add_error( struct Arg_parser * const ap, const char * const msg ) +static char add_error( Arg_parser * const ap, const char * const msg ) { const int len = strlen( msg ); void * tmp = ap_resize_buffer( ap->error, ap->error_size + len + 1 ); @@ -65,19 +83,20 @@ static char add_error( struct Arg_parser * const ap, const char * const msg ) } -static void free_data( struct Arg_parser * const ap ) +static void free_data( Arg_parser * const ap ) { int i; - for( i = 0; i < ap->data_size; ++i ) free( ap->data[i].argument ); + for( i = 0; i < ap->data_size; ++i ) + { free( ap->data[i].argument ); free( ap->data[i].parsed_name ); } if( ap->data ) { free( ap->data ); ap->data = 0; } ap->data_size = 0; } -static char parse_long_option( struct Arg_parser * const ap, +/* Return 0 only if out of memory. */ +static char parse_long_option( Arg_parser * const ap, const char * const opt, const char * const arg, - const struct ap_Option options[], - int * const argindp ) + const ap_Option options[], int * const argindp ) { unsigned len; int index = -1, i; @@ -87,14 +106,15 @@ static char parse_long_option( struct Arg_parser * const ap, /* Test all long options for either exact match or abbreviated matches. */ for( i = 0; options[i].code != 0; ++i ) - if( options[i].name && strncmp( options[i].name, &opt[2], len ) == 0 ) + if( options[i].long_name && + strncmp( options[i].long_name, &opt[2], len ) == 0 ) { - if( strlen( options[i].name ) == len ) /* Exact match found */ + if( strlen( options[i].long_name ) == len ) /* Exact match found */ { index = i; exact = 1; break; } else if( index < 0 ) index = i; /* First nonexact match found */ else if( options[index].code != options[i].code || options[index].has_arg != options[i].has_arg ) - ambig = 1; /* Second or later nonexact match found */ + ambig = 1; /* Second or later nonexact match found */ } if( ambig && !exact ) @@ -117,52 +137,55 @@ static char parse_long_option( struct Arg_parser * const ap, { if( options[index].has_arg == ap_no ) { - add_error( ap, "option '--" ); add_error( ap, options[index].name ); + add_error( ap, "option '--" ); add_error( ap, options[index].long_name ); add_error( ap, "' doesn't allow an argument" ); return 1; } if( options[index].has_arg == ap_yes && !opt[len+3] ) { - add_error( ap, "option '--" ); add_error( ap, options[index].name ); + add_error( ap, "option '--" ); add_error( ap, options[index].long_name ); add_error( ap, "' requires an argument" ); return 1; } - return push_back_record( ap, options[index].code, &opt[len+3] ); + return push_back_record( ap, options[index].code, options[index].long_name, + &opt[len+3] ); /* argument may be empty */ } - if( options[index].has_arg == ap_yes ) + if( options[index].has_arg == ap_yes || options[index].has_arg == ap_yme ) { - if( !arg || !arg[0] ) + if( !arg || ( options[index].has_arg == ap_yes && !arg[0] ) ) { - add_error( ap, "option '--" ); add_error( ap, options[index].name ); + add_error( ap, "option '--" ); add_error( ap, options[index].long_name ); add_error( ap, "' requires an argument" ); return 1; } ++*argindp; - return push_back_record( ap, options[index].code, arg ); + return push_back_record( ap, options[index].code, options[index].long_name, + arg ); /* argument may be empty */ } - return push_back_record( ap, options[index].code, "" ); + return push_back_record( ap, options[index].code, + options[index].long_name, 0 ); } -static char parse_short_option( struct Arg_parser * const ap, +/* Return 0 only if out of memory. */ +static char parse_short_option( Arg_parser * const ap, const char * const opt, const char * const arg, - const struct ap_Option options[], - int * const argindp ) + const ap_Option options[], int * const argindp ) { int cind = 1; /* character index in opt */ while( cind > 0 ) { int index = -1, i; - const unsigned char code = opt[cind]; + const unsigned char c = opt[cind]; char code_str[2]; - code_str[0] = code; code_str[1] = 0; + code_str[0] = c; code_str[1] = 0; - if( code != 0 ) + if( c != 0 ) for( i = 0; options[i].code; ++i ) - if( code == options[i].code ) + if( c == options[i].code ) { index = i; break; } if( index < 0 ) @@ -176,34 +199,34 @@ static char parse_short_option( struct Arg_parser * const ap, if( options[index].has_arg != ap_no && cind > 0 && opt[cind] ) { - if( !push_back_record( ap, code, &opt[cind] ) ) return 0; + if( !push_back_record( ap, c, 0, &opt[cind] ) ) return 0; ++*argindp; cind = 0; } - else if( options[index].has_arg == ap_yes ) + else if( options[index].has_arg == ap_yes || options[index].has_arg == ap_yme ) { - if( !arg || !arg[0] ) + if( !arg || ( options[index].has_arg == ap_yes && !arg[0] ) ) { add_error( ap, "option requires an argument -- '" ); add_error( ap, code_str ); add_error( ap, "'" ); return 1; } - ++*argindp; cind = 0; - if( !push_back_record( ap, code, arg ) ) return 0; + ++*argindp; cind = 0; /* argument may be empty */ + if( !push_back_record( ap, c, 0, arg ) ) return 0; } - else if( !push_back_record( ap, code, "" ) ) return 0; + else if( !push_back_record( ap, c, 0, 0 ) ) return 0; } return 1; } -char ap_init( struct Arg_parser * const ap, +char ap_init( Arg_parser * const ap, const int argc, const char * const argv[], - const struct ap_Option options[], const char in_order ) + const ap_Option options[], const char in_order ) { const char ** non_options = 0; /* skipped non-options */ int non_options_size = 0; /* number of skipped non-options */ int argind = 1; /* index in argv */ - int i; + char done = 0; /* false until success */ ap->data = 0; ap->error = 0; @@ -223,38 +246,41 @@ char ap_init( struct Arg_parser * const ap, if( ch2 == '-' ) { if( !argv[argind][2] ) { ++argind; break; } /* we found "--" */ - else if( !parse_long_option( ap, opt, arg, options, &argind ) ) return 0; + else if( !parse_long_option( ap, opt, arg, options, &argind ) ) goto out; } - else if( !parse_short_option( ap, opt, arg, options, &argind ) ) return 0; + else if( !parse_short_option( ap, opt, arg, options, &argind ) ) goto out; if( ap->error ) break; } else { - if( !in_order ) + if( in_order ) + { if( !push_back_record( ap, 0, 0, argv[argind++] ) ) goto out; } + else { void * tmp = ap_resize_buffer( non_options, ( non_options_size + 1 ) * sizeof *non_options ); - if( !tmp ) return 0; + if( !tmp ) goto out; non_options = (const char **)tmp; non_options[non_options_size++] = argv[argind++]; } - else if( !push_back_record( ap, 0, argv[argind++] ) ) return 0; } } if( ap->error ) free_data( ap ); else { + int i; for( i = 0; i < non_options_size; ++i ) - if( !push_back_record( ap, 0, non_options[i] ) ) return 0; + if( !push_back_record( ap, 0, 0, non_options[i] ) ) goto out; while( argind < argc ) - if( !push_back_record( ap, 0, argv[argind++] ) ) return 0; + if( !push_back_record( ap, 0, 0, argv[argind++] ) ) goto out; } - if( non_options ) free( non_options ); - return 1; + done = 1; +out: if( non_options ) free( non_options ); + return done; } -void ap_free( struct Arg_parser * const ap ) +void ap_free( Arg_parser * const ap ) { free_data( ap ); if( ap->error ) { free( ap->error ); ap->error = 0; } @@ -262,23 +288,26 @@ void ap_free( struct Arg_parser * const ap ) } -const char * ap_error( const struct Arg_parser * const ap ) - { return ap->error; } +const char * ap_error( const Arg_parser * const ap ) { return ap->error; } +int ap_arguments( const Arg_parser * const ap ) { return ap->data_size; } -int ap_arguments( const struct Arg_parser * const ap ) - { return ap->data_size; } - - -int ap_code( const struct Arg_parser * const ap, const int i ) +int ap_code( const Arg_parser * const ap, const int i ) { - if( i >= 0 && i < ap_arguments( ap ) ) return ap->data[i].code; - else return 0; + if( i < 0 || i >= ap_arguments( ap ) ) return 0; + return ap->data[i].code; } -const char * ap_argument( const struct Arg_parser * const ap, const int i ) +const char * ap_parsed_name( const Arg_parser * const ap, const int i ) { - if( i >= 0 && i < ap_arguments( ap ) ) return ap->data[i].argument; - else return ""; + if( i < 0 || i >= ap_arguments( ap ) || !ap->data[i].parsed_name ) return ""; + return ap->data[i].parsed_name; + } + + +const char * ap_argument( const Arg_parser * const ap, const int i ) + { + if( i < 0 || i >= ap_arguments( ap ) || !ap->data[i].argument ) return ""; + return ap->data[i].argument; } diff --git a/carg_parser.h b/carg_parser.h index ed4d9c5..28eabee 100644 --- a/carg_parser.h +++ b/carg_parser.h @@ -1,92 +1,101 @@ -/* Arg_parser - POSIX/GNU command line argument parser. (C version) - Copyright (C) 2006-2015 Antonio Diaz Diaz. +/* Arg_parser - POSIX/GNU command-line argument parser. (C version) + Copyright (C) 2006-2025 Antonio Diaz Diaz. - This library is free software. Redistribution and use in source and - binary forms, with or without modification, are permitted provided - that the following conditions are met: + This library is free software. Redistribution and use in source and + binary forms, with or without modification, are permitted provided + that the following conditions are met: - 1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions, and the following disclaimer. - 2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions, and the following disclaimer in the + documentation and/or other materials provided with the distribution. - This library 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. + This library 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. */ -/* Arg_parser reads the arguments in 'argv' and creates a number of - option codes, option arguments and non-option arguments. +/* Arg_parser reads the arguments in 'argv' and creates a number of + option codes, option arguments, and non-option arguments. - In case of error, 'ap_error' returns a non-null pointer to an error - message. + In case of error, 'ap_error' returns a non-null pointer to an error + message. - 'options' is an array of 'struct ap_Option' terminated by an element - containing a code which is zero. A null name means a short-only - option. A code value outside the unsigned char range means a - long-only option. + 'options' is an array of 'struct ap_Option' terminated by an element + containing a code which is zero. A null long_name means a short-only + option. A code value outside the unsigned char range means a long-only + option. - Arg_parser normally makes it appear as if all the option arguments - were specified before all the non-option arguments for the purposes - of parsing, even if the user of your program intermixed option and - non-option arguments. If you want the arguments in the exact order - the user typed them, call 'ap_init' with 'in_order' = true. + Arg_parser normally makes it appear as if all the option arguments + were specified before all the non-option arguments for the purposes + of parsing, even if the user of your program intermixed option and + non-option arguments. If you want the arguments in the exact order + the user typed them, call 'ap_init' with 'in_order' = true. - The argument '--' terminates all options; any following arguments are - treated as non-option arguments, even if they begin with a hyphen. + The argument '--' terminates all options; any following arguments are + treated as non-option arguments, even if they begin with a hyphen. - The syntax for optional option arguments is '-' - (without whitespace), or '--='. + The syntax of options with an optional argument is + '-' (without whitespace), or + '--='. + + The syntax of options with an empty argument is '- ""', + '-- ""', or '--=""'. */ #ifdef __cplusplus extern "C" { #endif -enum ap_Has_arg { ap_no, ap_yes, ap_maybe }; +/* ap_yme = yes but maybe empty */ +typedef enum ap_Has_arg { ap_no, ap_yes, ap_maybe, ap_yme } ap_Has_arg; -struct ap_Option +typedef struct ap_Option { int code; /* Short option letter or code ( code != 0 ) */ - const char * name; /* Long option name (maybe null) */ - enum ap_Has_arg has_arg; - }; + const char * long_name; /* Long option name (maybe null) */ + ap_Has_arg has_arg; + } ap_Option; -struct ap_Record +typedef struct ap_Record { int code; + char * parsed_name; char * argument; - }; + } ap_Record; -struct Arg_parser +typedef struct Arg_parser { - struct ap_Record * data; + ap_Record * data; char * error; int data_size; int error_size; - }; + } Arg_parser; -char ap_init( struct Arg_parser * const ap, +char ap_init( Arg_parser * const ap, const int argc, const char * const argv[], - const struct ap_Option options[], const char in_order ); + const ap_Option options[], const char in_order ); -void ap_free( struct Arg_parser * const ap ); +void ap_free( Arg_parser * const ap ); -const char * ap_error( const struct Arg_parser * const ap ); +const char * ap_error( const Arg_parser * const ap ); - /* The number of arguments parsed (may be different from argc) */ -int ap_arguments( const struct Arg_parser * const ap ); +/* The number of arguments parsed. May be different from argc. */ +int ap_arguments( const Arg_parser * const ap ); - /* If ap_code( i ) is 0, ap_argument( i ) is a non-option. - Else ap_argument( i ) is the option's argument (or empty). */ -int ap_code( const struct Arg_parser * const ap, const int i ); +/* If ap_code( i ) is 0, ap_argument( i ) is a non-option. + Else ap_argument( i ) is the option's argument (or empty). */ +int ap_code( const Arg_parser * const ap, const int i ); -const char * ap_argument( const struct Arg_parser * const ap, const int i ); +/* Full name of the option parsed (short or long). */ +const char * ap_parsed_name( const Arg_parser * const ap, const int i ); + +const char * ap_argument( const Arg_parser * const ap, const int i ); #ifdef __cplusplus } diff --git a/configure b/configure index 40db552..4f3fdd2 100755 --- a/configure +++ b/configure @@ -1,12 +1,12 @@ #! /bin/sh # configure script for Pdlzip - LZMA lossless data compressor -# Copyright (C) 2010-2015 Antonio Diaz Diaz. +# Copyright (C) 2010-2025 Antonio Diaz Diaz. # # This configure script is free software: you have unlimited permission -# to copy, distribute and modify it. +# to copy, distribute, and modify it. pkgname=pdlzip -pkgversion=1.6 +pkgversion=1.14 progname=pdlzip srctrigger=doc/${progname}.1 @@ -24,13 +24,10 @@ CC=gcc CPPFLAGS= CFLAGS='-Wall -W -O2' LDFLAGS= +MAKEINFO=makeinfo # checking whether we are using GNU C. -${CC} --version > /dev/null 2>&1 -if [ $? != 0 ] ; then - CC=cc - CFLAGS='-W -O2' -fi +/bin/sh -c "${CC} --version" > /dev/null 2>&1 || { CC=cc ; CFLAGS=-O2 ; } # Loop over all args args= @@ -42,22 +39,26 @@ while [ $# != 0 ] ; do shift # Add the argument quoted to args - args="${args} \"${option}\"" + if [ -z "${args}" ] ; then args="\"${option}\"" + else args="${args} \"${option}\"" ; fi # Split out the argument for options that take them case ${option} in - *=*) optarg=`echo ${option} | sed -e 's,^[^=]*=,,;s,/$,,'` ;; + *=*) optarg=`echo "${option}" | sed -e 's,^[^=]*=,,;s,/$,,'` ;; esac # Process the options case ${option} in --help | -h) - echo "Usage: configure [options]" + echo "Usage: $0 [OPTION]... [VAR=VALUE]..." echo - echo "Options: [defaults in brackets]" + echo "To assign makefile variables (e.g., CC, CFLAGS...), specify them as" + echo "arguments to configure in the form VAR=VALUE." + echo + echo "Options and variables: [defaults in brackets]" echo " -h, --help display this help and exit" echo " -V, --version output version information and exit" - echo " --srcdir=DIR find the sources in DIR [. or ..]" + echo " --srcdir=DIR find the source code in DIR [. or ..]" echo " --prefix=DIR install into DIR [${prefix}]" echo " --exec-prefix=DIR base directory for arch-dependent files [${exec_prefix}]" echo " --bindir=DIR user executables directory [${bindir}]" @@ -65,9 +66,11 @@ while [ $# != 0 ] ; do echo " --infodir=DIR info files directory [${infodir}]" echo " --mandir=DIR man pages directory [${mandir}]" echo " CC=COMPILER C compiler to use [${CC}]" - echo " CPPFLAGS=OPTIONS command line options for the preprocessor [${CPPFLAGS}]" - echo " CFLAGS=OPTIONS command line options for the C compiler [${CFLAGS}]" - echo " LDFLAGS=OPTIONS command line options for the linker [${LDFLAGS}]" + echo " CPPFLAGS=OPTIONS command-line options for the preprocessor [${CPPFLAGS}]" + echo " CFLAGS=OPTIONS command-line options for the C compiler [${CFLAGS}]" + echo " CFLAGS+=OPTIONS append options to the current value of CFLAGS" + echo " LDFLAGS=OPTIONS command-line options for the linker [${LDFLAGS}]" + echo " MAKEINFO=NAME makeinfo program to use [${MAKEINFO}]" echo exit 0 ;; --version | -V) @@ -90,10 +93,12 @@ while [ $# != 0 ] ; do --mandir=*) mandir=${optarg} ;; --no-create) no_create=yes ;; - CC=*) CC=${optarg} ;; - CPPFLAGS=*) CPPFLAGS=${optarg} ;; - CFLAGS=*) CFLAGS=${optarg} ;; - LDFLAGS=*) LDFLAGS=${optarg} ;; + CC=*) CC=${optarg} ;; + CPPFLAGS=*) CPPFLAGS=${optarg} ;; + CFLAGS=*) CFLAGS=${optarg} ;; + CFLAGS+=*) CFLAGS="${CFLAGS} ${optarg}" ;; + LDFLAGS=*) LDFLAGS=${optarg} ;; + MAKEINFO=*) MAKEINFO=${optarg} ;; --*) echo "configure: WARNING: unrecognized option: '${option}'" 1>&2 ;; @@ -104,7 +109,7 @@ while [ $# != 0 ] ; do exit 1 ;; esac - # Check if the option took a separate argument + # Check whether the option took a separate argument if [ "${arg2}" = yes ] ; then if [ $# != 0 ] ; then args="${args} \"$1\"" ; shift else echo "configure: Missing argument to '${option}'" 1>&2 @@ -113,19 +118,19 @@ while [ $# != 0 ] ; do fi done -# Find the source files, if location was not specified. +# Find the source code, if location was not specified. srcdirtext= if [ -z "${srcdir}" ] ; then srcdirtext="or . or .." ; srcdir=. if [ ! -r "${srcdir}/${srctrigger}" ] ; then srcdir=.. ; fi if [ ! -r "${srcdir}/${srctrigger}" ] ; then ## the sed command below emulates the dirname command - srcdir=`echo $0 | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'` + srcdir=`echo "$0" | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'` fi fi if [ ! -r "${srcdir}/${srctrigger}" ] ; then - echo "configure: Can't find sources in ${srcdir} ${srcdirtext}" 1>&2 + echo "configure: Can't find source code in ${srcdir} ${srcdirtext}" 1>&2 echo "configure: (At least ${srctrigger} is missing)." 1>&2 exit 1 fi @@ -139,13 +144,13 @@ if [ -z "${no_create}" ] ; then rm -f config.status cat > config.status << EOF #! /bin/sh -# This file was generated automatically by configure. Do not edit. +# This file was generated automatically by configure. Don't edit. # Run this file to recreate the current configuration. # # This script is free software: you have unlimited permission -# to copy, distribute and modify it. +# to copy, distribute, and modify it. -exec /bin/sh $0 ${args} --no-create +exec /bin/sh "$0" ${args} --no-create EOF chmod +x config.status fi @@ -162,14 +167,15 @@ echo "CC = ${CC}" echo "CPPFLAGS = ${CPPFLAGS}" echo "CFLAGS = ${CFLAGS}" echo "LDFLAGS = ${LDFLAGS}" +echo "MAKEINFO = ${MAKEINFO}" rm -f Makefile cat > Makefile << EOF # Makefile for Pdlzip - LZMA lossless data compressor -# Copyright (C) 2010-2015 Antonio Diaz Diaz. -# This file was generated automatically by configure. Do not edit. +# Copyright (C) 2010-2025 Antonio Diaz Diaz. +# This file was generated automatically by configure. Don't edit. # # This Makefile is free software: you have unlimited permission -# to copy, distribute and modify it. +# to copy, distribute, and modify it. pkgname = ${pkgname} pkgversion = ${pkgversion} @@ -185,6 +191,7 @@ CC = ${CC} CPPFLAGS = ${CPPFLAGS} CFLAGS = ${CFLAGS} LDFLAGS = ${LDFLAGS} +MAKEINFO = ${MAKEINFO} EOF cat "${srcdir}/Makefile.in" >> Makefile diff --git a/doc/pdlzip.1 b/doc/pdlzip.1 index 0c97f2c..81ab168 100644 --- a/doc/pdlzip.1 +++ b/doc/pdlzip.1 @@ -1,15 +1,33 @@ -.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.46.1. -.TH PDLZIP "1" "May 2015" "pdlzip 1.6" "User Commands" +.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.49.2. +.TH PDLZIP "1" "January 2025" "pdlzip 1.14" "User Commands" .SH NAME pdlzip \- reduces the size of files .SH SYNOPSIS .B pdlzip [\fI\,options\/\fR] [\fI\,files\/\fR] .SH DESCRIPTION -Pdlzip \- A "public domain" version of the lzip data compressor -also able to decompress legacy lzma\-alone (.lzma) files. -Lzma\-alone is a very bad format. If you keep any lzma\-alone files, it is -advisable to recompress them to lzip format. +Pdlzip is a permissively licensed implementation of the lzip data +compressor, intended for those who can't distribute (or even use) GPL +licensed Free Software. The name of pdlzip comes from 'public domain lzip'. +Pdlzip is written in C. +.PP +Lzip is a lossless data compressor with a user interface similar to the one +of gzip or bzip2. Lzip uses a simplified form of LZMA (Lempel\-Ziv\-Markov +chain\-Algorithm) designed to achieve complete interoperability between +implementations. The maximum dictionary size is 512 MiB so that any lzip +file can be decompressed on 32\-bit machines. Lzip provides accurate and +robust 3\-factor integrity checking. 'lzip \fB\-0\fR' compresses about as fast as +gzip, while 'lzip \fB\-9\fR' compresses most files more than bzip2. Decompression +speed is intermediate between gzip and bzip2. Lzip provides better data +recovery capabilities than gzip and bzip2. Lzip has been designed, written, +and tested with great care to replace gzip and bzip2 as general\-purpose +compressed format for Unix\-like systems. +.PP +Pdlzip is also able to decompress legacy lzma\-alone (.lzma) files. +Lzma\-alone is a very bad format; it is essentially a raw LZMA stream. +If you keep any lzma\-alone files, it is advisable to recompress them to +lzip format. Lziprecover can convert some lzma\-alone files to lzip format +without recompressing. .SH OPTIONS .TP \fB\-h\fR, \fB\-\-help\fR @@ -18,11 +36,14 @@ display this help and exit \fB\-V\fR, \fB\-\-version\fR output version information and exit .TP +\fB\-a\fR, \fB\-\-trailing\-error\fR +exit with error status if trailing data +.TP \fB\-c\fR, \fB\-\-stdout\fR -send output to standard output +write to standard output, keep input files .TP \fB\-d\fR, \fB\-\-decompress\fR -decompress +decompress, test compressed file integrity .TP \fB\-f\fR, \fB\-\-force\fR overwrite existing output files @@ -37,7 +58,7 @@ keep (don't delete) input files set match length limit in bytes [36] .TP \fB\-o\fR, \fB\-\-output=\fR -if reading stdin, place the output into +write to , keep input files .TP \fB\-q\fR, \fB\-\-quiet\fR suppress all messages @@ -51,35 +72,47 @@ test compressed file integrity \fB\-v\fR, \fB\-\-verbose\fR be verbose (a 2nd \fB\-v\fR gives more) .TP -\fB\-1\fR .. \fB\-9\fR +\fB\-0\fR .. \fB\-9\fR set compression level [default 6] .TP \fB\-\-fast\fR -alias for \fB\-1\fR +alias for \fB\-0\fR .TP \fB\-\-best\fR alias for \fB\-9\fR +.TP +\fB\-\-loose\-trailing\fR +allow trailing data seeming corrupt header .PP -If no file names are given, pdlzip compresses or decompresses -from standard input to standard output. +If no file names are given, or if a file is '\-', pdlzip compresses or +decompresses from standard input to standard output. Numbers may be followed by a multiplier: k = kB = 10^3 = 1000, Ki = KiB = 2^10 = 1024, M = 10^6, Mi = 2^20, G = 10^9, Gi = 2^30, etc... -The bidimensional parameter space of LZMA can't be mapped to a linear -scale optimal for all files. If your files are large, very repetitive, -etc, you may need to use the \fB\-\-match\-length\fR and \fB\-\-dictionary\-size\fR -options directly to achieve optimal performance. For example, \fB\-9m64\fR -usually compresses executables more (and faster) than \fB\-9\fR. +Dictionary sizes 12 to 27 are interpreted as powers of two, meaning 2^12 to +2^27 bytes. .PP -Exit status: 0 for a normal exit, 1 for environmental problems (file -not found, invalid flags, I/O errors, etc), 2 to indicate a corrupt or -invalid input file, 3 for an internal consistency error (eg, bug) which -caused pdlzip to panic. +The bidimensional parameter space of LZMA can't be mapped to a linear scale +optimal for all files. If your files are large, very repetitive, etc, you +may need to use the options \fB\-\-dictionary\-size\fR and \fB\-\-match\-length\fR directly +to achieve optimal performance. For example, \fB\-9m64\fR usually compresses +executables more (and faster) than \fB\-9\fR. +.PP +To extract all the files from archive 'foo.tar.lz', use the commands +\&'tar \fB\-xf\fR foo.tar.lz' or 'pdlzip \fB\-cd\fR foo.tar.lz | tar \fB\-xf\fR \-'. +.PP +Exit status: 0 for a normal exit, 1 for environmental problems +(file not found, invalid command\-line options, I/O errors, etc), 2 to +indicate a corrupt or invalid input file, 3 for an internal consistency +error (e.g., bug) which caused pdlzip to panic. +.PP +Pdlzip includes public domain compression/decompression code from the LZMA +SDK (Software Development Kit) written by Igor Pavlov. .SH "REPORTING BUGS" Report bugs to lzip\-bug@nongnu.org .br Pdlzip home page: http://www.nongnu.org/lzip/pdlzip.html .SH COPYRIGHT -Copyright \(co 2015 Antonio Diaz Diaz. +Copyright \(co 2025 Antonio Diaz Diaz. Public Domain 2009 Igor Pavlov. License 2\-clause BSD. .br diff --git a/lzip.h b/lzip.h index 3b8d172..7dc9ee3 100644 --- a/lzip.h +++ b/lzip.h @@ -1,42 +1,37 @@ -/* Pdlzip - LZMA lossless data compressor - Copyright (C) 2010-2015 Antonio Diaz Diaz. +/* Pdlzip - LZMA lossless data compressor + Copyright (C) 2010-2025 Antonio Diaz Diaz. - This program is free software. Redistribution and use in source and - binary forms, with or without modification, are permitted provided - that the following conditions are met: + This program is free software. Redistribution and use in source and + binary forms, with or without modification, are permitted provided + that the following conditions are met: - 1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions, and the following disclaimer. - 2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions, and the following disclaimer in the + documentation and/or other materials provided with the distribution. - 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. + 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. */ -#ifndef __cplusplus -enum Bool { false = 0, true = 1 }; -typedef enum Bool bool; -#endif - #ifndef max #define max(x,y) ((x) >= (y) ? (x) : (y)) #endif -#ifndef min - #define min(x,y) ((x) <= (y) ? (x) : (y)) -#endif typedef int State; enum { min_dictionary_bits = 12, - min_dictionary_size = 1 << min_dictionary_bits, + min_dictionary_size = 1 << min_dictionary_bits, /* >= modeled_distances */ max_dictionary_bits = 29, max_dictionary_size = 1 << max_dictionary_bits, + max_dictionary_bits_c = 27, /* kDicLogSizeMaxCompress */ + max_dictionary_size_c = 1 << max_dictionary_bits_c, literal_context_bits = 3, + literal_pos_state_bits = 0, /* not used */ pos_state_bits = 2, len_low_bits = 3, @@ -52,48 +47,6 @@ enum { min_match_len_limit = 5 }; -struct Pretty_print - { - const char * name; - const char * stdin_name; - unsigned longest_name; - bool first_post; - }; - -static inline void Pp_init( struct Pretty_print * const pp, - const char * const filenames[], const int num_filenames ) - { - unsigned stdin_name_len; - int i; - pp->name = 0; - pp->stdin_name = "(stdin)"; - pp->longest_name = 0; - pp->first_post = false; - stdin_name_len = strlen( pp->stdin_name ); - - for( i = 0; i < num_filenames; ++i ) - { - const char * const s = filenames[i]; - const unsigned len = (strcmp( s, "-" ) == 0) ? stdin_name_len : strlen( s ); - if( len > pp->longest_name ) pp->longest_name = len; - } - if( pp->longest_name == 0 ) pp->longest_name = stdin_name_len; - } - -static inline void Pp_set_name( struct Pretty_print * const pp, - const char * const filename ) - { - if( filename && filename[0] && strcmp( filename, "-" ) != 0 ) - pp->name = filename; - else pp->name = pp->stdin_name; - pp->first_post = true; - } - -static inline void Pp_reset( struct Pretty_print * const pp ) - { if( pp->name && pp->name[0] ) pp->first_post = true; } -void Pp_show_msg( struct Pretty_print * const pp, const char * const msg ); - - typedef uint32_t CRC32[256]; /* Table of CRCs of all 8-bit messages. */ extern CRC32 crc32; @@ -111,16 +64,24 @@ static inline void CRC32_init( void ) } } +/* about as fast as it is possible without messing with endianness */ static inline void CRC32_update_buf( uint32_t * const crc, const uint8_t * const buffer, const int size ) { int i; + uint32_t c = *crc; for( i = 0; i < size; ++i ) - *crc = crc32[(*crc^buffer[i])&0xFF] ^ ( *crc >> 8 ); + c = crc32[(c^buffer[i])&0xFF] ^ ( c >> 8 ); + *crc = c; } +static inline bool isvalid_ds( const unsigned dictionary_size ) + { return dictionary_size >= min_dictionary_size && + dictionary_size <= max_dictionary_size; } + + static inline int real_bits( unsigned value ) { int bits = 0; @@ -129,100 +90,125 @@ static inline int real_bits( unsigned value ) } -static const uint8_t magic_string[4] = { 0x4C, 0x5A, 0x49, 0x50 }; /* "LZIP" */ +static const uint8_t lzip_magic[4] = { 0x4C, 0x5A, 0x49, 0x50 }; /* "LZIP" */ -typedef uint8_t File_header[6]; /* 0-3 magic bytes */ +enum { Lh_size = 6 }; +typedef uint8_t Lzip_header[Lh_size]; /* 0-3 magic bytes */ /* 4 version */ - /* 5 coded_dict_size */ -enum { Fh_size = 6 }; + /* 5 coded dictionary size */ -static inline void Fh_set_magic( File_header data ) - { memcpy( data, magic_string, 4 ); data[4] = 1; } +static inline void Lh_set_magic( Lzip_header data ) + { memcpy( data, lzip_magic, 4 ); data[4] = 1; } -static inline bool Fh_verify_magic( const File_header data ) - { return ( memcmp( data, magic_string, 4 ) == 0 ); } +static inline bool Lh_check_magic( const Lzip_header data ) + { return memcmp( data, lzip_magic, 4 ) == 0; } -static inline uint8_t Fh_version( const File_header data ) +/* detect (truncated) header */ +static inline bool Lh_check_prefix( const Lzip_header data, const int sz ) + { + int i; for( i = 0; i < sz && i < 4; ++i ) + if( data[i] != lzip_magic[i] ) return false; + return sz > 0; + } + +/* detect corrupt header */ +static inline bool Lh_check_corrupt( const Lzip_header data ) + { + int matches = 0; + int i; for( i = 0; i < 4; ++i ) + if( data[i] == lzip_magic[i] ) ++matches; + return matches > 1 && matches < 4; + } + +static inline uint8_t Lh_version( const Lzip_header data ) { return data[4]; } -static inline bool Fh_verify_version( const File_header data ) - { return ( data[4] == 1 ); } +static inline bool Lh_check_version( const Lzip_header data ) + { return data[4] == 1; } -static inline unsigned Fh_get_dictionary_size( const File_header data ) +static inline unsigned Lh_get_dictionary_size( const Lzip_header data ) { - unsigned sz = ( 1 << ( data[5] & 0x1F ) ); + unsigned sz = 1 << ( data[5] & 0x1F ); if( sz > min_dictionary_size ) sz -= ( sz / 16 ) * ( ( data[5] >> 5 ) & 7 ); return sz; } -static inline bool Fh_set_dictionary_size( File_header data, const unsigned sz ) +static inline bool Lh_set_dictionary_size( Lzip_header data, const unsigned sz ) { - if( sz >= min_dictionary_size && sz <= max_dictionary_size ) + if( !isvalid_ds( sz ) ) return false; + data[5] = real_bits( sz - 1 ); + if( sz > min_dictionary_size ) { - data[5] = real_bits( sz - 1 ); - if( sz > min_dictionary_size ) - { - const unsigned base_size = 1 << data[5]; - const unsigned fraction = base_size / 16; - int i; - for( i = 7; i >= 1; --i ) - if( base_size - ( i * fraction ) >= sz ) - { data[5] |= ( i << 5 ); break; } - } - return true; + const unsigned base_size = 1 << data[5]; + const unsigned fraction = base_size / 16; + unsigned i; + for( i = 7; i >= 1; --i ) + if( base_size - ( i * fraction ) >= sz ) + { data[5] |= i << 5; break; } } - return false; + return true; } -typedef uint8_t File_trailer[20]; +enum { Lt_size = 20 }; +typedef uint8_t Lzip_trailer[Lt_size]; /* 0-3 CRC32 of the uncompressed data */ /* 4-11 size of the uncompressed data */ /* 12-19 member size including header and trailer */ -enum { Ft_size = 20 }; - -static inline unsigned Ft_get_data_crc( const File_trailer data ) +static inline unsigned Lt_get_data_crc( const Lzip_trailer data ) { unsigned tmp = 0; int i; for( i = 3; i >= 0; --i ) { tmp <<= 8; tmp += data[i]; } return tmp; } -static inline void Ft_set_data_crc( File_trailer data, unsigned crc ) +static inline void Lt_set_data_crc( Lzip_trailer data, unsigned crc ) { int i; for( i = 0; i <= 3; ++i ) { data[i] = (uint8_t)crc; crc >>= 8; } } -static inline unsigned long long Ft_get_data_size( const File_trailer data ) +static inline unsigned long long Lt_get_data_size( const Lzip_trailer data ) { unsigned long long tmp = 0; int i; for( i = 11; i >= 4; --i ) { tmp <<= 8; tmp += data[i]; } return tmp; } -static inline void Ft_set_data_size( File_trailer data, unsigned long long sz ) +static inline void Lt_set_data_size( Lzip_trailer data, unsigned long long sz ) { int i; for( i = 4; i <= 11; ++i ) { data[i] = (uint8_t)sz; sz >>= 8; } } -static inline unsigned long long Ft_get_member_size( const File_trailer data ) +static inline unsigned long long Lt_get_member_size( const Lzip_trailer data ) { unsigned long long tmp = 0; int i; for( i = 19; i >= 12; --i ) { tmp <<= 8; tmp += data[i]; } return tmp; } -static inline void Ft_set_member_size( File_trailer data, unsigned long long sz ) +static inline void Lt_set_member_size( Lzip_trailer data, unsigned long long sz ) { int i; for( i = 12; i <= 19; ++i ) { data[i] = (uint8_t)sz; sz >>= 8; } } -/* defined in main.c */ -int readblock( const int fd, uint8_t * const buf, const int size ); -int writeblock( const int fd, const uint8_t * const buf, const int size ); +typedef struct Cl_options /* command-line options */ + { + bool ignore_trailing; + bool loose_trailing; + } Cl_options; + +static inline void Cl_options_init( Cl_options * cl_opts ) + { cl_opts->ignore_trailing = true; cl_opts->loose_trailing = false; } + + +static inline void set_retval( int * retval, const int new_val ) + { if( *retval < new_val ) *retval = new_val; } + +static const char * const empty_msg = "Empty member not allowed."; +static const char * const mem_msg = "Not enough memory."; +static const char * const trailing_msg = "Trailing data not allowed."; /* defined in main.c */ extern int verbosity; -void cleanup_and_fail( const int retval ); -void show_error( const char * const msg, const int errcode, const bool help ); -void internal_error( const char * const msg ); +int readblock( const int fd, uint8_t * const buf, const int size ); +int writeblock( const int fd, const uint8_t * const buf, const int size ); #define SZ_OK 0 diff --git a/main.c b/main.c index 7972e7d..f224942 100644 --- a/main.c +++ b/main.c @@ -1,55 +1,61 @@ -/* Pdlzip - LZMA lossless data compressor - 2009-08-14 : Igor Pavlov : Public domain - Copyright (C) 2010-2015 Antonio Diaz Diaz. +/* Pdlzip - LZMA lossless data compressor + 2009-08-14 : Igor Pavlov : Public domain + Copyright (C) 2010-2025 Antonio Diaz Diaz. - This program is free software. Redistribution and use in source and - binary forms, with or without modification, are permitted provided - that the following conditions are met: + This program is free software. Redistribution and use in source and + binary forms, with or without modification, are permitted provided + that the following conditions are met: - 1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions, and the following disclaimer. - 2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions, and the following disclaimer in the + documentation and/or other materials provided with the distribution. - 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. + 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. */ /* - Exit status: 0 for a normal exit, 1 for environmental problems - (file not found, invalid flags, I/O errors, etc), 2 to indicate a - corrupt or invalid input file, 3 for an internal consistency error - (eg, bug) which caused pdlzip to panic. + Exit status: 0 for a normal exit, 1 for environmental problems + (file not found, invalid command-line options, I/O errors, etc), 2 to + indicate a corrupt or invalid input file, 3 for an internal consistency + error (e.g., bug) which caused pdlzip to panic. */ #define _FILE_OFFSET_BITS 64 #include #include -#include +#include /* CHAR_BIT, SSIZE_MAX */ #include -#include +#include +#include /* SIZE_MAX */ #include #include #include #include #include #include -#if defined(__MSVCRT__) +#if defined __MSVCRT__ || defined __OS2__ || defined __DJGPP__ #include +#if defined __MSVCRT__ #define fchmod(x,y) 0 #define fchown(x,y,z) 0 #define SIGHUP SIGTERM #define S_ISSOCK(x) 0 +#ifndef S_IRGRP #define S_IRGRP 0 #define S_IWGRP 0 #define S_IROTH 0 #define S_IWOTH 0 #endif -#if defined(__OS2__) -#include +#endif +#if defined __DJGPP__ +#define S_ISSOCK(x) 0 +#define S_ISVTX 0 +#endif #endif #include "carg_parser.h" @@ -65,72 +71,105 @@ #error "Environments where CHAR_BIT != 8 are not supported." #endif +#if ( defined SIZE_MAX && SIZE_MAX < UINT_MAX ) || \ + ( defined SSIZE_MAX && SSIZE_MAX < INT_MAX ) +#error "Environments where 'size_t' is narrower than 'int' are not supported." +#endif -const char * const Program_name = "Pdlzip"; -const char * const program_name = "pdlzip"; -const char * const program_year = "2015"; -const char * invocation_name = 0; +int verbosity = 0; +static void cleanup_and_fail( const int retval ); +static void show_error( const char * const msg, const int errcode, + const bool help ); +static void show_file_error( const char * const filename, + const char * const msg, const int errcode ); +static void internal_error( const char * const msg ); -struct { const char * from; const char * to; } const known_extensions[] = { +static const char * const program_name = "pdlzip"; +static const char * const program_year = "2025"; +static const char * invocation_name = "pdlzip"; /* default value */ + +static const struct { const char * from; const char * to; } known_extensions[] = { { ".lz", "" }, { ".tlz", ".tar" }, { ".lzma", "" }, { 0, 0 } }; -struct Lzma_options +typedef struct Lzma_options { int dictionary_size; /* 4 KiB .. 512 MiB */ int match_len_limit; /* 5 .. 273 */ - }; + } Lzma_options; -enum Mode { m_compress, m_decompress, m_test }; +typedef enum Mode { m_compress, m_decompress, m_test } Mode; -char * output_filename = 0; -int outfd = -1; -int verbosity = 0; -const mode_t usr_rw = S_IRUSR | S_IWUSR; -const mode_t all_rw = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH; -mode_t outfd_mode = S_IRUSR | S_IWUSR; -bool delete_output_on_interrupt = false; +/* Variables used in signal handler context. + They are not declared volatile because the handler never returns. */ +static char * output_filename = 0; +static int outfd = -1; +static bool delete_output_on_interrupt = false; static void show_help( void ) { - printf( "%s - A \"public domain\" version of the lzip data compressor\n", Program_name ); - printf( "also able to decompress legacy lzma-alone (.lzma) files.\n" - "Lzma-alone is a very bad format. If you keep any lzma-alone files, it is\n" - "advisable to recompress them to lzip format.\n" + printf( "Pdlzip is a permissively licensed implementation of the lzip data\n" + "compressor, intended for those who can't distribute (or even use) GPL\n" + "licensed Free Software. The name of pdlzip comes from 'public domain lzip'.\n" + "Pdlzip is written in C.\n" + "\nLzip is a lossless data compressor with a user interface similar to the one\n" + "of gzip or bzip2. Lzip uses a simplified form of LZMA (Lempel-Ziv-Markov\n" + "chain-Algorithm) designed to achieve complete interoperability between\n" + "implementations. The maximum dictionary size is 512 MiB so that any lzip\n" + "file can be decompressed on 32-bit machines. Lzip provides accurate and\n" + "robust 3-factor integrity checking. 'lzip -0' compresses about as fast as\n" + "gzip, while 'lzip -9' compresses most files more than bzip2. Decompression\n" + "speed is intermediate between gzip and bzip2. Lzip provides better data\n" + "recovery capabilities than gzip and bzip2. Lzip has been designed, written,\n" + "and tested with great care to replace gzip and bzip2 as general-purpose\n" + "compressed format for Unix-like systems.\n" + "\nPdlzip is also able to decompress legacy lzma-alone (.lzma) files.\n" + "Lzma-alone is a very bad format; it is essentially a raw LZMA stream.\n" + "If you keep any lzma-alone files, it is advisable to recompress them to\n" + "lzip format. Lziprecover can convert some lzma-alone files to lzip format\n" + "without recompressing.\n" "\nUsage: %s [options] [files]\n", invocation_name ); printf( "\nOptions:\n" " -h, --help display this help and exit\n" " -V, --version output version information and exit\n" - " -c, --stdout send output to standard output\n" - " -d, --decompress decompress\n" + " -a, --trailing-error exit with error status if trailing data\n" + " -c, --stdout write to standard output, keep input files\n" + " -d, --decompress decompress, test compressed file integrity\n" " -f, --force overwrite existing output files\n" " -F, --recompress force re-compression of compressed files\n" " -k, --keep keep (don't delete) input files\n" " -m, --match-length= set match length limit in bytes [36]\n" - " -o, --output= if reading stdin, place the output into \n" + " -o, --output= write to , keep input files\n" " -q, --quiet suppress all messages\n" " -s, --dictionary-size= set dictionary size limit in bytes [8 MiB]\n" " -t, --test test compressed file integrity\n" " -v, --verbose be verbose (a 2nd -v gives more)\n" - " -1 .. -9 set compression level [default 6]\n" - " --fast alias for -1\n" + " -0 .. -9 set compression level [default 6]\n" + " --fast alias for -0\n" " --best alias for -9\n" - "If no file names are given, pdlzip compresses or decompresses\n" - "from standard input to standard output.\n" + " --loose-trailing allow trailing data seeming corrupt header\n" + "\nIf no file names are given, or if a file is '-', pdlzip compresses or\n" + "decompresses from standard input to standard output.\n" "Numbers may be followed by a multiplier: k = kB = 10^3 = 1000,\n" "Ki = KiB = 2^10 = 1024, M = 10^6, Mi = 2^20, G = 10^9, Gi = 2^30, etc...\n" - "The bidimensional parameter space of LZMA can't be mapped to a linear\n" - "scale optimal for all files. If your files are large, very repetitive,\n" - "etc, you may need to use the --match-length and --dictionary-size\n" - "options directly to achieve optimal performance. For example, -9m64\n" - "usually compresses executables more (and faster) than -9.\n" - "\nExit status: 0 for a normal exit, 1 for environmental problems (file\n" - "not found, invalid flags, I/O errors, etc), 2 to indicate a corrupt or\n" - "invalid input file, 3 for an internal consistency error (eg, bug) which\n" - "caused pdlzip to panic.\n" + "Dictionary sizes 12 to 27 are interpreted as powers of two, meaning 2^12 to\n" + "2^27 bytes.\n" + "\nThe bidimensional parameter space of LZMA can't be mapped to a linear scale\n" + "optimal for all files. If your files are large, very repetitive, etc, you\n" + "may need to use the options --dictionary-size and --match-length directly\n" + "to achieve optimal performance. For example, -9m64 usually compresses\n" + "executables more (and faster) than -9.\n" + "\nTo extract all the files from archive 'foo.tar.lz', use the commands\n" + "'tar -xf foo.tar.lz' or 'pdlzip -cd foo.tar.lz | tar -xf -'.\n" + "\nExit status: 0 for a normal exit, 1 for environmental problems\n" + "(file not found, invalid command-line options, I/O errors, etc), 2 to\n" + "indicate a corrupt or invalid input file, 3 for an internal consistency\n" + "error (e.g., bug) which caused pdlzip to panic.\n" + "\nPdlzip includes public domain compression/decompression code from the LZMA\n" + "SDK (Software Development Kit) written by Igor Pavlov.\n" "\nReport bugs to lzip-bug@nongnu.org\n" "Pdlzip home page: http://www.nongnu.org/lzip/pdlzip.html\n" ); } @@ -147,45 +186,164 @@ static void show_version( void ) } -static void show_header( const unsigned dictionary_size ) +/* assure at least a minimum size for buffer 'buf' */ +static void * resize_buffer( void * buf, const unsigned min_size ) { - const char * const prefix[8] = - { "Ki", "Mi", "Gi", "Ti", "Pi", "Ei", "Zi", "Yi" }; - enum { factor = 1024 }; - const char * p = ""; - const char * np = " "; - unsigned num = dictionary_size, i; - bool exact = ( num % factor == 0 ); - - for( i = 0; i < 8 && ( num > 9999 || ( exact && num >= factor ) ); ++i ) - { num /= factor; if( num % factor != 0 ) exact = false; - p = prefix[i]; np = ""; } - fprintf( stderr, "dictionary size %s%4u %sB. ", np, num, p ); + if( buf ) buf = realloc( buf, min_size ); + else buf = malloc( min_size ); + if( !buf ) { show_error( mem_msg, 0, false ); cleanup_and_fail( 1 ); } + return buf; } -static unsigned long getnum( const char * const ptr, +typedef struct Pretty_print + { + const char * name; + char * padded_name; + const char * stdin_name; + unsigned longest_name; + bool first_post; + } Pretty_print; + +static void Pp_init( Pretty_print * const pp, + const char * const filenames[], const int num_filenames ) + { + pp->name = 0; + pp->padded_name = 0; + pp->stdin_name = "(stdin)"; + pp->longest_name = 0; + pp->first_post = false; + + if( verbosity <= 0 ) return; + const unsigned stdin_name_len = strlen( pp->stdin_name ); + int i; + for( i = 0; i < num_filenames; ++i ) + { + const char * const s = filenames[i]; + const unsigned len = (strcmp( s, "-" ) == 0) ? stdin_name_len : strlen( s ); + if( pp->longest_name < len ) pp->longest_name = len; + } + if( pp->longest_name == 0 ) pp->longest_name = stdin_name_len; + } + +void Pp_free( Pretty_print * const pp ) + { if( pp->padded_name ) { free( pp->padded_name ); pp->padded_name = 0; } } + +static void Pp_set_name( Pretty_print * const pp, const char * const filename ) + { + unsigned name_len, padded_name_len, i = 0; + + if( filename && filename[0] && strcmp( filename, "-" ) != 0 ) + pp->name = filename; + else pp->name = pp->stdin_name; + name_len = strlen( pp->name ); + padded_name_len = max( name_len, pp->longest_name ) + 4; + pp->padded_name = resize_buffer( pp->padded_name, padded_name_len + 1 ); + while( i < 2 ) pp->padded_name[i++] = ' '; + while( i < name_len + 2 ) { pp->padded_name[i] = pp->name[i-2]; ++i; } + pp->padded_name[i++] = ':'; + while( i < padded_name_len ) pp->padded_name[i++] = ' '; + pp->padded_name[i] = 0; + pp->first_post = true; + } + +static void Pp_reset( Pretty_print * const pp ) + { if( pp->name && pp->name[0] ) pp->first_post = true; } + +static void Pp_show_msg( Pretty_print * const pp, const char * const msg ) + { + if( verbosity < 0 ) return; + if( pp->first_post ) + { + pp->first_post = false; + fputs( pp->padded_name, stderr ); + if( !msg ) fflush( stderr ); + } + if( msg ) fprintf( stderr, "%s\n", msg ); + } + + +static void show_header( const unsigned dictionary_size ) + { + enum { factor = 1024, n = 3 }; + const char * const prefix[n] = { "Ki", "Mi", "Gi" }; + const char * p = ""; + const char * np = " "; + unsigned num = dictionary_size; + bool exact = num % factor == 0; + + int i; for( i = 0; i < n && ( num > 9999 || ( exact && num >= factor ) ); ++i ) + { num /= factor; if( num % factor != 0 ) exact = false; + p = prefix[i]; np = ""; } + fprintf( stderr, "dict %s%4u %sB, ", np, num, p ); + } + + +/* separate numbers of 5 or more digits in groups of 3 digits using '_' */ +static const char * format_num3( unsigned long long num ) + { + enum { buffers = 8, bufsize = 4 * sizeof num, n = 10 }; + const char * const si_prefix = "kMGTPEZYRQ"; + const char * const binary_prefix = "KMGTPEZYRQ"; + static char buffer[buffers][bufsize]; /* circle of static buffers for printf */ + static int current = 0; + int i; + char * const buf = buffer[current++]; current %= buffers; + char * p = buf + bufsize - 1; /* fill the buffer backwards */ + *p = 0; /* terminator */ + if( num > 9999 ) + { + char prefix = 0; /* try binary first, then si */ + for( i = 0; i < n && num != 0 && num % 1024 == 0; ++i ) + { num /= 1024; prefix = binary_prefix[i]; } + if( prefix ) *(--p) = 'i'; + else + for( i = 0; i < n && num != 0 && num % 1000 == 0; ++i ) + { num /= 1000; prefix = si_prefix[i]; } + if( prefix ) *(--p) = prefix; + } + const bool split = num >= 10000; + + for( i = 0; ; ) + { + *(--p) = num % 10 + '0'; num /= 10; if( num == 0 ) break; + if( split && ++i >= 3 ) { i = 0; *(--p) = '_'; } + } + return p; + } + + +void show_option_error( const char * const arg, const char * const msg, + const char * const option_name ) + { + if( verbosity >= 0 ) + fprintf( stderr, "%s: '%s': %s option '%s'.\n", + program_name, arg, msg, option_name ); + } + + +/* Recognized formats: k, Ki, [MGTPEZYRQ][i] */ +static unsigned long getnum( const char * const arg, + const char * const option_name, const unsigned long llimit, const unsigned long ulimit ) { - unsigned long result; char * tail; errno = 0; - result = strtoul( ptr, &tail, 0 ); - if( tail == ptr ) - { - show_error( "Bad or missing numerical argument.", 0, true ); - exit( 1 ); - } + unsigned long result = strtoul( arg, &tail, 0 ); + if( tail == arg ) + { show_option_error( arg, "Bad or missing numerical argument in", + option_name ); exit( 1 ); } if( !errno && tail[0] ) { - int factor = ( tail[1] == 'i' ) ? 1024 : 1000; - int exponent = 0, i; - bool bad_multiplier = false; + const unsigned factor = (tail[1] == 'i') ? 1024 : 1000; + int exponent = 0; /* 0 = bad multiplier */ + int i; switch( tail[0] ) { - case ' ': break; + case 'Q': exponent = 10; break; + case 'R': exponent = 9; break; case 'Y': exponent = 8; break; case 'Z': exponent = 7; break; case 'E': exponent = 6; break; @@ -193,17 +351,12 @@ static unsigned long getnum( const char * const ptr, case 'T': exponent = 4; break; case 'G': exponent = 3; break; case 'M': exponent = 2; break; - case 'K': if( factor == 1024 ) exponent = 1; else bad_multiplier = true; - break; - case 'k': if( factor == 1000 ) exponent = 1; else bad_multiplier = true; - break; - default : bad_multiplier = true; - } - if( bad_multiplier ) - { - show_error( "Bad multiplier in numerical argument.", 0, true ); - exit( 1 ); + case 'K': if( factor == 1024 ) exponent = 1; break; + case 'k': if( factor == 1000 ) exponent = 1; break; } + if( exponent <= 0 ) + { show_option_error( arg, "Bad multiplier in numerical argument of", + option_name ); exit( 1 ); } for( i = 0; i < exponent; ++i ) { if( ulimit / factor >= result ) result *= factor; @@ -213,120 +366,81 @@ static unsigned long getnum( const char * const ptr, if( !errno && ( result < llimit || result > ulimit ) ) errno = ERANGE; if( errno ) { - show_error( "Numerical argument out of limits.", 0, false ); + if( verbosity >= 0 ) + fprintf( stderr, "%s: '%s': Value out of limits [%s,%s] in " + "option '%s'.\n", program_name, arg, format_num3( llimit ), + format_num3( ulimit ), option_name ); exit( 1 ); } return result; } -static int get_dict_size( const char * const arg ) +static int get_dict_size( const char * const arg, const char * const option_name ) { char * tail; - const int bits = strtol( arg, &tail, 0 ); + const long bits = strtol( arg, &tail, 0 ); if( bits >= min_dictionary_bits && - bits <= max_dictionary_bits && *tail == 0 ) - return ( 1 << bits ); - return getnum( arg, min_dictionary_size, max_dictionary_size ); + bits <= max_dictionary_bits_c && *tail == 0 ) + return 1 << bits; + return getnum( arg, option_name, min_dictionary_size, max_dictionary_size_c ); + } + + +static void set_mode( Mode * const program_modep, const Mode new_mode ) + { + if( *program_modep != m_compress && *program_modep != new_mode ) + { + show_error( "Only one operation can be specified.", 0, true ); + exit( 1 ); + } + *program_modep = new_mode; } static int extension_index( const char * const name ) { - int i; - for( i = 0; known_extensions[i].from; ++i ) + int eindex; + for( eindex = 0; known_extensions[eindex].from; ++eindex ) { - const char * const ext = known_extensions[i].from; + const char * const ext = known_extensions[eindex].from; const unsigned name_len = strlen( name ); const unsigned ext_len = strlen( ext ); if( name_len > ext_len && strncmp( name + name_len - ext_len, ext, ext_len ) == 0 ) - return i; + return eindex; } return -1; } -static int open_instream( const char * const name, struct stat * const in_statsp, - const enum Mode program_mode, const int eindex, - const bool recompress, const bool to_stdout ) - { - int infd = -1; - if( program_mode == m_compress && !recompress && eindex >= 0 ) - { - if( verbosity >= 0 ) - fprintf( stderr, "%s: Input file '%s' already has '%s' suffix.\n", - program_name, name, known_extensions[eindex].from ); - } - else - { - infd = open( name, O_RDONLY | O_BINARY ); - if( infd < 0 ) - { - if( verbosity >= 0 ) - fprintf( stderr, "%s: Can't open input file '%s': %s.\n", - program_name, name, strerror( errno ) ); - } - else - { - const int i = fstat( infd, in_statsp ); - const mode_t mode = in_statsp->st_mode; - const bool can_read = ( i == 0 && - ( S_ISBLK( mode ) || S_ISCHR( mode ) || - S_ISFIFO( mode ) || S_ISSOCK( mode ) ) ); - const bool no_ofile = to_stdout || program_mode == m_test; - if( i != 0 || ( !S_ISREG( mode ) && ( !can_read || !no_ofile ) ) ) - { - if( verbosity >= 0 ) - fprintf( stderr, "%s: Input file '%s' is not a regular file%s.\n", - program_name, name, - ( can_read && !no_ofile ) ? - " and '--stdout' was not specified" : "" ); - close( infd ); - infd = -1; - } - } - } - return infd; - } - - -/* assure at least a minimum size for buffer 'buf' */ -static void * resize_buffer( void * buf, const int min_size ) - { - if( buf ) buf = realloc( buf, min_size ); - else buf = malloc( min_size ); - if( !buf ) - { - show_error( "Not enough memory.", 0, false ); - cleanup_and_fail( 1 ); - } - return buf; - } - - -static void set_c_outname( const char * const name ) +static void set_c_outname( const char * const name, const bool filenames_given, + const bool force_ext ) { + /* zupdate < 1.9 depends on lzip adding the extension '.lz' to name when + reading from standard input. */ output_filename = resize_buffer( output_filename, strlen( name ) + strlen( known_extensions[0].from ) + 1 ); strcpy( output_filename, name ); - strcat( output_filename, known_extensions[0].from ); + if( force_ext || + ( !filenames_given && extension_index( output_filename ) < 0 ) ) + strcat( output_filename, known_extensions[0].from ); } -static void set_d_outname( const char * const name, const int i ) +static void set_d_outname( const char * const name, const int eindex ) { const unsigned name_len = strlen( name ); - if( i >= 0 ) + if( eindex >= 0 ) { - const char * const from = known_extensions[i].from; + const char * const from = known_extensions[eindex].from; const unsigned from_len = strlen( from ); if( name_len > from_len ) { output_filename = resize_buffer( output_filename, name_len + - strlen( known_extensions[0].to ) + 1 ); + strlen( known_extensions[eindex].to ) + 1 ); strcpy( output_filename, name ); - strcpy( output_filename + name_len - from_len, known_extensions[i].to ); + strcpy( output_filename + name_len - from_len, known_extensions[eindex].to ); return; } } @@ -334,71 +448,128 @@ static void set_d_outname( const char * const name, const int i ) strcpy( output_filename, name ); strcat( output_filename, ".out" ); if( verbosity >= 1 ) - fprintf( stderr, "%s: Can't guess original name for '%s' -- using '%s'.\n", + fprintf( stderr, "%s: %s: Can't guess original name -- using '%s'\n", program_name, name, output_filename ); } -static bool open_outstream( const bool force ) +static int open_instream( const char * const name, struct stat * const in_statsp, + const Mode program_mode, const int eindex, + const bool one_to_one, const bool recompress ) { + if( program_mode == m_compress && !recompress && eindex >= 0 ) + { + if( verbosity >= 0 ) + fprintf( stderr, "%s: %s: Input file already has '%s' suffix, ignored.\n", + program_name, name, known_extensions[eindex].from ); + return -1; + } + int infd = open( name, O_RDONLY | O_BINARY ); + if( infd < 0 ) + show_file_error( name, "Can't open input file", errno ); + else + { + const int i = fstat( infd, in_statsp ); + const mode_t mode = in_statsp->st_mode; + const bool can_read = i == 0 && + ( S_ISBLK( mode ) || S_ISCHR( mode ) || + S_ISFIFO( mode ) || S_ISSOCK( mode ) ); + if( i != 0 || ( !S_ISREG( mode ) && ( !can_read || one_to_one ) ) ) + { + if( verbosity >= 0 ) + fprintf( stderr, "%s: %s: Input file is not a regular file%s.\n", + program_name, name, ( can_read && one_to_one ) ? + ",\n and neither '-c' nor '-o' were specified" : "" ); + close( infd ); + infd = -1; + } + } + return infd; + } + + +static bool open_outstream( const bool force, const bool protect ) + { + const mode_t usr_rw = S_IRUSR | S_IWUSR; + const mode_t all_rw = usr_rw | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH; + const mode_t outfd_mode = protect ? usr_rw : all_rw; int flags = O_CREAT | O_WRONLY | O_BINARY; if( force ) flags |= O_TRUNC; else flags |= O_EXCL; outfd = open( output_filename, flags, outfd_mode ); - if( outfd < 0 && verbosity >= 0 ) - { - if( errno == EEXIST ) - fprintf( stderr, "%s: Output file '%s' already exists, skipping.\n", - program_name, output_filename ); - else - fprintf( stderr, "%s: Can't create output file '%s': %s.\n", - program_name, output_filename, strerror( errno ) ); - } - return ( outfd >= 0 ); + if( outfd >= 0 ) delete_output_on_interrupt = true; + else if( errno == EEXIST ) + show_file_error( output_filename, + "Output file already exists, skipping.", 0 ); + else + show_file_error( output_filename, "Can't create output file", errno ); + return outfd >= 0; } -static bool check_tty( const int infd, const enum Mode program_mode ) +static void set_signals( void (*action)(int) ) { - if( program_mode == m_compress && isatty( outfd ) ) - { - show_error( "I won't write compressed data to a terminal.", 0, true ); - return false; - } - if( ( program_mode == m_decompress || program_mode == m_test ) && - isatty( infd ) ) - { - show_error( "I won't read compressed data from a terminal.", 0, true ); - return false; - } - return true; + signal( SIGHUP, action ); + signal( SIGINT, action ); + signal( SIGTERM, action ); } -void cleanup_and_fail( const int retval ) +static void cleanup_and_fail( const int retval ) { + set_signals( SIG_IGN ); /* ignore signals */ if( delete_output_on_interrupt ) { delete_output_on_interrupt = false; - if( verbosity >= 0 ) - fprintf( stderr, "%s: Deleting output file '%s', if it exists.\n", - program_name, output_filename ); + show_file_error( output_filename, "Deleting output file, if it exists.", 0 ); if( outfd >= 0 ) { close( outfd ); outfd = -1; } if( remove( output_filename ) != 0 && errno != ENOENT ) - show_error( "WARNING: deletion of output file (apparently) failed.", 0, false ); + show_error( "warning: deletion of output file failed", errno, false ); } exit( retval ); } - /* Set permissions, owner and times. */ +static void signal_handler( int sig ) + { + if( sig ) {} /* keep compiler happy */ + show_error( "Control-C or similar caught, quitting.", 0, false ); + cleanup_and_fail( 1 ); + } + + +static bool check_tty_in( const char * const input_filename, const int infd, + const Mode program_mode, int * const retval ) + { + if( ( program_mode == m_decompress || program_mode == m_test ) && + isatty( infd ) ) /* for example /dev/tty */ + { show_file_error( input_filename, + "I won't read compressed data from a terminal.", 0 ); + close( infd ); set_retval( retval, 2 ); + if( program_mode != m_test ) cleanup_and_fail( *retval ); + return false; } + return true; + } + +static bool check_tty_out( const Mode program_mode ) + { + if( program_mode == m_compress && isatty( outfd ) ) + { show_file_error( output_filename[0] ? + output_filename : "(stdout)", + "I won't write compressed data to a terminal.", 0 ); + return false; } + return true; + } + + +/* Set permissions, owner, and times. */ static void close_and_set_permissions( const struct stat * const in_statsp ) { bool warning = false; if( in_statsp ) { const mode_t mode = in_statsp->st_mode; - /* fchown will in many cases return with EPERM, which can be safely ignored. */ + /* fchown in many cases returns with EPERM, which can be safely ignored. */ if( fchown( outfd, in_statsp->st_uid, in_statsp->st_gid ) == 0 ) { if( fchmod( outfd, mode ) != 0 ) warning = true; } else @@ -406,7 +577,9 @@ static void close_and_set_permissions( const struct stat * const in_statsp ) fchmod( outfd, mode & ~( S_ISUID | S_ISGID | S_ISVTX ) ) != 0 ) warning = true; } - if( close( outfd ) != 0 ) cleanup_and_fail( 1 ); + if( close( outfd ) != 0 ) + { show_file_error( output_filename, "Error closing output file", errno ); + cleanup_and_fail( 1 ); } outfd = -1; delete_output_on_interrupt = false; if( in_statsp ) @@ -417,23 +590,25 @@ static void close_and_set_permissions( const struct stat * const in_statsp ) if( utime( output_filename, &t ) != 0 ) warning = true; } if( warning && verbosity >= 1 ) - show_error( "Can't change output file attributes.", 0, false ); + show_file_error( output_filename, + "warning: can't change output file attributes", errno ); } -static int compress( const struct Lzma_options * const encoder_options, - const int infd, struct Pretty_print * const pp ) +static int compress( const int infd, + const Lzma_options * const encoder_options, + Pretty_print * const pp ) { int retval = 0; CLzmaEncHandle encoder = 0; - File_header header; - Fh_set_magic( header ); + Lzip_header header; + Lh_set_magic( header ); if( verbosity >= 1 ) Pp_show_msg( pp, 0 ); - if( Fh_set_dictionary_size( header, encoder_options->dictionary_size ) && + if( Lh_set_dictionary_size( header, encoder_options->dictionary_size ) && encoder_options->match_len_limit >= min_match_len_limit && encoder_options->match_len_limit <= max_match_len ) - encoder = LzmaEnc_Init( Fh_get_dictionary_size( header ), + encoder = LzmaEnc_Init( Lh_get_dictionary_size( header ), encoder_options->match_len_limit, infd, outfd ); else internal_error( "invalid argument to encoder." ); @@ -443,8 +618,8 @@ static int compress( const struct Lzma_options * const encoder_options, return 1; } - if( writeblock( outfd, header, Fh_size ) != Fh_size ) - { show_error( "Can not write output file", errno, false ); retval = 1; } + if( writeblock( outfd, header, Lh_size ) != Lh_size ) + { show_error( "Write error", errno, false ); retval = 1; } else if( LzmaEnc_Encode( encoder ) != 0 ) { Pp_show_msg( pp, "Encoder error." ); retval = 1; } @@ -453,6 +628,28 @@ static int compress( const struct Lzma_options * const encoder_options, } +static void show_results( const long long data_size, + const long long member_size, const unsigned crc, + const unsigned dictionary_size, const bool lzip_mode ) + { + if( verbosity >= 2 ) + { + if( verbosity >= 4 ) show_header( dictionary_size ); + if( data_size == 0 || member_size == 0 ) + fputs( "no data compressed. ", stderr ); + else + fprintf( stderr, "%6.3f:1, %5.2f%% ratio, %5.2f%% saved. ", + (double)data_size / member_size, + ( 100.0 * member_size ) / data_size, + 100.0 - ( ( 100.0 * member_size ) / data_size ) ); + if( verbosity >= 4 && lzip_mode ) fprintf( stderr, "CRC %08X, ", crc ); + if( verbosity >= 3 ) + fprintf( stderr, "%9llu out, %8llu in. ", data_size, member_size ); + if( !lzip_mode ) fputs( "lzma-alone, ", stderr ); + } + } + + #define IN_BUF_SIZE (1 << 16) #define OUT_BUF_SIZE (1 << 16) @@ -484,12 +681,13 @@ enum { lzma_header_size = LZMA_PROPS_SIZE + 8 }; static int lzma_decode( uint64_t unpackSize, CLzmaDec *decoder, const int infd, uint8_t inBuf[], int * const inPos, - int * const inSize, const bool testing ) + int * const inSize, const unsigned dictionary_size, + const bool testing ) { - unsigned long long total_in = lzma_header_size, total_out = 0; + unsigned long long member_size = lzma_header_size, data_size = 0; uint8_t outBuf[OUT_BUF_SIZE]; int outPos = 0; - const bool thereIsSize = (unpackSize != (uint64_t)-1); + const bool thereIsSize = unpackSize != (uint64_t)-1; for (;;) { @@ -511,14 +709,14 @@ static int lzma_decode( uint64_t unpackSize, CLzmaDec *decoder, const int infd, inBuf + *inPos, &inProcessed, finishMode, &status ) ) { show_error( "Data error.", 0, false ); return 2; } *inPos += inProcessed; - total_in += inProcessed; + member_size += inProcessed; outPos += outProcessed; unpackSize -= outProcessed; if( outfd >= 0 && writeblock( outfd, outBuf, outPos ) != outPos ) - { show_error( "Can not write output file", errno, false ); return 1; } + { show_error( "Write error", errno, false ); return 1; } - total_out += outPos; + data_size += outPos; outPos = 0; if( ( inProcessed == 0 && outProcessed == 0 ) || @@ -527,23 +725,21 @@ static int lzma_decode( uint64_t unpackSize, CLzmaDec *decoder, const int infd, if( ( thereIsSize && unpackSize != 0 ) || ( !thereIsSize && status != LZMA_STATUS_FINISHED_WITH_MARK ) ) { show_error( "Data error.", 0, false ); return 2; } - if( verbosity >= 2 ) - { fprintf( stderr, "lzma-alone, " ); show_header( decoder->dicBufSize ); } - if( verbosity >= 3 ) - fprintf( stderr, "uncompressed size %9llu, compressed size %8llu. ", - total_out, total_in ); + show_results( data_size, member_size, 0, dictionary_size, false ); if( verbosity >= 1 ) - fprintf( stderr, testing ? "(apparently) ok\n" : "(apparently) done\n" ); + fputs( testing ? "(apparently) ok\n" : "(apparently) done\n", stderr ); return 0; } } } -static int lzip_decode( CLzmaDec *decoder, const int infd, uint8_t inBuf[], - int * const inPos, int * const inSize ) +static int lzip_decode( CLzmaDec * decoder, const int infd, + Pretty_print * const pp, uint8_t inBuf[], + int * const inPos, int * const inSize, + const unsigned dictionary_size, bool * const data0p ) { - unsigned long long total_in = Fh_size, total_out = 0; + unsigned long long member_size = Lh_size, data_size = 0; uint8_t outBuf[OUT_BUF_SIZE]; int outPos = 0; uint32_t crc = 0xFFFFFFFFU; @@ -558,85 +754,95 @@ static int lzip_decode( CLzmaDec *decoder, const int infd, uint8_t inBuf[], if( *inPos >= *inSize && !read_inbuf( infd, inBuf, inPos, inSize ) ) return 1; if( *inPos >= *inSize ) - { show_error( "Unexpected EOF.", 0, false ); return 2; } + { Pp_show_msg( pp, "Unexpected EOF." ); return 2; } inProcessed = *inSize - *inPos; if( !LzmaDec_DecodeToBuf( decoder, outBuf + outPos, &outProcessed, inBuf + *inPos, &inProcessed, finishMode, &status ) ) - { show_error( "Data error.", 0, false ); return 2; } + { Pp_show_msg( pp, "Data error." ); return 2; } *inPos += inProcessed; - total_in += inProcessed; + member_size += inProcessed; outPos += outProcessed; if( outfd >= 0 && writeblock( outfd, outBuf, outPos ) != outPos ) - { show_error( "Can not write output file", errno, false ); return 1; } + { show_error( "Write error", errno, false ); return 1; } CRC32_update_buf( &crc, outBuf, outPos ); - total_out += outPos; + data_size += outPos; outPos = 0; if (inProcessed == 0 && outProcessed == 0) { - File_trailer trailer; + Lzip_trailer trailer; int i; + unsigned td_crc; + unsigned long long td_size, tm_size; bool error = false; if( status != LZMA_STATUS_FINISHED_WITH_MARK ) - { show_error( "Data error.", 0, false ); return 2; } - if( *inSize - *inPos < Ft_size && + { Pp_show_msg( pp, "Data error." ); return 2; } + if( *inSize - *inPos < Lt_size && !read_inbuf( infd, inBuf, inPos, inSize ) ) return 1; - if( *inSize - *inPos < Ft_size ) + if( *inSize - *inPos < Lt_size ) { error = true; if( verbosity >= 0 ) - fprintf( stderr, "Trailer truncated at trailer position %u;" - " some checks may fail.\n", - (unsigned)(*inSize - *inPos) ); - for( i = *inSize - *inPos; i < Ft_size; ++i ) - inBuf[*inPos+i] = 0; + { + Pp_show_msg( pp, 0 ); + fprintf( stderr, "Trailer truncated at trailer position %d;" + " some checks may fail.\n", *inSize - *inPos ); + } } - for( i = 0; i < Ft_size; ++i ) + for( i = 0; i < Lt_size && *inPos < *inSize; ++i ) trailer[i] = inBuf[(*inPos)++]; - total_in += Ft_size; - if( Ft_get_data_crc( trailer ) != ( crc ^ 0xFFFFFFFFU ) ) + member_size += i; + while( i < Lt_size ) trailer[i++] = 0; + crc ^= 0xFFFFFFFFU; + td_crc = Lt_get_data_crc( trailer ); + if( td_crc != crc ) { error = true; if( verbosity >= 0 ) - fprintf( stderr, "CRC mismatch; trailer says %08X, data crc is %08X.\n", - Ft_get_data_crc( trailer ), crc ^ 0xFFFFFFFFU ); + { + Pp_show_msg( pp, 0 ); + fprintf( stderr, "CRC mismatch; stored %08X, computed %08X\n", + td_crc, crc ); + } } - if( Ft_get_data_size( trailer ) != total_out ) + td_size = Lt_get_data_size( trailer ); + if( td_size != data_size ) { error = true; if( verbosity >= 0 ) - fprintf( stderr, "Data size mismatch; trailer says %llu, data size is %llu (0x%llX).\n", - Ft_get_data_size( trailer ), total_out, total_out ); + { + Pp_show_msg( pp, 0 ); + fprintf( stderr, "Data size mismatch; stored %llu (0x%llX), computed %llu (0x%llX)\n", + td_size, td_size, data_size, data_size ); + } } - if( Ft_get_member_size( trailer ) != total_in ) + tm_size = Lt_get_member_size( trailer ); + if( tm_size != member_size ) { error = true; if( verbosity >= 0 ) - fprintf( stderr, "Member size mismatch; trailer says %llu, member size is %llu (0x%llX).\n", - Ft_get_member_size( trailer ), total_in, total_in ); + { + Pp_show_msg( pp, 0 ); + fprintf( stderr, "Member size mismatch; stored %llu (0x%llX), computed %llu (0x%llX)\n", + tm_size, tm_size, member_size, member_size ); + } } - if( !error && verbosity >= 2 && total_out > 0 && total_in > 0 ) - fprintf( stderr, "%6.3f:1, %6.3f bits/byte, %5.2f%% saved. ", - (double)total_out / total_in, - ( 8.0 * total_in ) / total_out, - 100.0 * ( 1.0 - ( (double)total_in / total_out ) ) ); - if( !error && verbosity >= 4 ) - fprintf( stderr, "data CRC %08X, data size %9llu, member size %8llu. ", - Ft_get_data_crc( trailer ), - Ft_get_data_size( trailer ), Ft_get_member_size( trailer ) ); if( error ) return 2; + show_results( data_size, member_size, td_crc, dictionary_size, true ); + *data0p = data_size == 0; return 0; } } } -static int decompress( const int infd, struct Pretty_print * const pp, - const bool testing ) +static int decompress( const int infd, const Cl_options * const cl_opts, + Pretty_print * const pp, + const bool from_stdin, const bool testing ) { uint64_t unpackSize = 0; CLzmaDec decoder; @@ -646,127 +852,114 @@ static int decompress( const int infd, struct Pretty_print * const pp, bool lzip_mode = true; bool first_member; uint8_t raw_props[lzma_header_size]; + bool empty = false, multi = false; for( first_member = true; ; first_member = false ) { int i; - unsigned dictionary_size = 0; - File_header header; + unsigned dictionary_size = 0; /* keep gcc 3.3.6 quiet */ + Lzip_header header; if( inSize - inPos < lzma_header_size && !read_inbuf( infd, inBuf, &inPos, &inSize ) ) return 1; - if( inSize - inPos <= Fh_size ) /* End Of File */ + const int size = inSize - inPos; + for( i = 0; i < size && i < Lh_size; ++i ) + raw_props[i] = header[i] = inBuf[inPos++]; + if( size <= Lh_size ) /* End Of File */ { if( first_member ) - { Pp_show_msg( pp, "File ends unexpectedly at member header." ); + { show_file_error( pp->name, "File ends unexpectedly at member header.", 0 ); retval = 2; } + else if( Lh_check_prefix( header, size ) ) + { Pp_show_msg( pp, "Truncated header in multimember file." ); + retval = 2; } + else if( size > 0 && !cl_opts->ignore_trailing ) + { Pp_show_msg( pp, trailing_msg ); retval = 2; } break; } - for( i = 0; i < Fh_size; ++i ) raw_props[i] = header[i] = inBuf[inPos++]; - if( !Fh_verify_magic( header ) ) + if( !Lh_check_magic( header ) ) { - if( !first_member ) break; /* trailing garbage */ - if( inSize - inPos >= lzma_header_size - Fh_size ) /* try lzma-alone */ + if( !first_member ) { - for( i = Fh_size; i < lzma_header_size; ++i ) + if( !cl_opts->loose_trailing && Lh_check_corrupt( header ) ) + { Pp_show_msg( pp, "Corrupt header in multimember file." ); + retval = 2; } + else if( !cl_opts->ignore_trailing ) + { Pp_show_msg( pp, trailing_msg ); retval = 2; } + break; + } + if( inSize - inPos >= lzma_header_size - Lh_size ) /* try lzma-alone */ + { + for( i = Lh_size; i < lzma_header_size; ++i ) raw_props[i] = inBuf[inPos++]; - for( i = 0; i < 8; ++i ) - unpackSize += (uint64_t)raw_props[LZMA_PROPS_SIZE+i] << (i * 8); if( ( raw_props[12] == 0 || raw_props[12] == 0xFF ) && raw_props[12] == raw_props[11] && raw_props[0] < (9 * 5 * 5) ) + { lzip_mode = false; + dictionary_size = 0; + for( i = 4; i >= 1; --i ) + { dictionary_size <<= 8; dictionary_size += raw_props[i]; } + for( i = 7; i >= 0; --i ) + { unpackSize <<= 8; unpackSize += raw_props[LZMA_PROPS_SIZE+i]; } + } } if( lzip_mode ) { - Pp_show_msg( pp, "Bad magic number (file not in lzip format)." ); + show_file_error( pp->name, + "Bad magic number (file not in lzip format).", 0 ); retval = 2; break; } } if( lzip_mode ) { - int ds, i; - if( !Fh_verify_version( header ) ) + if( !Lh_check_version( header ) ) { if( verbosity >= 0 ) { Pp_show_msg( pp, 0 ); fprintf( stderr, "Version %d member format not supported.\n", - Fh_version( header ) ); } + Lh_version( header ) ); } retval = 2; break; } - dictionary_size = Fh_get_dictionary_size( header ); - if( dictionary_size < min_dictionary_size || - dictionary_size > max_dictionary_size ) + dictionary_size = Lh_get_dictionary_size( header ); + if( !isvalid_ds( dictionary_size ) ) { Pp_show_msg( pp, "Invalid dictionary size in member header." ); retval = 2; break; } raw_props[0] = 93; /* (45 * 2) + (9 * 0) + 3 */ - ds = dictionary_size; + int ds = dictionary_size; for( i = 1; i <= 4; ++i ) { raw_props[i] = ds & 0xFF; ds >>= 8; } } if( verbosity >= 2 || ( verbosity == 1 && first_member ) ) - { Pp_show_msg( pp, 0 ); - if( lzip_mode && verbosity >= 3 ) show_header( dictionary_size ); } + Pp_show_msg( pp, 0 ); if( !LzmaDec_Init( &decoder, raw_props ) ) - { - show_error( "Not enough memory.", 0, false ); - cleanup_and_fail( 1 ); - } + { Pp_show_msg( pp, mem_msg ); return 1; } + bool data0 = false; if( lzip_mode ) - retval = lzip_decode( &decoder, infd, inBuf, &inPos, &inSize ); + retval = lzip_decode( &decoder, infd, pp, inBuf, &inPos, &inSize, + dictionary_size, &data0 ); else - retval = lzma_decode( unpackSize, &decoder, infd, - inBuf, &inPos, &inSize, testing ); + retval = lzma_decode( unpackSize, &decoder, infd, inBuf, &inPos, + &inSize, dictionary_size, testing ); LzmaDec_Free(&decoder); if( retval != 0 || !lzip_mode ) break; + if( !from_stdin ) { multi = !first_member; if( data0 ) empty = true; } if( verbosity >= 2 ) - { fprintf( stderr, testing ? "ok\n" : "done\n" ); Pp_reset( pp ); } + { fputs( testing ? "ok\n" : "done\n", stderr ); Pp_reset( pp ); } } if( lzip_mode && verbosity == 1 && retval == 0 ) - fprintf( stderr, testing ? "ok\n" : "done\n" ); + fputs( testing ? "ok\n" : "done\n", stderr ); + if( empty && multi && retval == 0 ) + { show_file_error( pp->name, empty_msg, 0 ); retval = 2; } return retval; } -void signal_handler( int sig ) - { - if( sig ) {} /* keep compiler happy */ - show_error( "Control-C or similar caught, quitting.", 0, false ); - cleanup_and_fail( 1 ); - } - - -static void set_signals( void ) - { - signal( SIGHUP, signal_handler ); - signal( SIGINT, signal_handler ); - signal( SIGTERM, signal_handler ); - } - - CRC32 crc32; -void Pp_show_msg( struct Pretty_print * const pp, const char * const msg ) - { - if( verbosity >= 0 ) - { - if( pp->first_post ) - { - int i, len; - pp->first_post = false; - fprintf( stderr, " %s: ", pp->name ); - len = pp->longest_name - strlen( pp->name ); - for( i = 0; i < len; ++i ) fprintf( stderr, " " ); - if( !msg ) fflush( stderr ); - } - if( msg ) fprintf( stderr, "%s\n", msg ); - } - } - - -/* Returns the number of bytes really read. - If (returned value < size) and (errno == 0), means EOF was reached. +/* Return the number of bytes really read. + If (value returned < size) and (errno == 0), means EOF was reached. */ int readblock( const int fd, uint8_t * const buf, const int size ) { @@ -784,8 +977,8 @@ int readblock( const int fd, uint8_t * const buf, const int size ) } -/* Returns the number of bytes really written. - If (returned value < size), it is always an error. +/* Return the number of bytes really written. + If (value returned < size), it is always an error. */ int writeblock( const int fd, const uint8_t * const buf, const int size ) { @@ -802,24 +995,31 @@ int writeblock( const int fd, const uint8_t * const buf, const int size ) } -void show_error( const char * const msg, const int errcode, const bool help ) +static void show_error( const char * const msg, const int errcode, + const bool help ) { - if( verbosity >= 0 ) - { - if( msg && msg[0] ) - { - fprintf( stderr, "%s: %s", program_name, msg ); - if( errcode > 0 ) fprintf( stderr, ": %s.", strerror( errcode ) ); - fprintf( stderr, "\n" ); - } - if( help ) - fprintf( stderr, "Try '%s --help' for more information.\n", - invocation_name ); - } + if( verbosity < 0 ) return; + if( msg && msg[0] ) + fprintf( stderr, "%s: %s%s%s\n", program_name, msg, + ( errcode > 0 ) ? ": " : "", + ( errcode > 0 ) ? strerror( errcode ) : "" ); + if( help ) + fprintf( stderr, "Try '%s --help' for more information.\n", + invocation_name ); } -void internal_error( const char * const msg ) +static void show_file_error( const char * const filename, + const char * const msg, const int errcode ) + { + if( verbosity >= 0 ) + fprintf( stderr, "%s: %s: %s%s%s\n", program_name, filename, msg, + ( errcode > 0 ) ? ": " : "", + ( errcode > 0 ) ? strerror( errcode ) : "" ); + } + + +static void internal_error( const char * const msg ) { if( verbosity >= 0 ) fprintf( stderr, "%s: internal error: %s\n", program_name, msg ); @@ -829,11 +1029,11 @@ void internal_error( const char * const msg ) int main( const int argc, const char * const argv[] ) { - /* Mapping from gzip/bzip2 style 1..9 compression modes - to the corresponding LZMA compression modes. */ - const struct Lzma_options option_mapping[] = + /* Mapping from gzip/bzip2 style 0..9 compression levels to the + corresponding LZMA compression parameters. */ + const Lzma_options option_mapping[] = { - { 1 << 20, 5 }, /* -0 */ + { 1 << 16, 5 }, /* -0 */ { 1 << 20, 5 }, /* -1 */ { 3 << 19, 6 }, /* -2 */ { 1 << 21, 8 }, /* -3 */ @@ -843,209 +1043,217 @@ int main( const int argc, const char * const argv[] ) { 1 << 24, 68 }, /* -7 */ { 3 << 23, 132 }, /* -8 */ { 1 << 25, 273 } }; /* -9 */ - struct Lzma_options encoder_options = option_mapping[6]; /* default = "-6" */ - const char * input_filename = ""; + Lzma_options encoder_options = option_mapping[6]; /* default = "-6" */ const char * default_output_filename = ""; - const char ** filenames = 0; - int num_filenames = 0; - int infd = -1; - enum Mode program_mode = m_compress; - int argind = 0; - int retval = 0; - int i; - bool filenames_given = false; + Mode program_mode = m_compress; + Cl_options cl_opts; /* command-line options */ + Cl_options_init( &cl_opts ); bool force = false; bool keep_input_files = false; bool recompress = false; bool to_stdout = false; - struct Pretty_print pp; + if( argc > 0 ) invocation_name = argv[0]; - const struct ap_Option options[] = + enum { opt_lt = 256 }; + const ap_Option options[] = { - { '0', "fast", ap_no }, - { '1', 0, ap_no }, - { '2', 0, ap_no }, - { '3', 0, ap_no }, - { '4', 0, ap_no }, - { '5', 0, ap_no }, - { '6', 0, ap_no }, - { '7', 0, ap_no }, - { '8', 0, ap_no }, - { '9', "best", ap_no }, - { 'b', "member-size", ap_yes }, - { 'c', "stdout", ap_no }, - { 'd', "decompress", ap_no }, - { 'f', "force", ap_no }, - { 'F', "recompress", ap_no }, - { 'h', "help", ap_no }, - { 'k', "keep", ap_no }, - { 'm', "match-length", ap_yes }, - { 'n', "threads", ap_yes }, - { 'o', "output", ap_yes }, - { 'q', "quiet", ap_no }, - { 's', "dictionary-size", ap_yes }, - { 'S', "volume-size", ap_yes }, - { 't', "test", ap_no }, - { 'v', "verbose", ap_no }, - { 'V', "version", ap_no }, - { 0 , 0, ap_no } }; + { '0', "fast", ap_no }, + { '1', 0, ap_no }, + { '2', 0, ap_no }, + { '3', 0, ap_no }, + { '4', 0, ap_no }, + { '5', 0, ap_no }, + { '6', 0, ap_no }, + { '7', 0, ap_no }, + { '8', 0, ap_no }, + { '9', "best", ap_no }, + { 'a', "trailing-error", ap_no }, + { 'b', "member-size", ap_yes }, + { 'c', "stdout", ap_no }, + { 'd', "decompress", ap_no }, + { 'f', "force", ap_no }, + { 'F', "recompress", ap_no }, + { 'h', "help", ap_no }, + { 'k', "keep", ap_no }, + { 'm', "match-length", ap_yes }, + { 'n', "threads", ap_yes }, + { 'o', "output", ap_yes }, + { 'q', "quiet", ap_no }, + { 's', "dictionary-size", ap_yes }, + { 'S', "volume-size", ap_yes }, + { 't', "test", ap_no }, + { 'v', "verbose", ap_no }, + { 'V', "version", ap_no }, + { opt_lt, "loose-trailing", ap_no }, + { 0, 0, ap_no } }; - struct Arg_parser parser; - - invocation_name = argv[0]; CRC32_init(); + /* static because valgrind complains and memory management in C sucks */ + static Arg_parser parser; if( !ap_init( &parser, argc, argv, options, 0 ) ) - { show_error( "Not enough memory.", 0, false ); return 1; } + { show_error( mem_msg, 0, false ); return 1; } if( ap_error( &parser ) ) /* bad option */ { show_error( ap_error( &parser ), 0, true ); return 1; } + int argind = 0; for( ; argind < ap_arguments( &parser ); ++argind ) { const int code = ap_code( &parser, argind ); - const char * const arg = ap_argument( &parser, argind ); if( !code ) break; /* no more options */ + const char * const pn = ap_parsed_name( &parser, argind ); + const char * const arg = ap_argument( &parser, argind ); switch( code ) { - case '0': case '1': case '2': case '3': case '4': - case '5': case '6': case '7': case '8': case '9': + case '0': case '1': case '2': case '3': case '4': case '5': + case '6': case '7': case '8': case '9': encoder_options = option_mapping[code-'0']; break; - case 'b': break; + case 'a': cl_opts.ignore_trailing = false; break; + case 'b': break; /* ignored */ case 'c': to_stdout = true; break; - case 'd': program_mode = m_decompress; break; + case 'd': set_mode( &program_mode, m_decompress ); break; case 'f': force = true; break; case 'F': recompress = true; break; case 'h': show_help(); return 0; case 'k': keep_input_files = true; break; case 'm': encoder_options.match_len_limit = - getnum( arg, min_match_len_limit, max_match_len ); break; - case 'n': break; - case 'o': default_output_filename = arg; break; + getnum( arg, pn, min_match_len_limit, max_match_len ); break; + case 'n': break; /* ignored */ + case 'o': if( strcmp( arg, "-" ) == 0 ) to_stdout = true; + else { default_output_filename = arg; } break; case 'q': verbosity = -1; break; - case 's': encoder_options.dictionary_size = get_dict_size( arg ); + case 's': encoder_options.dictionary_size = get_dict_size( arg, pn ); break; - case 'S': break; - case 't': program_mode = m_test; break; + case 'S': break; /* ignored */ + case 't': set_mode( &program_mode, m_test ); break; case 'v': if( verbosity < 4 ) ++verbosity; break; case 'V': show_version(); return 0; - default : internal_error( "uncaught option." ); + case opt_lt: cl_opts.loose_trailing = true; break; + default: internal_error( "uncaught option." ); } } /* end process options */ -#if defined(__MSVCRT__) || defined(__OS2__) +#if defined __MSVCRT__ || defined __OS2__ || defined __DJGPP__ setmode( STDIN_FILENO, O_BINARY ); setmode( STDOUT_FILENO, O_BINARY ); #endif - if( program_mode == m_test ) - outfd = -1; - - num_filenames = max( 1, ap_arguments( &parser ) - argind ); + static const char ** filenames = 0; + int num_filenames = max( 1, ap_arguments( &parser ) - argind ); filenames = resize_buffer( filenames, num_filenames * sizeof filenames[0] ); filenames[0] = "-"; + int i; + bool filenames_given = false; for( i = 0; argind + i < ap_arguments( &parser ); ++i ) { filenames[i] = ap_argument( &parser, argind + i ); if( strcmp( filenames[i], "-" ) != 0 ) filenames_given = true; } - if( !to_stdout && program_mode != m_test && - ( filenames_given || default_output_filename[0] ) ) - set_signals(); - - Pp_init( &pp, filenames, num_filenames ); + if( program_mode == m_test ) to_stdout = false; /* apply overrides */ + if( program_mode == m_test || to_stdout ) default_output_filename = ""; output_filename = resize_buffer( output_filename, 1 ); + output_filename[0] = 0; + if( to_stdout && program_mode != m_test ) /* check tty only once */ + { outfd = STDOUT_FILENO; if( !check_tty_out( program_mode ) ) return 1; } + else outfd = -1; + + const bool to_file = !to_stdout && program_mode != m_test && + default_output_filename[0]; + if( !to_stdout && program_mode != m_test && ( filenames_given || to_file ) ) + set_signals( signal_handler ); + + static Pretty_print pp; + Pp_init( &pp, filenames, num_filenames ); + + int failed_tests = 0; + int retval = 0; + const bool one_to_one = !to_stdout && program_mode != m_test && !to_file; + bool stdin_used = false; + struct stat in_stats; for( i = 0; i < num_filenames; ++i ) { - int tmp; - struct stat in_stats; - const struct stat * in_statsp; - output_filename[0] = 0; + const char * input_filename = ""; + int infd; + const bool from_stdin = strcmp( filenames[i], "-" ) == 0; - if( !filenames[i][0] || strcmp( filenames[i], "-" ) == 0 ) + Pp_set_name( &pp, filenames[i] ); + if( from_stdin ) { - input_filename = ""; + if( stdin_used ) continue; else stdin_used = true; infd = STDIN_FILENO; - if( program_mode != m_test ) - { - if( to_stdout || !default_output_filename[0] ) - outfd = STDOUT_FILENO; - else - { - if( program_mode == m_compress ) - set_c_outname( default_output_filename ); - else - { - output_filename = resize_buffer( output_filename, - strlen( default_output_filename ) + 1 ); - strcpy( output_filename, default_output_filename ); - } - outfd_mode = all_rw; - if( !open_outstream( force ) ) - { - if( retval < 1 ) retval = 1; - close( infd ); infd = -1; - continue; - } - } - } + if( !check_tty_in( pp.name, infd, program_mode, &retval ) ) continue; + if( one_to_one ) { outfd = STDOUT_FILENO; output_filename[0] = 0; } } else { - const int eindex = extension_index( filenames[i] ); - input_filename = filenames[i]; + const int eindex = extension_index( input_filename = filenames[i] ); infd = open_instream( input_filename, &in_stats, program_mode, - eindex, recompress, to_stdout ); - if( infd < 0 ) { if( retval < 1 ) retval = 1; continue; } - if( program_mode != m_test ) + eindex, one_to_one, recompress ); + if( infd < 0 ) { set_retval( &retval, 1 ); continue; } + if( !check_tty_in( pp.name, infd, program_mode, &retval ) ) continue; + if( one_to_one ) /* open outfd after checking infd */ { - if( to_stdout ) outfd = STDOUT_FILENO; - else - { - if( program_mode == m_compress ) - set_c_outname( input_filename ); - else set_d_outname( input_filename, eindex ); - outfd_mode = usr_rw; - if( !open_outstream( force ) ) - { - if( retval < 1 ) retval = 1; - close( infd ); infd = -1; - continue; - } - } + if( program_mode == m_compress ) + set_c_outname( input_filename, true, true ); + else set_d_outname( input_filename, eindex ); + if( !open_outstream( force, true ) ) + { close( infd ); set_retval( &retval, 1 ); continue; } } } - if( !check_tty( infd, program_mode ) ) return 1; + if( one_to_one && !check_tty_out( program_mode ) ) + { set_retval( &retval, 1 ); return retval; } /* don't delete a tty */ - if( output_filename[0] && !to_stdout && program_mode != m_test ) - delete_output_on_interrupt = true; - in_statsp = input_filename[0] ? &in_stats : 0; - Pp_set_name( &pp, input_filename ); - if( program_mode == m_compress ) - tmp = compress( &encoder_options, infd, &pp ); - else - tmp = decompress( infd, &pp, program_mode == m_test ); - if( tmp > retval ) retval = tmp; - if( tmp && program_mode != m_test ) cleanup_and_fail( retval ); - - if( delete_output_on_interrupt ) - close_and_set_permissions( in_statsp ); - if( input_filename[0] ) + if( to_file && outfd < 0 ) /* open outfd after checking infd */ { - close( infd ); infd = -1; - if( !keep_input_files && !to_stdout && program_mode != m_test ) - remove( input_filename ); + if( program_mode == m_compress ) set_c_outname( default_output_filename, + filenames_given, false ); + else + { output_filename = resize_buffer( output_filename, + strlen( default_output_filename ) + 1 ); + strcpy( output_filename, default_output_filename ); } + if( !open_outstream( force, false ) || !check_tty_out( program_mode ) ) + return 1; /* check tty only once and don't try to delete a tty */ } + + const struct stat * const in_statsp = + ( input_filename[0] && one_to_one ) ? &in_stats : 0; + int tmp; + if( program_mode == m_compress ) + tmp = compress( infd, &encoder_options, &pp ); + else + tmp = decompress( infd, &cl_opts, &pp, from_stdin, + program_mode == m_test ); + if( close( infd ) != 0 ) + { show_file_error( pp.name, "Error closing input file", errno ); + set_retval( &tmp, 1 ); } + set_retval( &retval, tmp ); + if( tmp ) + { if( program_mode != m_test ) cleanup_and_fail( retval ); + else ++failed_tests; } + + if( delete_output_on_interrupt && one_to_one ) + close_and_set_permissions( in_statsp ); + if( input_filename[0] && !keep_input_files && one_to_one ) + remove( input_filename ); } - if( outfd >= 0 && close( outfd ) != 0 ) + if( delete_output_on_interrupt ) /* -o */ + close_and_set_permissions( ( retval == 0 && !stdin_used && + filenames_given && num_filenames == 1 ) ? &in_stats : 0 ); + else if( outfd >= 0 && close( outfd ) != 0 ) /* -c */ { - show_error( "Can't close stdout", errno, false ); - if( retval < 1 ) retval = 1; + show_error( "Error closing stdout", errno, false ); + set_retval( &retval, 1 ); } + if( failed_tests > 0 && verbosity >= 1 && num_filenames > 1 ) + fprintf( stderr, "%s: warning: %d %s failed the test.\n", + program_name, failed_tests, + ( failed_tests == 1 ) ? "file" : "files" ); free( output_filename ); + Pp_free( &pp ); free( filenames ); ap_free( &parser ); return retval; diff --git a/testsuite/check.sh b/testsuite/check.sh index 3fab78e..1760359 100755 --- a/testsuite/check.sh +++ b/testsuite/check.sh @@ -1,9 +1,9 @@ #! /bin/sh # check script for Pdlzip - LZMA lossless data compressor -# Copyright (C) 2010-2015 Antonio Diaz Diaz. +# Copyright (C) 2010-2025 Antonio Diaz Diaz. # # This script is free software: you have unlimited permission -# to copy, distribute and modify it. +# to copy, distribute, and modify it. LC_ALL=C export LC_ALL @@ -17,121 +17,391 @@ if [ ! -f "${LZIP}" ] || [ ! -x "${LZIP}" ] ; then exit 1 fi +[ -e "${LZIP}" ] 2> /dev/null || + { + echo "$0: a POSIX shell is required to run the tests" + echo "Try bash -c \"$0 $1 $2\"" + exit 1 + } + if [ -d tmp ] ; then rm -rf tmp ; fi mkdir tmp -cd "${objdir}"/tmp +cd "${objdir}"/tmp || framework_failure -cat "${testdir}"/test.txt > in || framework_failure +cp "${testdir}"/test.txt in || framework_failure in_lz="${testdir}"/test.txt.lz +fox_lz="${testdir}"/fox.lz +fnz_lz="${testdir}"/fox_nz.lz fail=0 +test_failed() { fail=1 ; printf " $1" ; [ -z "$2" ] || printf "($2)" ; } printf "testing pdlzip-%s..." "$2" -"${LZIP}" -cqm4 in > /dev/null -if [ $? = 1 ] ; then printf . ; else printf - ; fail=1 ; fi -"${LZIP}" -cqm274 in > /dev/null -if [ $? = 1 ] ; then printf . ; else printf - ; fail=1 ; fi -"${LZIP}" -cqs-1 in > /dev/null -if [ $? = 1 ] ; then printf . ; else printf - ; fail=1 ; fi -"${LZIP}" -cqs0 in > /dev/null -if [ $? = 1 ] ; then printf . ; else printf - ; fail=1 ; fi -"${LZIP}" -cqs4095 in > /dev/null -if [ $? = 1 ] ; then printf . ; else printf - ; fail=1 ; fi -"${LZIP}" -cqs513MiB in > /dev/null -if [ $? = 1 ] ; then printf . ; else printf - ; fail=1 ; fi -printf " in: Bad magic number (file not in lzip format).\n" > msg -"${LZIP}" -t in 2> out -if [ $? = 2 ] && cmp out msg ; then printf . ; else printf - ; fail=1 ; fi -printf " (stdin): Bad magic number (file not in lzip format).\n" > msg -"${LZIP}" -t < in 2> out -if [ $? = 2 ] && cmp out msg ; then printf . ; else printf - ; fail=1 ; fi -rm -f out msg +"${LZIP}" -fkqm4 in +[ $? = 1 ] || test_failed $LINENO +[ ! -e in.lz ] || test_failed $LINENO +"${LZIP}" -fkqm274 in +[ $? = 1 ] || test_failed $LINENO +[ ! -e in.lz ] || test_failed $LINENO +for i in bad_size -1 0 4095 513MiB 1G 1T 1P 1E 1Z 1Y 10KB ; do + "${LZIP}" -fkqs $i in + [ $? = 1 ] || test_failed $LINENO $i + [ ! -e in.lz ] || test_failed $LINENO $i +done +"${LZIP}" -tq in +[ $? = 2 ] || test_failed $LINENO +"${LZIP}" -tq < in +[ $? = 2 ] || test_failed $LINENO "${LZIP}" -cdq in -if [ $? = 2 ] ; then printf . ; else printf - ; fail=1 ; fi +[ $? = 2 ] || test_failed $LINENO "${LZIP}" -cdq < in -if [ $? = 2 ] ; then printf . ; else printf - ; fail=1 ; fi -dd if="${in_lz}" bs=1 count=6 2> /dev/null | "${LZIP}" -tq -if [ $? = 2 ] ; then printf . ; else printf - ; fail=1 ; fi -dd if="${in_lz}" bs=1 count=20 2> /dev/null | "${LZIP}" -tq -if [ $? = 2 ] ; then printf . ; else printf - ; fail=1 ; fi +[ $? = 2 ] || test_failed $LINENO +"${LZIP}" -dq -o in < "${in_lz}" +[ $? = 1 ] || test_failed $LINENO +"${LZIP}" -dq -o in "${in_lz}" +[ $? = 1 ] || test_failed $LINENO +"${LZIP}" -dq -o out nx_file.lz +[ $? = 1 ] || test_failed $LINENO +[ ! -e out ] || test_failed $LINENO +"${LZIP}" -q -o out.lz nx_file +[ $? = 1 ] || test_failed $LINENO +[ ! -e out.lz ] || test_failed $LINENO +# these are for code coverage +"${LZIP}" -cdt "${in_lz}" 2> /dev/null +[ $? = 1 ] || test_failed $LINENO +"${LZIP}" -t -- nx_file.lz 2> /dev/null +[ $? = 1 ] || test_failed $LINENO +"${LZIP}" -t "" < /dev/null 2> /dev/null +[ $? = 1 ] || test_failed $LINENO +"${LZIP}" --help > /dev/null || test_failed $LINENO +"${LZIP}" -n1 -V > /dev/null || test_failed $LINENO +"${LZIP}" -m 2> /dev/null +[ $? = 1 ] || test_failed $LINENO +"${LZIP}" -z 2> /dev/null +[ $? = 1 ] || test_failed $LINENO +"${LZIP}" --bad_option 2> /dev/null +[ $? = 1 ] || test_failed $LINENO +"${LZIP}" --t 2> /dev/null +[ $? = 1 ] || test_failed $LINENO +"${LZIP}" --test=2 2> /dev/null +[ $? = 1 ] || test_failed $LINENO +"${LZIP}" --output= 2> /dev/null +[ $? = 1 ] || test_failed $LINENO +"${LZIP}" --output 2> /dev/null +[ $? = 1 ] || test_failed $LINENO +printf "LZIP\001-.............................." | "${LZIP}" -t 2> /dev/null +printf "LZIP\002-.............................." | "${LZIP}" -t 2> /dev/null +printf "LZIP\001+.............................." | "${LZIP}" -t 2> /dev/null -"${LZIP}" -t "${in_lz}" || fail=1 -"${LZIP}" -cd "${in_lz}" > copy || fail=1 -cmp in copy || fail=1 -printf . +printf "\ntesting decompression..." -"${LZIP}" -t "${testdir}"/test.txt.lzma || fail=1 -"${LZIP}" -cd "${testdir}"/test.txt.lzma > copy || fail=1 -cmp in copy || fail=1 -printf . +for i in "${in_lz}" "${testdir}"/test.txt.lzma ; do + "${LZIP}" -t "$i" || test_failed $LINENO "$i" + "${LZIP}" -d "$i" -o out || test_failed $LINENO "$i" + cmp in out || test_failed $LINENO "$i" + "${LZIP}" -cd "$i" > out || test_failed $LINENO "$i" + cmp in out || test_failed $LINENO "$i" + "${LZIP}" -d "$i" -o - > out || test_failed $LINENO "$i" + cmp in out || test_failed $LINENO "$i" + "${LZIP}" -d < "$i" > out || test_failed $LINENO "$i" + cmp in out || test_failed $LINENO "$i" + rm -f out || framework_failure +done -cat "${in_lz}" > copy.lz || framework_failure -printf "to be overwritten" > copy || framework_failure -"${LZIP}" -df copy.lz || fail=1 -cmp in copy || fail=1 -printf . +cp "${in_lz}" out.lz || framework_failure +"${LZIP}" -dk out.lz || test_failed $LINENO +cmp in out || test_failed $LINENO +rm -f out || framework_failure +"${LZIP}" -cd "${fox_lz}" > fox || test_failed $LINENO +cp fox copy || framework_failure +cp "${in_lz}" copy.lz || framework_failure +"${LZIP}" -d copy.lz out.lz 2> /dev/null # skip copy, decompress out +[ $? = 1 ] || test_failed $LINENO +[ ! -e out.lz ] || test_failed $LINENO +cmp fox copy || test_failed $LINENO +cmp in out || test_failed $LINENO +"${LZIP}" -df copy.lz || test_failed $LINENO +[ ! -e copy.lz ] || test_failed $LINENO +cmp in copy || test_failed $LINENO +rm -f copy out || framework_failure -printf "to be overwritten" > copy || framework_failure -"${LZIP}" -df -o copy < "${in_lz}" || fail=1 -cmp in copy || fail=1 -printf . +printf "to be overwritten" > out || framework_failure +"${LZIP}" -df -o out < "${in_lz}" || test_failed $LINENO +cmp in out || test_failed $LINENO +"${LZIP}" -d -o ./- "${in_lz}" || test_failed $LINENO +cmp in ./- || test_failed $LINENO +rm -f ./- || framework_failure +"${LZIP}" -d -o ./- < "${in_lz}" || test_failed $LINENO +cmp in ./- || test_failed $LINENO +rm -f ./- || framework_failure -"${LZIP}" -s16 < in > anyothername || fail=1 -"${LZIP}" -d anyothername || fail=1 -cmp in anyothername.out || fail=1 -printf . +cp "${in_lz}" anyothername || framework_failure +"${LZIP}" -dv - anyothername - < "${in_lz}" > out 2> /dev/null || + test_failed $LINENO +cmp in out || test_failed $LINENO +cmp in anyothername.out || test_failed $LINENO +rm -f anyothername.out || framework_failure + +"${LZIP}" -tq in "${in_lz}" +[ $? = 2 ] || test_failed $LINENO +"${LZIP}" -tq nx_file.lz "${in_lz}" +[ $? = 1 ] || test_failed $LINENO +"${LZIP}" -cdq in "${in_lz}" > out +[ $? = 2 ] || test_failed $LINENO +cat out in | cmp in - || test_failed $LINENO # out must be empty +"${LZIP}" -cdq nx_file.lz "${in_lz}" > out # skip nx_file, decompress in +[ $? = 1 ] || test_failed $LINENO +cmp in out || test_failed $LINENO +rm -f out || framework_failure +cp "${in_lz}" out.lz || framework_failure +for i in 1 2 3 4 5 6 7 ; do + printf "g" >> out.lz || framework_failure + "${LZIP}" -atvvvv out.lz "${in_lz}" 2> /dev/null + [ $? = 2 ] || test_failed $LINENO $i +done +"${LZIP}" -dq in out.lz +[ $? = 2 ] || test_failed $LINENO +[ -e out.lz ] || test_failed $LINENO +[ ! -e out ] || test_failed $LINENO +[ ! -e in.out ] || test_failed $LINENO +"${LZIP}" -dq nx_file.lz out.lz +[ $? = 1 ] || test_failed $LINENO +[ ! -e out.lz ] || test_failed $LINENO +[ ! -e nx_file ] || test_failed $LINENO +cmp in out || test_failed $LINENO +rm -f out || framework_failure cat in in > in2 || framework_failure -"${LZIP}" -s16 -o copy2 < in2 || fail=1 -"${LZIP}" -t copy2.lz || fail=1 -printf . -"${LZIP}" -cd copy2.lz > copy2 || fail=1 -cmp in2 copy2 || fail=1 -printf . +"${LZIP}" -t "${in_lz}" "${in_lz}" || test_failed $LINENO +"${LZIP}" -cd "${in_lz}" "${in_lz}" -o out > out2 || test_failed $LINENO +[ ! -e out ] || test_failed $LINENO # override -o +cmp in2 out2 || test_failed $LINENO +rm -f out2 || framework_failure +"${LZIP}" -d "${in_lz}" "${in_lz}" -o out2 || test_failed $LINENO +cmp in2 out2 || test_failed $LINENO +rm -f out2 || framework_failure -printf "garbage" >> copy2.lz || framework_failure -printf "to be overwritten" > copy2 || framework_failure -"${LZIP}" -df copy2.lz || fail=1 -cmp in2 copy2 || fail=1 -printf . +cat "${in_lz}" "${in_lz}" > out2.lz || framework_failure +lines=`"${LZIP}" -tvv out2.lz 2>&1 | wc -l` || test_failed $LINENO +[ "${lines}" -eq 2 ] || test_failed $LINENO "${lines}" -"${LZIP}" -cfq "${in_lz}" > out -if [ $? = 1 ] ; then printf . ; else printf - ; fail=1 ; fi -"${LZIP}" -cF -s16 "${in_lz}" > out || fail=1 -"${LZIP}" -cd out | "${LZIP}" -d > copy || fail=1 -cmp in copy || fail=1 -printf . +printf "\ngarbage" >> out2.lz || framework_failure +"${LZIP}" -tvvvv out2.lz 2> /dev/null || test_failed $LINENO +"${LZIP}" -atq out2.lz +[ $? = 2 ] || test_failed $LINENO +"${LZIP}" -atq < out2.lz +[ $? = 2 ] || test_failed $LINENO +"${LZIP}" -adkq out2.lz +[ $? = 2 ] || test_failed $LINENO +[ ! -e out2 ] || test_failed $LINENO +"${LZIP}" -adkq -o out2 < out2.lz +[ $? = 2 ] || test_failed $LINENO +[ ! -e out2 ] || test_failed $LINENO +printf "to be overwritten" > out2 || framework_failure +"${LZIP}" -df out2.lz || test_failed $LINENO +cmp in2 out2 || test_failed $LINENO +rm -f out2 || framework_failure + +touch empty em || framework_failure +"${LZIP}" -0 em || test_failed $LINENO +"${LZIP}" -dk em.lz || test_failed $LINENO +cmp empty em || test_failed $LINENO +cat em.lz em.lz | "${LZIP}" -t || test_failed $LINENO +cat em.lz em.lz | "${LZIP}" -d > em || test_failed $LINENO +cmp empty em || test_failed $LINENO +cat em.lz "${in_lz}" | "${LZIP}" -t || test_failed $LINENO +cat em.lz "${in_lz}" | "${LZIP}" -d > out || test_failed $LINENO +cmp in out || test_failed $LINENO +cat "${in_lz}" em.lz | "${LZIP}" -t || test_failed $LINENO +cat "${in_lz}" em.lz | "${LZIP}" -d > out || test_failed $LINENO +cmp in out || test_failed $LINENO + +printf "\ntesting compression..." + +"${LZIP}" -c -0 in in in -o out3.lz > copy2.lz || test_failed $LINENO +[ ! -e out3.lz ] || test_failed $LINENO # override -o +"${LZIP}" -0f in in --output=copy2.lz || test_failed $LINENO +"${LZIP}" -d copy2.lz -o out2 || test_failed $LINENO +[ -e copy2.lz ] || test_failed $LINENO +cmp in2 out2 || test_failed $LINENO +rm -f copy2.lz || framework_failure + +"${LZIP}" -cf "${in_lz}" > lzlz 2> /dev/null # /dev/null is a tty on OS/2 +[ $? = 1 ] || test_failed $LINENO +"${LZIP}" -Fvvm36 -o - -s16 "${in_lz}" > lzlz 2> /dev/null || test_failed $LINENO +"${LZIP}" -cd lzlz | "${LZIP}" -d > out || test_failed $LINENO +cmp in out || test_failed $LINENO +rm -f lzlz out || framework_failure + +"${LZIP}" -0 -o ./- in || test_failed $LINENO +"${LZIP}" -cd ./- | cmp in - || test_failed $LINENO +rm -f ./- || framework_failure +"${LZIP}" -0 -o ./- < in || test_failed $LINENO # add .lz +[ ! -e ./- ] || test_failed $LINENO +"${LZIP}" -cd -- -.lz | cmp in - || test_failed $LINENO +rm -f ./-.lz || framework_failure for i in s4Ki 0 1 2 3 4 5 6 7 8 9 ; do - "${LZIP}" -k -$i -s16 in || fail=1 - mv -f in.lz copy.lz || fail=1 - printf "garbage" >> copy.lz || fail=1 - "${LZIP}" -df copy.lz || fail=1 - cmp in copy || fail=1 -done -printf . + "${LZIP}" -k -$i -s16 in || test_failed $LINENO $i + mv in.lz out.lz || test_failed $LINENO $i + printf "garbage" >> out.lz || framework_failure + "${LZIP}" -df out.lz || test_failed $LINENO $i + cmp in out || test_failed $LINENO $i -for i in s4Ki 0 1 2 3 4 5 6 7 8 9 ; do - "${LZIP}" -c -$i -s16 in > out || fail=1 - printf "g" >> out || fail=1 - "${LZIP}" -cd out > copy || fail=1 - cmp in copy || fail=1 -done -printf . + "${LZIP}" -$i -s16 in -c > out || test_failed $LINENO $i + "${LZIP}" -$i -s16 in -o o_out || test_failed $LINENO $i # don't add .lz + [ ! -e o_out.lz ] || test_failed $LINENO + cmp out o_out || test_failed $LINENO $i + rm -f o_out || framework_failure + printf "g" >> out || framework_failure + "${LZIP}" -cd out > copy || test_failed $LINENO $i + cmp in copy || test_failed $LINENO $i -for i in s4Ki 0 1 2 3 4 5 6 7 8 9 ; do - "${LZIP}" -$i -s16 < in > out || fail=1 - "${LZIP}" -d < out > copy || fail=1 - cmp in copy || fail=1 -done -printf . + "${LZIP}" -$i -s16 < in > out || test_failed $LINENO $i + "${LZIP}" -d < out > copy || test_failed $LINENO $i + cmp in copy || test_failed $LINENO $i -for i in s4Ki 0 1 2 3 4 5 6 7 8 9 ; do - "${LZIP}" -f -$i -s16 -o out < in || fail=1 - "${LZIP}" -df -o copy < out.lz || fail=1 - cmp in copy || fail=1 + rm -f out || framework_failure + printf "to be overwritten" > out.lz || framework_failure + "${LZIP}" -f -$i -s16 -o out < in || test_failed $LINENO $i # add .lz + [ ! -e out ] || test_failed $LINENO + "${LZIP}" -df -o copy < out.lz || test_failed $LINENO $i + cmp in copy || test_failed $LINENO $i done -printf . +rm -f copy out.lz || framework_failure + +printf "\ntesting bad input..." + +cat em.lz em.lz > ee.lz || framework_failure +"${LZIP}" -t < ee.lz || test_failed $LINENO +"${LZIP}" -d < ee.lz > em || test_failed $LINENO +cmp empty em || test_failed $LINENO +"${LZIP}" -tq ee.lz +[ $? = 2 ] || test_failed $LINENO +"${LZIP}" -dq ee.lz +[ $? = 2 ] || test_failed $LINENO +[ ! -e ee ] || test_failed $LINENO +"${LZIP}" -cdq ee.lz > em +[ $? = 2 ] || test_failed $LINENO +cmp empty em || test_failed $LINENO +rm -f empty em || framework_failure +cat "${in_lz}" em.lz "${in_lz}" > inein.lz || framework_failure +"${LZIP}" -t < inein.lz || test_failed $LINENO +"${LZIP}" -d < inein.lz > out2 || test_failed $LINENO +cmp in2 out2 || test_failed $LINENO +"${LZIP}" -tq inein.lz +[ $? = 2 ] || test_failed $LINENO +"${LZIP}" -dq inein.lz +[ $? = 2 ] || test_failed $LINENO +[ ! -e inein ] || test_failed $LINENO +"${LZIP}" -cdq inein.lz > out2 +[ $? = 2 ] || test_failed $LINENO +cmp in2 out2 || test_failed $LINENO +rm -f in2 out2 inein.lz em.lz || framework_failure + +headers='LZIp LZiP LZip LzIP LzIp LziP lZIP lZIp lZiP lzIP' +body='\001\014\000\000\101\376\367\377\377\340\000\200\000\215\357\002\322\001\000\000\000\000\000\000\000\045\000\000\000\000\000\000\000' +cp "${in_lz}" int.lz || framework_failure +printf "LZIP${body}" >> int.lz || framework_failure +if "${LZIP}" -t int.lz ; then + for header in ${headers} ; do + printf "${header}${body}" > int.lz || framework_failure + "${LZIP}" -tq int.lz # first member + [ $? = 2 ] || test_failed $LINENO ${header} + "${LZIP}" -tq < int.lz + [ $? = 2 ] || test_failed $LINENO ${header} + "${LZIP}" -cdq int.lz > /dev/null + [ $? = 2 ] || test_failed $LINENO ${header} + "${LZIP}" -tq --loose-trailing int.lz + [ $? = 2 ] || test_failed $LINENO ${header} + "${LZIP}" -tq --loose-trailing < int.lz + [ $? = 2 ] || test_failed $LINENO ${header} + "${LZIP}" -cdq --loose-trailing int.lz > /dev/null + [ $? = 2 ] || test_failed $LINENO ${header} + cp "${in_lz}" int.lz || framework_failure + printf "${header}${body}" >> int.lz || framework_failure + "${LZIP}" -tq int.lz # trailing data + [ $? = 2 ] || test_failed $LINENO ${header} + "${LZIP}" -tq < int.lz + [ $? = 2 ] || test_failed $LINENO ${header} + "${LZIP}" -cdq int.lz > /dev/null + [ $? = 2 ] || test_failed $LINENO ${header} + "${LZIP}" -t --loose-trailing int.lz || + test_failed $LINENO ${header} + "${LZIP}" -t --loose-trailing < int.lz || + test_failed $LINENO ${header} + "${LZIP}" -cd --loose-trailing int.lz > /dev/null || + test_failed $LINENO ${header} + "${LZIP}" -tq --loose-trailing --trailing-error int.lz + [ $? = 2 ] || test_failed $LINENO ${header} + "${LZIP}" -tq --loose-trailing --trailing-error < int.lz + [ $? = 2 ] || test_failed $LINENO ${header} + "${LZIP}" -cdq --loose-trailing --trailing-error int.lz > /dev/null + [ $? = 2 ] || test_failed $LINENO ${header} + done +else + printf "warning: skipping header test: 'printf' does not work on your system." +fi +rm -f int.lz || framework_failure + +"${LZIP}" -tq "${fnz_lz}" +[ $? = 2 ] || test_failed $LINENO + +for i in fox_v2.lz fox_s11.lz fox_de20.lz \ + fox_bcrc.lz fox_crc0.lz fox_das46.lz fox_mes81.lz ; do + "${LZIP}" -tq "${testdir}"/$i + [ $? = 2 ] || test_failed $LINENO $i +done + +for i in fox_bcrc.lz fox_crc0.lz fox_das46.lz fox_mes81.lz ; do + "${LZIP}" -cdq "${testdir}"/$i > out + [ $? = 2 ] || test_failed $LINENO $i + cmp fox out || test_failed $LINENO $i +done +rm -f fox || framework_failure + +cat "${in_lz}" "${in_lz}" > in2.lz || framework_failure +cat "${in_lz}" "${in_lz}" "${in_lz}" > in3.lz || framework_failure +if dd if=in3.lz of=trunc.lz bs=14682 count=1 2> /dev/null && + [ -e trunc.lz ] && cmp in2.lz trunc.lz ; then + for i in 6 20 14664 14683 14684 14685 14686 14687 14688 ; do + dd if=in3.lz of=trunc.lz bs=$i count=1 2> /dev/null + "${LZIP}" -tq trunc.lz + [ $? = 2 ] || test_failed $LINENO $i + "${LZIP}" -tq < trunc.lz + [ $? = 2 ] || test_failed $LINENO $i + "${LZIP}" -cdq trunc.lz > /dev/null + [ $? = 2 ] || test_failed $LINENO $i + "${LZIP}" -dq < trunc.lz > /dev/null + [ $? = 2 ] || test_failed $LINENO $i + done +else + printf "warning: skipping truncation test: 'dd' does not work on your system." +fi +rm -f in2.lz in3.lz trunc.lz || framework_failure + +cp "${in_lz}" ingin.lz || framework_failure +printf "g" >> ingin.lz || framework_failure +cat "${in_lz}" >> ingin.lz || framework_failure +"${LZIP}" -atq ingin.lz +[ $? = 2 ] || test_failed $LINENO +"${LZIP}" -atq < ingin.lz +[ $? = 2 ] || test_failed $LINENO +"${LZIP}" -acdq ingin.lz > out +[ $? = 2 ] || test_failed $LINENO +cmp in out || test_failed $LINENO +"${LZIP}" -adq < ingin.lz > out +[ $? = 2 ] || test_failed $LINENO +cmp in out || test_failed $LINENO +"${LZIP}" -t ingin.lz || test_failed $LINENO +"${LZIP}" -t < ingin.lz || test_failed $LINENO +"${LZIP}" -dk ingin.lz || test_failed $LINENO +cmp in ingin || test_failed $LINENO +"${LZIP}" -cd ingin.lz > out || test_failed $LINENO +cmp in out || test_failed $LINENO +"${LZIP}" -d < ingin.lz > out || test_failed $LINENO +cmp in out || test_failed $LINENO +rm -f out ingin ingin.lz || framework_failure echo if [ ${fail} = 0 ] ; then diff --git a/testsuite/fox.lz b/testsuite/fox.lz new file mode 100644 index 0000000..509da82 Binary files /dev/null and b/testsuite/fox.lz differ diff --git a/testsuite/fox_bcrc.lz b/testsuite/fox_bcrc.lz new file mode 100644 index 0000000..8f6a7c4 Binary files /dev/null and b/testsuite/fox_bcrc.lz differ diff --git a/testsuite/fox_crc0.lz b/testsuite/fox_crc0.lz new file mode 100644 index 0000000..1abe926 Binary files /dev/null and b/testsuite/fox_crc0.lz differ diff --git a/testsuite/fox_das46.lz b/testsuite/fox_das46.lz new file mode 100644 index 0000000..43ed9f9 Binary files /dev/null and b/testsuite/fox_das46.lz differ diff --git a/testsuite/fox_de20.lz b/testsuite/fox_de20.lz new file mode 100644 index 0000000..10949d8 Binary files /dev/null and b/testsuite/fox_de20.lz differ diff --git a/testsuite/fox_mes81.lz b/testsuite/fox_mes81.lz new file mode 100644 index 0000000..d50ef2e Binary files /dev/null and b/testsuite/fox_mes81.lz differ diff --git a/testsuite/fox_nz.lz b/testsuite/fox_nz.lz new file mode 100644 index 0000000..44a4b58 Binary files /dev/null and b/testsuite/fox_nz.lz differ diff --git a/testsuite/fox_s11.lz b/testsuite/fox_s11.lz new file mode 100644 index 0000000..dca909c Binary files /dev/null and b/testsuite/fox_s11.lz differ diff --git a/testsuite/fox_v2.lz b/testsuite/fox_v2.lz new file mode 100644 index 0000000..8620981 Binary files /dev/null and b/testsuite/fox_v2.lz differ diff --git a/testsuite/test.txt b/testsuite/test.txt index 9196a3a..423f0c0 100644 --- a/testsuite/test.txt +++ b/testsuite/test.txt @@ -1,8 +1,7 @@ GNU GENERAL PUBLIC LICENSE Version 2, June 1991 - Copyright (C) 1989, 1991 Free Software Foundation, Inc., - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Copyright (C) 1989, 1991 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. @@ -339,8 +338,7 @@ Public License instead of this License. GNU GENERAL PUBLIC LICENSE Version 2, June 1991 - Copyright (C) 1989, 1991 Free Software Foundation, Inc., - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Copyright (C) 1989, 1991 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. diff --git a/testsuite/test.txt.lz b/testsuite/test.txt.lz index 41d2e39..5dc169f 100644 Binary files a/testsuite/test.txt.lz and b/testsuite/test.txt.lz differ diff --git a/testsuite/test.txt.lzma b/testsuite/test.txt.lzma index 77f2b87..091c023 100644 Binary files a/testsuite/test.txt.lzma and b/testsuite/test.txt.lzma differ