Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package pigz for openSUSE:Factory checked in at 2022-05-10 15:11:18 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/pigz (Old) and /work/SRC/openSUSE:Factory/.pigz.new.1538 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "pigz" Tue May 10 15:11:18 2022 rev:16 rq:975711 version:2.7 Changes: -------- --- /work/SRC/openSUSE:Factory/pigz/pigz.changes 2021-02-11 12:45:58.277365171 +0100 +++ /work/SRC/openSUSE:Factory/.pigz.new.1538/pigz.changes 2022-05-10 15:11:20.483525491 +0200 @@ -1,0 +2,8 @@ +Sat May 7 19:02:01 UTC 2022 - Andreas Stieger <andreas.stie...@gmx.de> + +- update to 2.7: + * Improved display of multiple-member gzip files + * Better gzip compatibility and bug fixes +- add pigz-2.7-NOTHREAD-tests.patch to fix tests + +------------------------------------------------------------------- Old: ---- pigz-2.6.tar.gz pigz-2.6.tar.gz.asc New: ---- pigz-2.7-NOTHREAD-tests.patch pigz-2.7.tar.gz pigz-2.7.tar.gz.asc ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ pigz.spec ++++++ --- /var/tmp/diff_new_pack.KdWWGx/_old 2022-05-10 15:11:21.083526247 +0200 +++ /var/tmp/diff_new_pack.KdWWGx/_new 2022-05-10 15:11:21.087526252 +0200 @@ -1,7 +1,7 @@ # # spec file for package pigz # -# Copyright (c) 2021 SUSE LLC +# Copyright (c) 2022 SUSE LLC # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -17,7 +17,7 @@ Name: pigz -Version: 2.6 +Version: 2.7 Release: 0 Summary: Multi-core gzip version License: Zlib @@ -26,20 +26,20 @@ Source0: https://www.zlib.net/%{name}/%{name}-%{version}.tar.gz Source1: https://www.zlib.net/%{name}/%{name}-%{version}-sig.txt#/%{name}-%{version}.tar.gz.asc Source9: %{name}.keyring +Patch0: pigz-2.7-NOTHREAD-tests.patch BuildRequires: zlib-devel %description A parallel implementation of gzip for modern multi-processor, multi-core machines %prep -%setup -q -%autopatch -p1 +%autosetup -p1 %build -%make_build CFLAGS="%{optflags}" +%make_build %check -%make_build tests CFLAGS="%{optflags}" +%make_build tests %install install -Dpm 0755 pigz \ ++++++ pigz-2.7-NOTHREAD-tests.patch ++++++ >From 67fd6e436f4f479aead529a719e24d6864cf1dfa Mon Sep 17 00:00:00 2001 From: Mark Adler <mad...@alumni.caltech.edu> Date: Sun, 16 Jan 2022 09:23:07 -0800 Subject: [PATCH] Fix NOTHREAD compile error for combination code. The ability to combine CRCs is needed for the new summary line when listing multiple gzip members. --- pigz.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/pigz.c b/pigz.c index 01e50e0..f915c4a 100644 --- a/pigz.c +++ b/pigz.c @@ -558,9 +558,7 @@ local struct { int procs; // maximum number of compression threads (>= 1) int setdict; // true to initialize dictionary in each thread size_t block; // uncompressed input size per thread (>= 32K) -#ifndef NOTHREAD crc_t shift; // pre-calculated CRC-32 shift for length block -#endif // saved gzip/zip header data for decompression, testing, and listing time_t stamp; // time stamp from gzip header @@ -1338,9 +1336,6 @@ local long zlib_vernum(void) { return left < 2 ? num << (left << 2) : -1; } -#ifndef NOTHREAD -// -- threaded portions of pigz -- - // -- check value combination routines for parallel calculation -- #define COMB(a,b,c) (g.form == 1 ? adler32_comb(a,b,c) : crc32_comb(a,b,c)) @@ -1421,6 +1416,9 @@ local unsigned long adler32_comb(unsigned long adler1, unsigned long adler2, return sum1 | (sum2 << 16); } +#ifndef NOTHREAD +// -- threaded portions of pigz -- + // -- pool of spaces for buffer management -- // These routines manage a pool of spaces. Each pool specifies a fixed size @@ -4337,11 +4335,11 @@ local void defaults(void) { ZopfliInitOptions(&g.zopts); #endif g.block = 131072UL; // 128K + g.shift = x2nmodp(g.block, 3); #ifdef NOTHREAD g.procs = 1; #else g.procs = nprocs(8); - g.shift = x2nmodp(g.block, 3); #endif g.rsync = 0; // don't do rsync blocking g.setdict = 1; // initialize dictionary each thread -- 2.36.0 ++++++ pigz-2.6.tar.gz -> pigz-2.7.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pigz-2.6/README new/pigz-2.7/README --- old/pigz-2.6/README 2021-02-06 10:00:18.000000000 +0100 +++ new/pigz-2.7/README 2022-01-15 20:18:20.000000000 +0100 @@ -1,4 +1,4 @@ -pigz 2.6 (6 Feb 2021) by Mark Adler +pigz 2.7 (15 Jan 2022) by Mark Adler pigz, which stands for Parallel Implementation of GZip, is a fully functional replacement for gzip that exploits multiple processors and multiple cores to diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pigz-2.6/pigz.1 new/pigz-2.7/pigz.1 --- old/pigz-2.6/pigz.1 2021-02-06 10:00:18.000000000 +0100 +++ new/pigz-2.7/pigz.1 2022-01-15 20:18:20.000000000 +0100 @@ -1,4 +1,4 @@ -.TH PIGZ 1 "February 6, 2021" +.TH PIGZ 1 "January 15, 2022" .SH NAME pigz, unpigz \- compress or expand files .SH SYNOPSIS @@ -97,7 +97,9 @@ .PP All options on the command line are processed before any names are processed. If no names are provided on the command line, or if "-" is given as a name (but -not after "--"), then the input is taken from stdin. +not after "--"), then the input is taken from stdin. If the GZIP or PIGZ +environment variables are set, then options are taken from their values before +any command line options are processed, first from GZIP, then from PIGZ. .PP Compressed files can be restored to their original form using .I pigz -d @@ -240,4 +242,4 @@ warranty. In no event will the author be held liable for any damages arising from the use of this software. .PP -Copyright (C) 2007-2021 Mark Adler <mad...@alumni.caltech.edu> +Copyright (C) 2007-2022 Mark Adler <mad...@alumni.caltech.edu> diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pigz-2.6/pigz.c new/pigz-2.7/pigz.c --- old/pigz-2.6/pigz.c 2021-02-06 10:00:18.000000000 +0100 +++ new/pigz-2.7/pigz.c 2022-01-15 20:18:20.000000000 +0100 @@ -1,6 +1,6 @@ /* pigz.c -- parallel implementation of gzip - * Copyright (C) 2007-2021 Mark Adler - * Version 2.6 6 Feb 2021 Mark Adler + * Copyright (C) 2007-2022 Mark Adler + * Version 2.7 15 Jan 2022 Mark Adler */ /* @@ -198,9 +198,16 @@ 2.6 6 Feb 2021 Add --huffman/-H and --rle/U strategy options Fix issue when compiling for no threads Fail silently on a broken pipe + 2.7 15 Jan 2022 Show time stamp only for the first gzip member + Show totals when listing more than one gzip member + Don't unlink input file if it has other links + Add documentation for environment variables + Fix bug when combining -l with -d + Exit with status of zero if skipping non .gz files + Permit Huffman only (-H) when not compiling with zopfli */ -#define VERSION "pigz 2.6" +#define VERSION "pigz 2.7" /* To-do: - make source portable for Windows, VMS, etc. (see gzip source code) @@ -367,9 +374,11 @@ # include <inttypes.h> // intmax_t, uintmax_t typedef uintmax_t length_t; typedef uint32_t crc_t; + typedef uint_least16_t index_t; #else typedef unsigned long length_t; typedef unsigned long crc_t; + typedef unsigned index_t; #endif #ifdef PIGZ_DEBUG @@ -516,7 +525,7 @@ // Globals (modified by main thread only when it's the only thread). local struct { - int volatile ret; // pigz return code + int ret; // pigz return code char *prog; // name by which pigz was invoked int ind; // input file descriptor int outd; // output file descriptor @@ -582,19 +591,31 @@ #endif } g; -// Display a complaint with the program name on stderr. -local int complain(char *fmt, ...) { - va_list ap; - +local void message(char *fmt, va_list ap) { if (g.verbosity > 0) { fprintf(stderr, "%s: ", g.prog); - va_start(ap, fmt); vfprintf(stderr, fmt, ap); - va_end(ap); putc('\n', stderr); fflush(stderr); } +} + +// Display a complaint with the program name on stderr. +local int complain(char *fmt, ...) { g.ret = 1; + va_list ap; + va_start(ap, fmt); + message(fmt, ap); + va_end(ap); + return 0; +} + +// Same as complain(), but don't force a bad return code. +local int grumble(char *fmt, ...) { + va_list ap; + va_start(ap, fmt); + message(fmt, ap); + va_end(ap); return 0; } @@ -3100,7 +3121,7 @@ strcpy(tag + max - 3, "..."); // convert time stamp to text - if (g.stamp) { + if (g.stamp && !cont) { strcpy(mod, ctime(&g.stamp)); now = time(NULL); if (strcmp(mod + 20, ctime(&now) + 20) != 0) @@ -3139,19 +3160,13 @@ (method == 8 && g.in_tot > (len + (len >> 10) + 12)) || (method == 257 && g.in_tot > len + (len >> 1) + 3)) #if __STDC_VERSION__-0 >= 199901L || __GNUC__-0 >= 3 - printf("%10jd %10jd? unk %s\n", - (intmax_t)g.in_tot, (intmax_t)len, tag); + printf("%10ju %10ju? unk %s\n", g.in_tot, len, tag); else - printf("%10jd %10jd %6.1f%% %s\n", - (intmax_t)g.in_tot, (intmax_t)len, red, tag); + printf("%10ju %10ju %6.1f%% %s\n", g.in_tot, len, red, tag); #else - printf(sizeof(off_t) == sizeof(long) ? - "%10ld %10ld? unk %s\n" : "%10lld %10lld? unk %s\n", - g.in_tot, len, tag); + printf("%10lu %10lu? unk %s\n", g.in_tot, len, tag); else - printf(sizeof(off_t) == sizeof(long) ? - "%10ld %10ld %6.1f%% %s\n" : "%10lld %10lld %6.1f%% %s\n", - g.in_tot, len, red, tag); + printf("%10lu %10lu %6.1f%% %s\n", g.in_tot, len, red, tag); #endif } if (g.verbosity > 1 && g.hcomm != NULL) @@ -3444,12 +3459,14 @@ // and check the gzip, zlib, or zip trailer. local void infchk(void) { int ret, cont, more; - unsigned long check, len; + unsigned long check, len, ktot; z_stream strm; unsigned tmp2; unsigned long tmp4; - length_t clen; + length_t clen, ctot, utot; + ctot = utot = 0; + ktot = CHECK(0L, Z_NULL, 0); cont = more = 0; do { // header already read -- set up for decompression @@ -3576,15 +3593,31 @@ // show file information if requested if (g.list) { + ctot += clen; + utot += g.out_tot; + ktot = COMB(ktot, check, g.out_tot); g.in_tot = clen; show_info(8, check, g.out_tot, cont); - cont = 1; + cont = cont ? 2 : 1; } // if a gzip entry follows a gzip entry, decompress it (don't replace // saved header information from first entry) } while (g.form == 0 && (ret = get_header(0)) == 8); + // show totals if more than one gzip member + if (cont > 1 && g.verbosity > 0) { + if (g.verbosity > 1) + printf(" %08lx ", ktot); + printf( +#if __STDC_VERSION__-0 >= 199901L || __GNUC__-0 >= 3 + "%10ju %10ju %6.1f%% (total)\n", +#else + "%10lu %10lu %6.1f%% (total)\n", +#endif + ctot, utot, 100. * (utot - (double)ctot) / utot); + } + // gzip -cdf copies junk after gzip stream directly to output if (g.form == 0 && ret == -2 && g.force && g.pipeout && g.decode != 2 && !g.list) @@ -3630,7 +3663,7 @@ // memory for unlzw() -- the first 256 entries of prefix[] and suffix[] are // never used, so could have offset the index but it's faster to waste a // little memory - uint_least16_t prefix[65536]; // index to LZW prefix string + index_t prefix[65536]; // index to LZW prefix string unsigned char suffix[65536]; // one-character LZW suffix unsigned char match[65280 + 2]; // buffer for reversed match @@ -3776,7 +3809,7 @@ // link new table entry if (end < mask) { end++; - prefix[end] = (uint_least16_t)prev; + prefix[end] = (index_t)prev; suffix[end] = (unsigned char)final; } @@ -3981,7 +4014,7 @@ // don't compress .gz (or provided suffix) files, unless -f if (!(g.force || g.list || g.decode) && len >= strlen(g.sufx) && strcmp(g.inf + len - strlen(g.sufx), g.sufx) == 0) { - complain("skipping: %s ends with %s", g.inf, g.sufx); + grumble("skipping: %s ends with %s", g.inf, g.sufx); return; } @@ -4007,6 +4040,13 @@ } SET_BINARY_MODE(g.ind); + // if requested, just list information about the input file + if (g.list && g.decode != 2) { + list_info(); + load_end(); + return; + } + // if decoding or testing, try to read gzip header if (g.decode) { in_init(); @@ -4048,13 +4088,6 @@ } } - // if requested, just list information about input file - if (g.list) { - list_info(); - load_end(); - return; - } - // create output file out, descriptor outd if (path == NULL || g.pipeout) { // write to stdout @@ -4177,8 +4210,12 @@ g.outd = -1; // now prevent deletion on interrupt if (g.ind != 0) { copymeta(g.inf, g.outf); - if (!g.keep) - unlink(g.inf); + if (!g.keep) { + if (st.st_nlink > 1 && !g.force) + complain("%s has hard links -- not unlinking", g.inf); + else + unlink(g.inf); + } } if (g.decode && (g.headis & 2) != 0 && g.stamp) touch(g.outf, g.stamp); @@ -4432,14 +4469,16 @@ case 'C': get = 7; break; #ifndef NOZOPFLI case 'F': g.zopts.blocksplittinglast = 1; break; - case 'I': get = 4; break; +#endif case 'H': g.strategy = Z_HUFFMAN_ONLY; break; +#ifndef NOZOPFLI + case 'I': get = 4; break; case 'J': get = 5; break; #endif case 'K': g.form = 2; g.sufx = ".zip"; break; case 'L': puts(VERSION); - puts("Copyright (C) 2007-2021 Mark Adler"); + puts("Copyright (C) 2007-2022 Mark Adler"); puts("Subject to the terms of the zlib license."); puts("No warranty is provided or implied."); exit(0); Binary files old/pigz-2.6/pigz.pdf and new/pigz-2.7/pigz.pdf differ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pigz-2.6/pigz.spec new/pigz-2.7/pigz.spec --- old/pigz-2.6/pigz.spec 2021-02-06 10:00:18.000000000 +0100 +++ new/pigz-2.7/pigz.spec 2022-01-15 20:18:20.000000000 +0100 @@ -1,6 +1,6 @@ Summary: pigz is a parallel implementation of gzip which utilizes multiple cores Name: pigz -Version: 2.6 +Version: 2.7 Release: 1 Source0: %{name}-%{version}.tar.gz License: zlib diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pigz-2.6/try.c new/pigz-2.7/try.c --- old/pigz-2.6/try.c 2015-01-20 05:12:31.000000000 +0100 +++ new/pigz-2.7/try.c 2022-01-15 20:18:20.000000000 +0100 @@ -1,7 +1,5 @@ /* try.c -- try / catch / throw exception handling for C99 - * Copyright (C) 2013, 2015 Mark Adler - * Version 1.2 19 January 2015 - * For conditions of distribution and use, see copyright notice in try.h + * For copyright, version, and conditions of distribution and use, see try.h */ /* See try.h for documentation. This source file provides the global pointer @@ -40,6 +38,7 @@ /* save the thrown information in the try stack before jumping */ try_setup_(); assert(try_stack_ != NULL && "try: naked throw"); + try_stack_->ball.ret = 1; try_stack_->ball.code = code; try_stack_->ball.free = 0; try_stack_->ball.why = fmt; @@ -70,5 +69,5 @@ } /* jump to the end of the nearest enclosing try block */ - longjmp(try_stack_->env, 2); + longjmp(try_stack_->env, 1); } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pigz-2.6/try.h new/pigz-2.7/try.h --- old/pigz-2.6/try.h 2021-02-06 10:00:18.000000000 +0100 +++ new/pigz-2.7/try.h 2022-01-15 20:18:20.000000000 +0100 @@ -1,6 +1,6 @@ /* try.h -- try / catch / throw exception handling for C99 - Copyright (C) 2013, 2015 Mark Adler - Version 1.2 19 January 2015 + Copyright (C) 2013, 2015, 2016, 2021 Mark Adler + Version 1.5 10 April 2021 This software is provided 'as-is', without any express or implied warranty. In no event will the author be held liable for any damages @@ -28,16 +28,18 @@ structure assignment, allowing arbitrary arguments to printf() 1.2 19 Jan 2015 - Obey setjmp() invocation limits from C standard + 1.3 1 Mar 2015 - Add preserve to avoid use of volatile, remove retry + 1.4 2 Jan 2016 - Add no-return attribute to throw() + 1.5 10 Apr 2021 - Portability improvements */ /* To use, include try.h in all source files that use these operations, and - compile and link try.c. If pthread threads are used, then there must be an - #include <pthread.h> in try.h to make the exception handling thread-safe. - (Uncomment the include below.) If threads other than pthread are being - used, then try.h and try.c must be modified to use that environment's - thread-local storage for the try_stack_ pointer. try.h and try.c assume - that the compiler and library conform to the C99 standard, at least with - respect to the use of variadic macro and function arguments. */ + compile and link try.c. By default, pthread threads are used to make the + exception handling thread-safe. If a different threads library is required, + then try.h and try.c must be modified to use that environment's thread-local + storage for the try_stack_ pointer. try.h and try.c assume that the + compiler and library conform to the C99 standard, at least with respect to + the use of variadic macro and function arguments. */ /* try.h provides a try / catch / throw exception handler, which allows @@ -46,12 +48,12 @@ innermost enclosing try, passing the thrown information to the associated catch block. A global try stack is used, to avoid having to pass exception handler information through all of the functions down to the invocations of - throw. The try stack is thread-unique if requested by uncommenting the - pthread.h include below. In addition to the macros try, catch, and throw, - the macros always, retry, punt, and drop, and the type ball_t are created. - All other symbols are of the form try_*_ or TRY_*_, where the final - underscore should avoid conflicts with application symbols. The eight - exposed names can be changed easily in #defines below. + throw. The try stack is thread-unique if pthread is made available. In + addition to the macros try, catch, and throw, the macros preserve, always, + punt, and drop, and the type ball_t are created. All other symbols are of + the form try_*_ or TRY_*_, where the final underscore should avoid conflicts + with application symbols. The eight exposed names can be changed easily in + #defines below. A try block encloses code that may throw an exception with the throw() macro, either directly in the try block or in any function called directly @@ -88,7 +90,7 @@ is the argument of catch. This passes the exception on to the next enclosing try/catch handler. - If a catch block does not always end with a punt(), it should contain a + If a catch block does not always complete with a punt(), it should contain a drop(), where the argument of drop() is the argument of catch. This frees the allocated string made if vsnprintf() was used by throw() to generate the string. If printf() format strings are never used, then drop() is not @@ -104,9 +106,6 @@ A naked break or continue in a try or always block will go directly to the end of that block. - A retry from the try block or from any function called from the try block at - any level of nesting will restart the try block from the beginning. - try is thread-safe when compiled with pthread.h. A throw() in a thread can only be caught in the same thread. If a throw() is attempted from a thread without an enclosing try in that thread, even if in another thread there is @@ -126,16 +125,25 @@ There must not be a return in any try block, nor a goto in any try block that leaves that block. The always block does not catch a return from the try block. There is no check or protection for an improper use of return or - goto. It is up to the user to assure that this doesn't happen. If it does + goto. It is up to the user to assure that this doesn't happen. If it does happen, then the reference to the current try block is left on the try stack, and the next throw which is supposed to go to an enclosing try would instead go to this try, possibly after the enclosing function has returned. Mayhem will then ensue. This may be caught by the longjmp() implementation, which would report "longjmp botch" and then abort. - Any automatic storage variables that are modified in the try block and used - in the catch or always block must be declared volatile. Otherwise their - value in the catch or always block is indeterminate. + Any local automatic storage variables that are modified in the try block and + used in the catch or always block must be declared volatile. Otherwise + their value in the catch or always block is indeterminate. Alternatively, a + preserve block can be inserted after an automatic storage variable is + changed in a try block. The preserve block saves the state of those + variables at the moment preserve is executed, and effectively continues the + try block. The same admonition then applies to variables modified in the + preserve block. The only assurance is that the catch and always block will + have the values of the local automatic storage variables as they were at the + time of the last try or preserve statement, but only if they have not been + modified since the last try or preserve statement. As many preserve blocks + as desired are permitted. Any statements between try and always, between try and catch if there is no always, or between always and catch are part of those respective try or @@ -151,15 +159,58 @@ in try.c should be updated accordingly. If there is no memory allocation in throw(), then drop() can be eliminated. + + Summary: + + Here is the permitted structure, where [] means an optional element + permitted once, and []* means an optional element permitted more than once: + + try { body } [preserve { body }]* [always { clean }] catch (ball) { action } + + body must not contain a goto or return that exits body. ball must be a + variable of the type ball_t. "action" must always execute either a + drop(ball) or a punt(ball). + + A throw() may be executed in body or at any level of functions called by + body, and will be processed by the innermost enclosing try in the same + thread: + + throw(code); + + or, + + throw(code, "string"); + + or, + + throw(code, "format string", ...); + + A throw() whose first argument is not zero, and that is caught by this try, + will execute "clean" followed by "action". Either a throw() whose first + argument is zero and caught by this try, or the normal completion of body, + results in the execution of just "clean", with execution continuing after + the catch block. If "action" does not execute a punt(ball), then execution + continues after the catch block. If "action" does execute a punt(ball), + then what was caught is thrown to the next enclosing try. + + A '} preserve {' must be inserted in body after any local automatic storage + variable is changed in body that can be used in the always or catch block. + Alternatively, such variables may be declared volatile. However the use of + volatile can limit optimization, and has a tendency to propagate compiler + warnings. + + Example usage: ball_t err; - volatile char *temp = NULL; + char *temp = NULL; try { ... do something ... if (ret == -1) throw(1, "bad thing happened to %s\n", me); temp = malloc(sizeof(me) + 1); + } + preserve { if (temp == NULL) throw(2, "out of memory"); ... do more ... @@ -256,7 +307,8 @@ #include <assert.h> #include <setjmp.h> -/* If pthreads are used, uncomment this include to make try thread-safe. */ +/* If a POSIX pthread library is not available, then compile with NOTHREAD + defined. */ #ifndef NOTHREAD # include <pthread.h> #endif @@ -264,10 +316,10 @@ /* The exposed names can be changed here. */ #define ball_t try_ball_t_ #define try TRY_TRY_ +#define preserve TRY_PRESERVE_ #define always TRY_ALWAYS_ #define catch TRY_CATCH_ #define throw TRY_THROW_ -#define retry TRY_RETRY_ #define punt TRY_PUNT_ #define drop TRY_DROP_ @@ -279,6 +331,7 @@ in try.c must also be updated accordingly. As an example, why could be a structure with information for use in the catch block. */ typedef struct { + int ret; /* longjmp() return value */ int code; /* integer code (required) */ int free; /* if true, the message string was allocated */ char *why; /* informational string or NULL */ @@ -314,29 +367,46 @@ #endif /* PTHREAD_ONCE_INIT */ /* Try a block. The block should follow the invocation of try enclosed in { }. - The block must be immediately followed by an always or a catch. You must - not goto or return out of the try block. A naked break or continue in the - try block will go to the end of the block. */ + The block must be immediately followed by a preserve, always, or catch. You + must not goto or return out of the try block. A naked break or continue in + the try block will go to the end of the block. */ #define TRY_TRY_ \ do { \ try_t_ try_this_; \ volatile int try_pushed_ = 1; \ + try_this_.ball.ret = 0; \ try_this_.ball.code = 0; \ try_this_.ball.free = 0; \ try_this_.ball.why = NULL; \ try_setup_(); \ try_this_.next = try_stack_; \ try_stack_set_(&try_this_); \ - if (setjmp(try_this_.env) < 2) \ + if (setjmp(try_this_.env) == 0) \ + do { \ + +/* Preserve local automatic variables that were changed in the try block by + reissuing the setjmp(), replacing the state for the next longjmp(). The + preserve block should be enclosed in { }. The block must be immediately + followed by a preserve, always, or catch. You must not goto or return out + of the preserve block. A naked break or continue in the preserve block will + go to the end of the block. This can only follow a try or another preserve. + preserve effectively saves the state of local automatic variables at threat, + i.e. the register state, at that point so that a subsequent throw() will + restore those variables to that state for the always and catch blocks. + Changes to those variables after the preserve statement may or may not be + reflected in the always and catch blocks. */ +#define TRY_PRESERVE_ \ + } while (0); \ + if (try_this_.ball.ret == 0) if (setjmp(try_this_.env) == 0) \ do { \ /* Execute the code between always and catch, whether or not something was thrown. An always block is optional. If present, the always block must - follow a try block and be followed by a catch block. The always block - should be enclosed in { }. A naked break or continue in the always block - will go to the end of the block. It is permitted to use throw in the always - block, which will fall up to the next enclosing try. However this will - result in a memory leak if the original throw() allocated space for the + follow a try or preserve block and be followed by a catch block. The always + block should be enclosed in { }. A naked break or continue in the always + block will go to the end of the block. It is permitted to use throw in the + always block, which will fall up to the next enclosing try. However this + will result in a memory leak if the original throw() allocated space for the informational string. So it's best to not throw() in an always block. Keep the always block simple. @@ -354,27 +424,29 @@ try_stack_set_(try_this_.next); \ try_pushed_ = 0; \ } \ + if (1) \ do { /* Catch an error thrown in the preceding try block. The catch block must follow catch and its parameter, and must be enclosed in { }. The catch must - immediately follow the try or always block. It is permitted to use throw() - in the catch block, which will fall up to the next enclosing try. However - the ball_t passed by throw() must be freed using drop() before doing another - throw, to avoid a potential memory leak. The parameter of catch must be a - ball_t declared in the function or block containing the catch. It is set to - the parameters of the throw() that jumped to the catch. The catch block is - not executed if the first parameter of the throw() was zero. + immediately follow a try, preserve, or always block. It is permitted to use + throw() in the catch block, which will fall up to the next enclosing try. + However the ball_t passed by throw() must be freed using drop() before doing + another throw, to avoid a potential memory leak. The parameter of catch must + be a ball_t declared in the function or block containing the catch. It is + set to the parameters of the throw() that jumped to the catch. The catch + block is not executed if the first parameter of the throw() was zero. A catch block should end with either a punt() or a drop(). Great care must be taken if the catch block uses an automatic storage variable local to the enclosing function that can be modified in the try - block. Such variables must be declared volatile. If such a variable is not - declared volatile, and if the compiler elects to keep that variable in a - register, then the throw will restore that variable to its state at the - beginning of the try block, wiping out any change that occurred in the try - block. This can cause very confusing bugs until you remember that you + block. Such variables must be declared volatile or preserve must be used to + save their state. If such a variable is not declared volatile, and if the + compiler elects to keep that variable in a register, then the throw will + restore that variable to its state at the beginning of the most recent try + or preserve block, wiping out any change that occurred after the start of + that block. This can cause very confusing bugs until you remember that you didn't follow this rule. */ #define TRY_CATCH_(try_ball_) \ } while (0); \ @@ -418,29 +490,13 @@ to make use of any arguments after the 0 anyway. try.c must be compiled and linked to provide the try_throw_() function. */ -void try_throw_(int code, char *fmt, ...); -#define TRY_THROW_(...) try_throw_(__VA_ARGS__, NULL) +void try_throw_(int code, char *fmt, ...) +#if defined(__GNUC__) || defined(__has_builtin) + __attribute__((noreturn)) +#endif + ; -/* Retry the try block. This will start over at the beginning of the try - block. This can be used in the try block or in any function called from the - try block at any level of nesting, just like throw. retry has no argument. - If there is a retry in the always or catch block, then it will retry the - next enclosing try, not the immediately preceding try. - - If you use this, make sure you have carefully thought through how it will - work. It can be tricky to correctly rerun a chunk of code that has been - partially executed. Especially if there are different degrees of progress - that could have been made. Also note that automatic variables changed in - the try block and not declared volatile will have indeterminate values. - - We use 1 here instead of 0, since some implementations prevent returning a - zero value from longjmp() to setjmp(). */ -#define TRY_RETRY_ \ - do { \ - try_setup_(); \ - assert(try_stack_ != NULL && "try: naked retry"); \ - longjmp(try_stack_->env, 1); \ - } while (0) +#define TRY_THROW_(...) try_throw_(__VA_ARGS__, NULL) /* Punt a caught error on to the next enclosing catcher. This is normally used in a catch block with same argument as the catch. */ @@ -449,7 +505,7 @@ try_setup_(); \ assert(try_stack_ != NULL && "try: naked punt"); \ try_stack_->ball = try_ball_; \ - longjmp(try_stack_->env, 2); \ + longjmp(try_stack_->env, 1); \ } while (0) /* Clean up at the end of the line in a catch (no more punts). */