stuffr-cli 0.25.3

The stuffr command line interface
Documentation
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
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
//! Every string the lz4 family prints, as lz4 1.10.0 prints it (C locale;
//! lz4 has no NLS).
//!
//! **Clean room** (spec decision 3): lz4's `programs/` is GPL-2.0-or-later
//! and was never read. Each literal names where it was taken from: the
//! reference binary's own output (`// probe: <command>`, run as
//! `/opt/homebrew/opt/lz4/bin/lz4` 1.10.0 under `LC_ALL=C`), its `-h`/`-H`
//! (`// lz4 -H`), or the man page (`// lz4.1`). Formats follow C's `printf`
//! as [`printf`] renders them: `%s` and `%d` take the next argument (an
//! integer is formatted by the caller).
//!
//! The deliberate changes: the banner names stuffr ([`BANNER`], spec
//! deviation 1), and the benchmark refusal is stuffr's own
//! ([`BENCH_REFUSED`], deviation 5).

pub(super) use crate::compat::host::printf;

// ---- the banner and the help texts ----

/// probe: `lz4 -V` prints `*** lz4 v1.10.0 64-bit multithread, by Yann
/// Collet ***`; spec deviation 1 adds `(stuffr X.Y.Z)` (the argument)
/// after `multithread`, so the prefix `*** lz4 v1.10.0 ` a script reads is
/// unchanged. Printed by `-V` (standard output), first by `-h`/`-H`
/// (standard error), and at `-v`.
pub(super) const BANNER: &str =
    "*** lz4 v1.10.0 64-bit multithread (stuffr %s), by Yann Collet ***\n";

/// probe: `lz4 --bogus`: the line before the short usage on a usage error.
pub(super) const INCORRECT_PARAMETERS: &str = "Incorrect parameters\n";

/// lz4 -h (lines 2-15), also the usage `Incorrect parameters` is followed by
/// (probe: `lz4 --bogus`). `%s` is the program name: the invoked name's last
/// path component, after `/` or `\` (probe: `exec -a 'x\unlz4.exe' lz4 -y`).
pub(super) const USAGE: &str = concat!(
    "Usage : \n",
    "      %s [arg] [input] [output] \n",
    "\n",
    "input   : a filename \n",
    "          with no FILE, or when FILE is - or stdin, read standard input\n",
    "Arguments : \n",
    " -1     : fast compression (default) \n",
    " -12    : slowest compression level \n",
    " -T#    : use # threads for compression (default:0==auto) \n",
    " -d     : decompression (default for .lz4 extension)\n",
    " -f     : overwrite output without prompting \n",
    " -k     : preserve source files(s)  (default) \n",
    "--rm    : remove source file(s) after successful de/compression \n",
    " -h/-H  : display help/long help and exit \n",
);
/// lz4 -h: the rest of the short help, after the usage (lines 16-43), the
/// benchmark lines included (R8: printed though `-b` is refused).
pub(super) const HELP_REST: &str = concat!(
    "\n",
    "Advanced arguments :\n",
    " -V     : display Version number and exit \n",
    " -v     : verbose mode \n",
    " -q     : suppress warnings; specify twice to suppress errors too\n",
    " -c     : force write to standard output, even if it is the console\n",
    " -t     : test compressed file integrity\n",
    " -m     : multiple input files (implies automatic output filenames)\n",
    " -r     : operate recursively on directories (sets also -m) \n",
    " -l     : compress using Legacy format (Linux kernel compression)\n",
    " -z     : force compression \n",
    " -D FILE: use FILE as dictionary (compression & decompression)\n",
    " -B#    : cut file into blocks of size # bytes [32+] \n",
    "                     or predefined block size [4-7] (default: 7) \n",
    " -BI    : Block Independence (default) \n",
    " -BD    : Block dependency (improves compression ratio) \n",
    " -BX    : enable block checksum (default:disabled) \n",
    "--no-frame-crc : disable stream checksum (default:enabled) \n",
    "--content-size : compressed frame includes original size (default:not present)\n",
    "--list FILE : lists information about .lz4 files (useful for files compressed with --content-size flag)\n",
    "--[no-]sparse  : sparse mode (default:enabled on file, disabled on stdout)\n",
    "--favor-decSpeed: compressed files decompress faster, but are less compressed \n",
    "--fast[=#]: switch to ultra fast compression level (default: 1)\n",
    "--best  : same as -12\n",
    "Benchmark arguments : \n",
    " -b#    : benchmark file(s), using # compression level (default : 1) \n",
    " -e#    : test all compression levels from -bX to # (default : 1)\n",
    " -i#    : minimum evaluation time in seconds (default : 3s) \n",
);
/// lz4c -h, lz4c -H: the block a name matching `lz4c` adds after the
/// benchmark lines (probe: `lz4c -h`, `exec -a lz4c.exe lz4 -H`).
pub(super) const HELP_LEGACY: &str = concat!(
    "Legacy arguments : \n",
    " -c0    : fast compression \n",
    " -c1    : high compression \n",
    " -c2,-hc: very high compression \n",
    " -y     : overwrite output without prompting \n",
);
/// lz4 -H: what the long help adds after the short help; `%s` (six times)
/// is the program name.
pub(super) const LONG_HELP: &str = concat!(
    "\n",
    "****************************\n",
    "***** Advanced comment *****\n",
    "****************************\n",
    "\n",
    "Which values can [output] have ? \n",
    "---------------------------------\n",
    "[output] : a filename \n",
    "          'stdout', or '-' for standard output (pipe mode)\n",
    "          'null' to discard output (test mode) \n",
    "[output] can be left empty. In this case, it receives the following value :\n",
    "          - if stdout is not the console, then [output] = stdout \n",
    "          - if stdout is console : \n",
    "               + for compression, output to filename.lz4 \n",
    "               + for decompression, output to filename without '.lz4'\n",
    "                    > if input filename has no '.lz4' extension : error \n",
    "\n",
    "Compression levels : \n",
    "---------------------\n",
    "-0 ... -2  => Fast compression, all identical\n",
    "-3 ... -12 => High compression; higher number == more compression but slower\n",
    "\n",
    "stdin, stdout and the console : \n",
    "--------------------------------\n",
    "To protect the console from binary flooding (bad argument mistake)\n",
    "%s will refuse to read from console, or write to console \n",
    "except if '-c' command is specified, to force output to console \n",
    "\n",
    "Simple example :\n",
    "----------------\n",
    "1 : compress 'filename' fast, using default output name 'filename.lz4'\n",
    "          %s filename\n",
    "\n",
    "Short arguments can be aggregated. For example :\n",
    "----------------------------------\n",
    "2 : compress 'filename' in high compression mode, overwrite output if exists\n",
    "          %s -9 -f filename \n",
    "    is equivalent to :\n",
    "          %s -9f filename \n",
    "\n",
    "%s can be used in 'pure pipe mode'. For example :\n",
    "-------------------------------------\n",
    "3 : compress data stream from 'generator', send result to 'consumer'\n",
    "          generator | %s | consumer \n",
);
/// lz4c -H: the warning a name matching `lz4c` adds at the end of the long
/// help; `%s` (twice) is the program name.
pub(super) const LONG_HELP_LEGACY: &str = concat!(
    "\n",
    "***** Warning  ***** \n",
    "Legacy arguments take precedence. Therefore : \n",
    "--------------------------------- \n",
    "          %s -hc filename \n",
    "means 'compress filename in high compression mode' \n",
    "It is not equivalent to : \n",
    "          %s -h -c filename \n",
    "which displays help text and exits \n",
);

// ---- usage errors (exit 1, display level 1) ----

/// probe: `lz4 a x.lz4 y`, `lz4 -c a b c` (the first operand with no slot).
pub(super) const WONT_BE_USED: &str =
    "Error : %s won't be used ! Do you want multiple input files (-m) ? \n";

/// probe: `lz4 a --threads` (no argument follows).
pub(super) const MISSING_ARGUMENT: &str = "error: missing command argument \n";

/// probe: `lz4 --threads -c a`.
pub(super) const SEPARATED_ARGUMENT: &str =
    "error: command cannot be separated from its argument by another command \n";

/// probe: `lz4 --threads=x`, `lz4 --threads=4x`.
pub(super) const NUMERIC_ONLY: &str =
    "error: only numeric values with optional suffixes K, KB, KiB, M, MB, MiB are allowed \n";

/// probe: `LZ4_CLEVEL=x lz4 -qq -c` (printed before the arguments are
/// read, under every name): the variable's name, then its value.
pub(super) const ENV_IGNORED: &str =
    "Ignore environment variable setting %s=%s: not a valid unsigned value \n";

// ---- parsing notes ----

/// probe: `lz4 -B4 -c` (display level 2, while parsing): the block size
/// of an ID, or of a byte count of 1024 or more, in KiB.
pub(super) const BLOCKS_KB: &str = "using blocks of size %d KB \n";

/// probe: `lz4 -B32 -c`, `lz4 -B1023 -c`: a byte count under 1024.
pub(super) const BLOCKS_BYTES: &str = "using blocks of size %d bytes \n";

/// probe: `lz4 -v -T300 -c a` (display level 3, compression only; printed
/// instead of [`USING_THREADS`] when the count was capped): the count asked
/// for, then the cap.
pub(super) const THREADS_REDUCED: &str =
    "Requested %d threads too large => automatically reduced to %d \n";

// ---- where the output goes (the decision table in `cli.rs`) ----

/// probe: `lz4 -d a`, `unlz4 a`, `lz4 .lz4`; followed by the usage error.
pub(super) const CANNOT_DETERMINE: &str = "Cannot determine an output filename \n";

/// probe: `script -q /dev/null lz4` (standard input a terminal).
pub(super) const STDIN_CONSOLE: &str = "refusing to read from a console\n";

/// probe: `lz4 a -` with standard output a terminal.
pub(super) const STDOUT_CONSOLE: &str = "refusing to write to console without -c \n";

/// probe: `lz4 -m -d b.lz4 a` (per file, the run continues; exit 1).
pub(super) const EXTENSION_MISMATCH: &str =
    "File extension doesn't match expected LZ4_EXTENSION (.lz4); will not process file: %s\n";

/// probe: `lz4 a` (display level 2): a compressed output name derived from
/// the input's.
pub(super) const COMPRESSED_FILENAME: &str = "Compressed filename will be : %s \n";

/// probe: `lz4 -d b.lz4` (display level 2): a decompressed output name
/// derived from the input's.
pub(super) const DECODING_FILE: &str = "Decoding file %s \n";

// ---- files (display level 1 unless noted) ----

/// probe: `lz4 nosuch`, `lz4 -d nosuch.lz4 o`, `lz4 -D nosuch a` (an
/// input or dictionary that cannot be opened): the name as given, then
/// `strerror` and a blank.
pub(super) const OPEN_INPUT_FAILED: &str = "%s: %s \n";

/// probe: `lz4 -f a ro/x`, `lz4 -d b.lz4 nodir/x`, `lz4 -f a d`: an output
/// that cannot be created; no blank before the newline.
pub(super) const OPEN_OUTPUT_FAILED: &str = "%s: %s\n";

/// probe: `lz4 d`, `lz4 -d d.lz4 o`, `lz4 -D d a` (the same text under
/// every name: `unlz4 d.lz4 o` prints `lz4:` too).
pub(super) const IS_A_DIRECTORY: &str = "lz4: %s is a directory -- ignored \n";

/// probe: `lz4 a` with `a.lz4` present (display level 2 and up; standard
/// input is read for the answer whether or not it is a terminal, R11).
pub(super) const OVERWRITE_PROMPT: &str = "%s already exists; do you want to overwrite (y/N) ? ";

/// probe: `lz4 a < /dev/null` with `a.lz4` present: a refused answer
/// (anything but `y` or `Y` first, or end of file).
pub(super) const NOT_OVERWRITTEN: &str = "    not overwritten  \n";

/// probe: `lz4 -q a`, `lz4 -m a`, `lz4 -qqqq a` with `a.lz4` present: below
/// display level 2 there is no question, and this line is printed at every
/// level.
pub(super) const ALREADY_EXISTS: &str = "%s already exists; not overwritten  \n";

/// Spec deviation 3 (stuffr's own text, in lz4's words): an output that is
/// the input file itself is refused, so the input is never lost (lz4
/// truncates it first: probe `lz4 -f a a` leaves an empty frame). Printed
/// at every level, as [`ALREADY_EXISTS`] is.
pub(super) const SAME_FILE: &str = "%s is the input file; not overwritten (stuffr)  \n";

// ---- progress and summaries (display level 2; stderr not a terminal) ----

/// probe: `lz4 a 2>err` (`\r`-refreshed while compressing): MiB read, then
/// the ratio so far as `%.2f` (deviation 2: the harness normalises both).
pub(super) const READ_PROGRESS: &str = "\rRead : %d MiB   ==> %s%%   ";

/// probe: `lz4 -d b.lz4 2>err`: MiB decoded so far.
pub(super) const DECODE_PROGRESS: &str = "\rDecompressed : %d MiB  ";

/// probe: `lz4 a 2>err`, `lz4 -t b.lz4 2>err`: the progress line is
/// cleared (79 blanks) before the summary, even when no refresh was drawn.
pub(super) const CLEAR_LINE: &str = concat!(
    "\r",
    "                                                                               ",
    "\r"
);

/// probe: `lz4 a` (a frame): bytes read, bytes written, the ratio (`%.2f`,
/// of at least one byte read: `lz4 e` says `1500.00%`).
pub(super) const COMPRESSED_SUMMARY: &str = "Compressed %d bytes into %d bytes ==> %s%%\n";

/// probe: `lz4 -l a`: the legacy format's summary has a blank before the
/// newline.
pub(super) const COMPRESSED_LEGACY_SUMMARY: &str = "Compressed %d bytes into %d bytes ==> %s%% \n";

/// probe: `lz4 -t b.lz4`, `lz4 -d sub/x.lz4`: the input name as given
/// (`stdin` for standard input), cut or padded to 30 bytes by the caller
/// (`a_very_long_file_name_more_tha`), then the bytes decoded.
pub(super) const DECODED_SUMMARY: &str = "%s : decoded %d bytes \n";

/// probe: `lz4 -d lg.lz4` (a legacy frame, then bytes that are no frame;
/// display level 2): the run decodes what came before, prints this and
/// the summary, and ends with exit 1. lz4 says where the bytes were only
/// when it can tell (`ftell` on a file: [`AT_POSITION`]); on a pipe the
/// line ends here.
pub(super) const UNDECODABLE: &str = "Stream followed by undecodable data ";

/// probe: `lz4 -d lg.lz4` on a file: the offset just past the four bytes
/// read as a magic number.
pub(super) const AT_POSITION: &str = "at position %d ";

/// The end of [`UNDECODABLE`]'s line.
pub(super) const NEWLINE: &str = "\n";

// ---- `Error N : ...` (display level 1; the run ends with exit N) ----

/// probe: `lz4 -t t_hdr.lz4`, `lz4 -dc g4.lz4` (a frame left unfinished at
/// end of input).
pub(super) const E26_INCOMPLETE: &str =
    "Error 26 : LZ4F frame decoding could not complete: invalid frame \n";

/// probe: `lz4 -D nosuch a`, `lz4 -D d a` (after the open's own line).
pub(super) const E27_DICTIONARY: &str =
    "Error 27 : Dictionary error : could not open dictionary file \n";

/// probe: `lz4 -t bad_hc.lz4`: liblz4's error name (`LZ4F_getErrorName`).
pub(super) const E34_DECOMPRESSION: &str = "Error 34 : Decompression error : %s \n";

/// probe: `lz4 -c big | head -c1` with SIGPIPE ignored.
pub(super) const E38_WRITE_COMPRESSED: &str =
    "Error 38 : Write error : cannot write compressed block \n";

/// probe: `lz4 -c <&-` (closed standard input): the first read of a frame's
/// input failed. Two blanks before the newline.
pub(super) const E40_FIRST_CHUNK: &str =
    "Error 40 : Error reading first chunk (%d bytes) of '%s'  \n";

/// probe: `lz4 -t t_magic.lz4`, a skippable frame then two bytes.
pub(super) const E40_MAGIC_UNREADABLE: &str =
    "Error 40 : Unrecognized header : Magic Number unreadable \n";

/// probe: `lz4 -t skip_hdr_trunc.lz4`.
pub(super) const E42_SKIPPABLE_SIZE: &str =
    "Error 42 : Stream error : skippable size unreadable \n";

/// probe: `cat skip_trunc.lz4 | lz4 -dc` (on a pipe the area is read, and it
/// ends early; a file is seeked past its end instead, silently).
pub(super) const E43_SKIP: &str = "Error 43 : Stream error : cannot skip skippable area \n";

/// probe: `lz4 -d unk_hdr.lz4`.
pub(super) const E44_UNRECOGNIZED: &str =
    "Error 44 : Unrecognized header : file cannot be decoded \n";

/// probe: `lz4 -c --rm < a` (the name removed is `stdin`): the name, then
/// `strerror`.
pub(super) const E50_REMOVE: &str = "Error 50 : Remove error : %s: %s \n";

/// probe: `lz4 -t leg_bsz_trunc.lz4`, `lz4 -t leg_garbage.lz4`.
pub(super) const E61_LEGACY_SIZE: &str =
    "Error 61 : Error: cannot read block size in Legacy format \n";

/// probe: `lz4 -t leg_blk_trunc.lz4`.
pub(super) const E63_LEGACY_BLOCK: &str =
    "Error 63 : Read error : cannot access compressed block ! \n";

/// probe: `lz4 -t leg_bad.lz4`, `lz4 -t legzero.lz4`.
pub(super) const E64_LEGACY_DECODE: &str =
    "Error 64 : Decoding Failed ! Corrupted input detected ! \n";

/// probe: `lz4 --sparse -dc z.lz4 | cat` (all zeros, so only the final
/// skip, which a pipe refuses). The blank line is lz4's.
pub(super) const E69_FINAL_SKIP: &str = "Error 69 : Final skip error (sparse file)\n \n";

/// probe: `lz4cat big.lz4 | head -c1` with SIGPIPE ignored.
pub(super) const E70_WRITE_DECODED: &str = "Error 70 : Write error : cannot write decoded block \n";

/// probe: `lz4 --sparse -dc a8.lz4 | cat` (data in whole words: the skip
/// before it is tried, even an empty one, and a pipe refuses): `errno`, then
/// `strerror`.
pub(super) const E72_SPARSE_SKIP: &str =
    "Error 72 : Sparse skip error(%d): %s ; try --no-sparse \n";

/// probe: `lz4 --sparse -dc z8a.lz4 | cat` (8 zero bytes then `A`: data in
/// the bytes after the last whole word).
pub(super) const E74_SPARSE_TAIL_SKIP: &str = "Error 74 : Sparse skip error ; try --no-sparse \n";

// ---- `--list` (probed on /opt/homebrew/opt/lz4/bin/lz4, 1.10.0) ----

/// probe: `lz4 --list b.lz4` (standard output, once before the first file
/// and even when none is read: `lz4 --list` on standard input prints only
/// this; absent at display level 3).
pub(super) const LIST_HEADER: &str =
    "    Frames           Type Block  Compressed  Uncompressed    Ratio   Filename\n";

/// probe: `lz4 --list cs100.lz4` (a file whose frames all say how big their
/// content is): the cells `%10s %14s %5s %11s %13s %8s` as padded by the
/// caller, the ratio's `%`, two blanks, the file's base name, a blank.
pub(super) const LIST_ROW_KNOWN: &str = "%s %s %s %s %s %s%%  %s \n";

/// probe: `lz4 --list b.lz4` (a content size missing from a frame, or any
/// legacy or skippable frame): the same cells, three blanks, the base name,
/// no trailing blank.
pub(super) const LIST_ROW_UNKNOWN: &str = "%s %s %s %s %s %s   %s\n";

/// probe: `lz4 --list -v b.lz4` (display level 3): `name(i/n)` before each
/// file, the base name.
pub(super) const LIST_FILE_VERBOSE: &str = "%s(%s/%s)\n";

/// probe: `lz4 --list -v b.lz4`: the columns of the per-frame table.
pub(super) const LIST_HEADER_VERBOSE: &str = "     Frame           Type Block Checksum           Compressed         Uncompressed     Ratio\n";

/// probe: `lz4 --list -v cs100.lz4` (a modern frame with a content size):
/// the cells `%10s %14s %5s %8s %20s %20s %9s` as padded by the caller, then
/// the ratio's `%`.
pub(super) const LIST_FRAME_KNOWN: &str = "%s %s %s %s %s %s %s%%\n";

/// probe: `lz4 --list -v b.lz4` (a modern frame with no content size): the
/// same cells and a trailing blank.
pub(super) const LIST_FRAME_UNKNOWN: &str = "%s %s %s %s %s %s %s \n";

/// probe: `lz4 --list -v leg.lz4`, `skip.lz4` (legacy and skippable frames):
/// the same cells and nothing after them.
pub(super) const LIST_FRAME_OTHER: &str = "%s %s %s %s %s %s %s\n";

/// probe: `lz4 --list -v b.lz4`: the blank line after a file's frames (not
/// printed after a file that failed).
pub(super) const LIST_FILE_END: &str = "\n";

/// probe: `lz4 --list b.lz4`: a modern frame's type cell.
pub(super) const TYPE_FRAME: &str = "LZ4Frame";
/// probe: `lz4 --list leg.lz4`: a legacy frame's type cell.
pub(super) const TYPE_LEGACY: &str = "LegacyFrame";
/// probe: `lz4 --list skip.lz4`: a skippable frame's type cell.
pub(super) const TYPE_SKIPPABLE: &str = "SkippableFrame";
/// probe: `lz4 --list leg_then_good.lz4`: no cell, or frames that differ
/// (type, block size, block mode), or a frame with nothing to say.
pub(super) const CELL_NONE: &str = "-";
/// probe: `lz4 --list -v b.lz4`: the checksum cell of a frame with a
/// content checksum (`-` without).
pub(super) const CHECKSUM_XXH32: &str = "XXH32";
/// probe: `lz4 --list b.lz4`: the block cell is `B`, the size ID (4 to 7)
/// and `I` (independent blocks) or `D` (linked: `lz4 -BD`).
pub(super) const BLOCK_INDEPENDENT: &str = "I";
/// probe: `lz4 --list linked.lz4` (a frame header with block-independence
/// off).
pub(super) const BLOCK_LINKED: &str = "D";
/// probe: `lz4 --list b.lz4`: the block cell's `B` before the size ID.
pub(super) const BLOCK_PREFIX: &str = "B";

/// probe: `lz4 --list notlz4`, `lz4 --list badhc.lz4`, `lz4 --list e.lz4`
/// (display level 1 and up; the run ends with exit 1, without reading the
/// files after it).
pub(super) const LIST_NOT_RECOGNIZED: &str = "lz4: %s: File format not recognized \n";

/// probe: `lz4 --list nosuch`, `lz4 --list d`, `lz4 --list /dev/null`,
/// `lz4 --list stdin` (a file named `stdin`: only the operand `-` is
/// standard input in a list).
pub(super) const LIST_NOT_REGULAR: &str = "lz4: %s is not a regular file \n";

/// probe: `lz4 --list leg_huge.lz4` (a legacy block size above 8 MiB, or a
/// size word cut short): printed before [`LIST_NOT_RECOGNIZED`].
pub(super) const LIST_CORRUPTED_LEGACY: &str = "Corrupted legacy frame \n";

/// probe: `lz4 --list c04.lz4` (a frame's magic number and nothing after
/// it): the name as given.
pub(super) const E71_READING: &str = "Error 71 : Error reading %s \n";

/// probe: `lz4 --list c05.lz4` (magic number, then one to three bytes of a
/// header that says it carries a content size).
pub(super) const E72_READING: &str = "Error 72 : Error reading %s \n";

/// probe: `lz4 -vv --list b.lz4` (display level 4, after the banner):
/// `_POSIX_VERSION` as the system reports it (`sysconf(_SC_VERSION)`: on
/// macOS 200112L), twice.
pub(super) const POSIX_VERSION: &str =
    "_POSIX_VERSION defined: %sL\nPLATFORM_POSIX_VERSION defined: %sL\n";

/// probe: `lz4 -vv -c f` on Linux (the 1.10.0 tarball built with `make -C
/// programs lz4` on glibc, Debian `rust:1.95`, and on musl, Alpine 3.20;
/// CI run 39166896170 on ubuntu): a line before the two above, at display
/// level 4. The reference built on macOS prints no such line. The value is
/// the reference build's, 200809L under both C libraries.
#[cfg(target_os = "linux")]
pub(super) const POSIX_C_SOURCE: &str = "_POSIX_C_SOURCE defined: 200809L\n";

// ---- the end of a run ----

/// probe: `lz4 -p -V < /dev/null` (hidden `-p`): printed at a clean exit,
/// whatever the display level, then a byte of standard input is read.
pub(super) const PRESS_ENTER: &str = "Press enter to continue...\n";

/// Spec deviation 5 (stuffr's own text): `-b#` (`-e#` and `-i#` are accepted and ignored).
pub(super) const BENCH_REFUSED: &str = "Benchmark mode is not available in this build (stuffr)\n";

// ---- display levels 3 and 4 (Task 6) ----

/// probe: `lz4 -v -T3 a` (display level 3, compression only, after
/// `Compressed filename will be :` and before the first file): the worker
/// count, when it is above 1 and was not capped (`-T1` prints nothing,
/// `-T200` prints it, `-T201` prints [`THREADS_REDUCED`] instead).
pub(super) const USING_THREADS: &str = "Using %d threads for compression \n";

/// probe: `lz4 -v -l a` (display level 3): after the thread line.
pub(super) const GENERATING_LEGACY: &str = "! Generating LZ4 Legacy format (deprecated) ! \n";

/// probe: `lz4 -v --content-size - o < pipe`, `lz4 -v --content-size
/// /dev/null o` (display level 3, compression of a frame, once the output
/// is open; a regular file, standard input redirected from one included,
/// does not say it).
pub(super) const CONTENT_SIZE_UNKNOWN: &str = "Warning : cannot determine input content size \n";

/// probe: `lz4 -vv - o` (display level 4): standard input is the input,
/// printed as it is opened (before the overwrite question when compressing,
/// after it when decoding).
pub(super) const USING_STDIN: &str = "Using stdin for input \n";

/// probe: `lz4 -vv -c a` (display level 4): standard output is the output.
/// Compressing says it for every file written there; decoding says it once
/// at the start of a run whose output is standard output or which has `-m`
/// operands (`lz4 -vv -d -m a.lz4` says it though it writes `a`).
pub(super) const USING_STDOUT: &str = "Using stdout for output \n";

/// probe: `lz4 -vv -c a`, after [`USING_STDOUT`] (display level 4): said
/// unless `--sparse` or `--no-sparse` was given.
pub(super) const SPARSE_DISABLED: &str = "Sparse File Support automatically disabled on stdout ; to force-enable it, add --sparse command \n";

/// probe: `lz4 -vv -r d` (display level 4, before everything else of the
/// run): each input of the list `-r` made, from 0.
pub(super) const LIST_ENTRY: &str = "%d %s\n";

/// probe: `lz4 -v a`, `lz4 -v -d -m b.lz4` (display level 3): after the
/// last input of a compression (always), of a decode that has `-m`
/// operands, or of a single decode where the input failed. The seconds
/// and the speed are `%.2f`, the load `%.0f` (deviation 2: the harness
/// normalises all three).
pub(super) const DONE_IN: &str = "Done in %s s ==> %s MiB/s  (cpu load : %s%%)\n";

/// probe: `lz4 -vv -t leg.lz4` (display level 4, decoding): a legacy frame's
/// magic number was read; said for each such frame, before its first
/// refresh (not by `--list`).
pub(super) const LEGACY_DETECTED: &str = "Detected : Legacy format \n";

/// probe: `lz4 -vv -t skip_then_frame.lz4` (display level 4, decoding):
/// a skippable frame's magic number was read, before its size is.
pub(super) const SKIPPABLE_DETECTED: &str = "Skipping detected skippable area \n";