1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
//! The GNU personality: stuffr behaving as GNU gzip 1.15.
//!
//! **Written from the outside only.** GNU gzip is GPLv3 and stuffr is MIT,
//! so nothing here is ported: every flag, check, message and exit code was
//! established from `gzip --help`, the manual, the NEWS file, and running
//! the binary and watching its stdout, stderr, exit code and file tree.
//! `messages.rs` names the command behind each string.
//!
//! **Names.** GNU installs `gunzip` and `zcat` as shell scripts: when their
//! first argument is exactly `--help` or `--version` they print their own
//! text, and otherwise they exec `gzip -d "$@"` and `gzip -dc "$@"`, so
//! every later message says `gzip:`. `gzcat`, which GNU does not install,
//! behaves as `zcat`.
//!
//! Behaviours reproduced because the binary shows them (each pinned by the
//! differential suite `tests/compat_gzip_gnu.rs`):
//!
//! - a set-user-ID input is refused even under `-f`, a sticky one unless
//! `-f` (whose output then drops the sticky bit); `-c` takes both;
//! - an input that already has a gzip suffix is skipped with a note (under
//! `-r` only with `-v`) and exit 0, not 2; the suffixes are `.gz .z -gz -z _z .tgz .taz` in any
//! case, and `-S`'s is tried before them when it holds an uppercase
//! letter and after them otherwise (which one the note names);
//! - standard input compressed after a file operand stores the name
//! `stdin` (`gzip a -`); before any file it stores none;
//! - standard input that is a regular file stores its modification time;
//! a pipe stores 0, and so does any input that is not a regular file;
//! - `-c` follows a symbolic link and compresses a hard-linked or
//! non-regular file without complaint; without `-c` a link is refused
//! (`O_NOFOLLOW`'s error) unless `-f`;
//! - a terminal on standard output refuses only standard input, and ends
//! the run there (exit 1, later operands untouched);
//! - `-v` prints the name before compressing and the ratio after, so a
//! timestamp warning lands between them.
//!
//! Decompression, `-t` and `-l` (`run/decompress.rs`, `decode.rs`,
//! `unpack.rs`, `list.rs`; each module doc lists what the binary showed):
//!
//! - gzip (and the old magic `1f 9e`), `.Z`, pack and a zip's first entry;
//! members of any of the first three may follow one another;
//! - a missing operand is looked for with a suffix added; `-N` restores the
//! stored name's last component only, beside the input, and the last
//! stored time; a stored name that with the operand's directory part
//! reaches 1024 bytes is "corrupted input -- file name too large" under
//! `-N` to a file and `-lN` (the run ends), while `-dNc`, `-tN` and every
//! mode without `-N` read a stored name or comment of any length;
//! - `-v` and `-l` subtract a first header longer than GNU's 256 KiB input
//! buffer only modulo that buffer (`decode::counted_header`);
//! - which failures end the whole run (truncation, a corrupt body, a
//! corrupt `.Z`, any pack failure, and CRC or length errors under `-d`)
//! and which do not, and that standard input refused at its header ends
//! it too;
//! - a later member whose header is refused keeps the output and removes
//! the input, exit 1 (under `-k` the output goes instead); a run-ending
//! failure in an input's header removes the previous `-k` output;
//! - `-f` copies what is not compressed when the output is standard
//! output, and trailing data after the last member likewise;
//! - `-l` decodes every input in full, prints a totals line in place of an
//! input that does not decode cleanly, quotes names, and dates a piped
//! standard input with the previous input's time.
//!
//! Known deviations:
//!
//! - `--version`/`-V`/`-L` print stuffr's first line and its own MIT notice,
//! not GNU's copyright and licence text (Ruling T7-2).
//! - The compressed bytes are stuffr's deflate, not GNU's (GNU's deflate is
//! its own and GPL): the reference decodes them to the same bytes and the
//! header through FNAME matches, but the size, and so `-v`'s ratio,
//! differ.
//! - `--rsyncable` and `--synchronous` are accepted and change nothing.
//! - Diagnostics quote names as GNU does in the C locale; non-ASCII bytes
//! are always escaped, whatever the locale.
//! - The `gunzip`/`zcat` scripts' `--help` names their own path (`$0`);
//! stuffr prints the `argv[0]` it was given, which for a command found on
//! `PATH` is the bare name, where the shell gives the script its full
//! path.
//! - Output is written under a temporary name and renamed into place, so an
//! interrupted run leaves a `.stuffr-gzip-*.tmp` file.
//! - LZH (`1f a0`, `compress -H`) is not decoded: a first member is "not in
//! gzip format", a later one trailing garbage (Ruling T5).
//! - An output name past the file system's `NAME_MAX` is not shortened:
//! GNU compresses a 253-byte name to a cut-short `.gz` name ("NAME
//! compressed to SHORTER", exit 0) and under `-dN` writes a too-long
//! stored name cut short ("warning, name truncated", exit 2); stuffr
//! says "File name too long", exit 1, and keeps the input (observed:
//! `gzip -k` on 252- to 255-byte names; final review, item 3).
//! - A `.Z` header claiming fewer than 9 bits is "corrupt input." before
//! any output; GNU writes a few bytes first.
//! - A zip refused as "not a valid zip file" with its data-descriptor flag
//! set makes GNU check the *next* input's gzip trailer as a data
//! descriptor ("crc/length error"); that leaked state is not reproduced.
//! - `gzip -dNf` on an input whose stored name is its own name (or, on a
//! case-insensitive or normalising volume, any spelling of it: `X.GZ`
//! for `x.gz`): GNU removes the input before decoding and the output
//! after, losing both whether the body decodes or not; stuffr says and
//! exits the same but keeps the decoded file on success and the
//! untouched input on failure (Rulings T8-4, T8-5). "The same file" is
//! judged by device and inode; off unix, by spelling only.
//! - Read and write failures say `gzip: NAME: strerror`, exit 1, and end
//! the run: the shape every other file error has, not observed.
use OsString;
use Write;
use crateHost;
use ;
/// GNU gzip's `main`, for the invoked `name`; `argv0` is the full
/// `argv[0]`, which getopt's own errors name when `name` is `gzip`.
pub
/// Flush the help or version text, then stop with `code`.