:: bzip3 / src / main.c 29.1 KB raw

1
2
/*
3
 * BZip3 - A spiritual successor to BZip2.
4
 * Copyright (C) 2022-2024 Kamila Szewczyk
5
 *
6
 * This program is free software: you can redistribute it and/or modify it
7
 * under the terms of the GNU Lesser General Public License as published by the Free
8
 * Software Foundation, either version 3 of the License, or (at your option)
9
 * any later version.
10
 *
11
 * This program is distributed in the hope that it will be useful, but WITHOUT
12
 * ANY WARRANTY; without even the implied warranty of  MERCHANTABILITY or
13
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
14
 * more details.
15
 *
16
 * You should have received a copy of the GNU Lesser General Public License along with
17
 * this program.  If not, see <http://www.gnu.org/licenses/>.
18
 */
19
20
#include <ctype.h>
21
#include <errno.h>
22
#include <inttypes.h>
23
#include <limits.h>
24
#include <stdio.h>
25
#include <stdlib.h>
26
#include <string.h>
27
#include <sys/stat.h>
28
#include <unistd.h>
29
30
#if defined __MSVCRT__
31
    #include <fcntl.h>
32
    #include <io.h>
33
#endif
34
35
#include "common.h"
36
#include "libbz3.h"
37
#include "yarg.h"
38
39
#define MODE_DECODE 0
40
#define MODE_ENCODE 1
41
#define MODE_TEST 2
42
#define MODE_RECOVER 3
43
44
static void version() {
45
    fprintf(stdout, "bzip3 " VERSION
46
                    "\n"
47
                    "Copyright (C) by Kamila Szewczyk, 2022-2025.\n"
48
                    "License: GNU Lesser GPL version 3 <https://www.gnu.org/licenses/lgpl-3.0.en.html>\n");
49
}
50
51
static void help() {
52
    fprintf(stdout,
53
            "bzip3 - better and stronger spiritual successor to bzip2.\n"
54
            "Usage: bzip3 [-e/-z/-d/-t/-c/-h/-V] [-b block_size] [-j jobs] files...\n"
55
            "Operations:\n"
56
            "  -e/-z, --encode   compress data (default)\n"
57
            "  -d, --decode      decompress data\n"
58
            "  -r, --recover     attempt at recovering corrupted data\n"
59
            "  -t, --test        verify validity of compressed data\n"
60
            "  -h, --help        display an usage overview\n"
61
            "  -f, --force       force overwriting output if it already exists\n"
62
            "      --rm          remove input files after successful (de)compression\n"
63
            "  -k, --keep        keep (don't delete) input files (default)\n"
64
            "  -v, --verbose     verbose mode (display more information)\n"
65
            "  -V, --version     display version information\n"
66
            "Extra flags:\n"
67
            "  -c, --stdout      force writing to standard output\n"
68
            "  -b N, --block=N   set block size in MiB {16}\n"
69
            "  -B, --batch       process all files specified as inputs\n"
70
#ifdef PTHREAD
71
            "  -j N, --jobs=N    set the amount of parallel threads {1}\n"
72
#endif
73
            "\n"
74
            "Report bugs to: https://github.com/iczelia/bzip3\n");
75
}
76
77
static void xwrite(const void * data, size_t size, size_t len, FILE * des) {
78
    if (len == 0 || size == 0) return;
79
    if (fwrite(data, size, len, des) != len) {
80
        fprintf(stderr, "Write error: %s\n", strerror(errno));
81
        exit(1);
82
    }
83
}
84
85
/* Read any amount of items (from 0 to len) as long as there is no error */
86
static size_t xread(void * data, size_t size, size_t len, FILE * des) {
87
    size_t written = fread(data, size, len, des);
88
    if (ferror(des)) {
89
        fprintf(stderr, "Read error: %s\n", strerror(errno));
90
        exit(1);
91
    }
92
    return written;
93
}
94
95
/* Either read 0 (due to eof) items or exactly len items */
96
static size_t xread_eofcheck(void * data, size_t size, size_t len, FILE * des) {
97
    size_t written = xread(data, size, len, des);
98
    /* feof will be true */
99
    if (!written) return 0;
100
    if (feof(des)) {
101
        fprintf(stderr, "Error: Corrupt file\n");
102
        exit(1);
103
    }
104
    return written;
105
}
106
107
/* Always read len items */
108
static void xread_noeof(void * data, size_t size, size_t len, FILE * des) {
109
    if (!xread_eofcheck(data, size, len, des)) {
110
        fprintf(stderr, "Error: Corrupt file\n");
111
        exit(1);
112
    }
113
}
114
115
static void close_out_file(FILE * des) {
116
    if (des) {
117
        int outfd = fileno(des);
118
119
        if (fflush(des)) {
120
            fprintf(stderr, "Error: Failed on fflush: %s\n", strerror(errno));
121
            exit(1);
122
        }
123
124
#ifdef __linux__
125
        while (1) {
126
            int status = fsync(outfd);
127
            if (status == -1) {
128
                if (errno == EINVAL) break;
129
                if (errno == EINTR) continue;
130
                fprintf(stderr, "Error: Failed on fsync: %s\n", strerror(errno));
131
                exit(1);
132
            }
133
            break;
134
        }
135
#endif
136
137
        if (des != stdout && fclose(des)) {
138
            fprintf(stderr, "Error: Failed on fclose: %s\n", strerror(errno));
139
            exit(1);
140
        }
141
    }
142
}
143
144
static void remove_in_file(char * file_name, FILE * output_des) {
145
    if (file_name == NULL) {
146
        return;
147
    }
148
    if (output_des == stdout) {
149
        return;
150
    }
151
    if (remove(file_name)) {
152
        fprintf(stderr, "Error: failed to remove input file `%s': %s\n", file_name, strerror(errno));
153
        exit(1);
154
    }
155
}
156
157
static int process(FILE * input_des, FILE * output_des, int mode, int block_size, int workers, int verbose,
158
                   char * file_name) {
159
    uint64_t bytes_read = 0, bytes_written = 0;
160
161
    if ((mode == MODE_ENCODE && isatty(fileno(output_des))) ||
162
        ((mode == MODE_DECODE || mode == MODE_TEST || mode == MODE_RECOVER) && isatty(fileno(input_des)))) {
163
        fprintf(stderr, "Refusing to read/write binary data from/to the terminal.\n");
164
        return 1;
165
    }
166
167
    // Reset errno after the isatty() call.
168
    errno = 0;
169
170
    u8 byteswap_buf[4];
171
172
    switch (mode) {
173
        case MODE_ENCODE:
174
            xwrite("BZ3v1", 5, 1, output_des);
175
176
            write_neutral_s32(byteswap_buf, block_size);
177
            xwrite(byteswap_buf, 4, 1, output_des);
178
179
            bytes_written += 9;
180
            break;
181
        case MODE_RECOVER:
182
        case MODE_DECODE:
183
        case MODE_TEST: {
184
            char signature[5];
185
186
            if (xread(signature, 5, 1, input_des) != 1 || strncmp(signature, "BZ3v1", 5) != 0) {
187
                fprintf(stderr, "Invalid signature.\n");
188
                return 1;
189
            }
190
191
            xread_noeof(byteswap_buf, 4, 1, input_des);
192
193
            block_size = read_neutral_s32(byteswap_buf);
194
195
            if (block_size < KiB(65) || block_size > MiB(511)) {
196
                fprintf(stderr,
197
                        "The input file is corrupted. Reason: Invalid block "
198
                        "size in the header.\n");
199
                if (mode == MODE_RECOVER) {
200
                    fprintf(stderr, "Recovery mode: Proceeding.\n");
201
                    block_size = MiB(511);
202
                } else {
203
                    return 1;
204
                }
205
            }
206
207
            bytes_read += 9;
208
            break;
209
        }
210
    }
211
212
#ifdef PTHREAD
213
    if (workers > 64 || workers < 0) {
214
        fprintf(stderr, "Number of workers must be between 0 and 64.\n");
215
        return 1;
216
    }
217
218
    if (workers <= 1) {
219
#endif
220
        struct bz3_state * state = bz3_new(block_size);
221
222
        if (state == NULL) {
223
            fprintf(stderr, "Failed to create a block encoder state.\n");
224
            return 1;
225
        }
226
227
        size_t buffer_size = bz3_bound(block_size);
228
        u8 * buffer = malloc(buffer_size);
229
230
        if (!buffer) {
231
            fprintf(stderr, "Failed to allocate memory.\n");
232
            return 1;
233
        }
234
235
        if (mode == MODE_ENCODE) {
236
            s32 read_count;
237
            while (!feof(input_des)) {
238
                read_count = xread(buffer, 1, block_size, input_des);
239
                bytes_read += read_count;
240
241
                if (read_count == 0) break;
242
243
                s32 new_size = bz3_encode_block(state, buffer, read_count);
244
                if (new_size == -1) {
245
                    fprintf(stderr, "Failed to encode a block: %s\n", bz3_strerror(state));
246
                    return 1;
247
                }
248
249
                write_neutral_s32(byteswap_buf, new_size);
250
                xwrite(byteswap_buf, 4, 1, output_des);
251
                write_neutral_s32(byteswap_buf, read_count);
252
                xwrite(byteswap_buf, 4, 1, output_des);
253
                xwrite(buffer, new_size, 1, output_des);
254
                bytes_written += 8 + new_size;
255
            }
256
            fflush(output_des);
257
        } else if (mode == MODE_DECODE) {
258
            s32 new_size, old_size;
259
            while (!feof(input_des)) {
260
                if (!xread_eofcheck(&byteswap_buf, 1, 4, input_des)) continue;
261
262
                new_size = read_neutral_s32(byteswap_buf);
263
                xread_noeof(&byteswap_buf, 1, 4, input_des);
264
                old_size = read_neutral_s32(byteswap_buf);
265
                if (old_size > bz3_bound(block_size) || new_size > bz3_bound(block_size)) {
266
                    fprintf(stderr, "Failed to decode a block: Inconsistent headers.\n");
267
                    return 1;
268
                }
269
                xread_noeof(buffer, 1, new_size, input_des);
270
                bytes_read += 8 + new_size;
271
                if (bz3_decode_block(state, buffer, buffer_size, new_size, old_size) == -1) {
272
                    fprintf(stderr, "Failed to decode a block: %s\n", bz3_strerror(state));
273
                    return 1;
274
                }
275
                xwrite(buffer, old_size, 1, output_des);
276
                bytes_written += old_size;
277
            }
278
            fflush(output_des);
279
        } else if (mode == MODE_RECOVER) {
280
            s32 new_size, old_size;
281
            while (!feof(input_des)) {
282
                if (!xread_eofcheck(&byteswap_buf, 1, 4, input_des)) continue;
283
284
                new_size = read_neutral_s32(byteswap_buf);
285
                xread_noeof(&byteswap_buf, 1, 4, input_des);
286
                old_size = read_neutral_s32(byteswap_buf);
287
                if (old_size > bz3_bound(block_size) || new_size > bz3_bound(block_size)) {
288
                    fprintf(stderr, "Failed to decode a block: Inconsistent headers.\n");
289
                    return 1;
290
                }
291
                xread_noeof(buffer, 1, new_size, input_des);
292
                bytes_read += 8 + new_size;
293
                if (bz3_decode_block(state, buffer, buffer_size, new_size, old_size) == -1) {
294
                    fprintf(stderr, "Writing invalid block: %s\n", bz3_strerror(state));
295
                }
296
                xwrite(buffer, old_size, 1, output_des);
297
                bytes_written += old_size;
298
            }
299
            fflush(output_des);
300
        } else if (mode == MODE_TEST) {
301
            s32 new_size, old_size;
302
            while (!feof(input_des)) {
303
                if (!xread_eofcheck(&byteswap_buf, 1, 4, input_des)) continue;
304
                new_size = read_neutral_s32(byteswap_buf);
305
                xread_noeof(&byteswap_buf, 1, 4, input_des);
306
                old_size = read_neutral_s32(byteswap_buf);
307
                if (old_size > bz3_bound(block_size) || new_size > bz3_bound(block_size)) {
308
                    fprintf(stderr, "Failed to decode a block: Inconsistent headers.\n");
309
                    return 1;
310
                }
311
                xread_noeof(buffer, 1, new_size, input_des);
312
                bytes_read += 8 + new_size;
313
                bytes_written += old_size;
314
                if (bz3_decode_block(state, buffer, buffer_size, new_size, old_size) == -1) {
315
                    fprintf(stderr, "Failed to decode a block: %s\n", bz3_strerror(state));
316
                    return 1;
317
                }
318
            }
319
        }
320
321
        if (bz3_last_error(state) != BZ3_OK && mode != MODE_RECOVER) {
322
            fprintf(stderr, "Failed to read data: %s\n", bz3_strerror(state));
323
            return 1;
324
        }
325
326
        free(buffer);
327
328
        bz3_free(state);
329
#ifdef PTHREAD
330
    } else {
331
        struct bz3_state * states[workers];
332
        u8 * buffers[workers];
333
        s32 sizes[workers];
334
        size_t buffer_sizes[workers];
335
        s32 old_sizes[workers];
336
        for (s32 i = 0; i < workers; i++) {
337
            states[i] = bz3_new(block_size);
338
            if (states[i] == NULL) {
339
                fprintf(stderr, "Failed to create a block encoder state.\n");
340
                return 1;
341
            }
342
            size_t buffer_size = bz3_bound(block_size);
343
            buffer_sizes[i] = buffer_size;
344
            buffers[i] = malloc(buffer_size);
345
            if (!buffers[i]) {
346
                fprintf(stderr, "Failed to allocate memory.\n");
347
                return 1;
348
            }
349
        }
350
351
        if (mode == MODE_ENCODE) {
352
            while (!feof(input_des)) {
353
                s32 i = 0;
354
                for (; i < workers; i++) {
355
                    size_t read_count = xread(buffers[i], 1, block_size, input_des);
356
                    bytes_read += read_count;
357
                    sizes[i] = old_sizes[i] = read_count;
358
                    if (read_count < block_size) {
359
                        i++;
360
                        break;
361
                    }
362
                }
363
                bz3_encode_blocks(states, buffers, sizes, i);
364
                for (s32 j = 0; j < i; j++) {
365
                    if (bz3_last_error(states[j]) != BZ3_OK) {
366
                        fprintf(stderr, "Failed to encode data: %s\n", bz3_strerror(states[j]));
367
                        return 1;
368
                    }
369
                }
370
                for (s32 j = 0; j < i; j++) {
371
                    write_neutral_s32(byteswap_buf, sizes[j]);
372
                    xwrite(byteswap_buf, 4, 1, output_des);
373
                    write_neutral_s32(byteswap_buf, old_sizes[j]);
374
                    xwrite(byteswap_buf, 4, 1, output_des);
375
                    xwrite(buffers[j], sizes[j], 1, output_des);
376
                    bytes_written += 8 + sizes[j];
377
                }
378
            }
379
            fflush(output_des);
380
        } else if (mode == MODE_DECODE) {
381
            while (!feof(input_des)) {
382
                s32 i = 0;
383
                for (; i < workers; i++) {
384
                    if (!xread_eofcheck(&byteswap_buf, 1, 4, input_des)) break;
385
                    sizes[i] = read_neutral_s32(byteswap_buf);
386
                    xread_noeof(&byteswap_buf, 1, 4, input_des);
387
                    old_sizes[i] = read_neutral_s32(byteswap_buf);
388
                    if (old_sizes[i] > bz3_bound(block_size) || sizes[i] > bz3_bound(block_size)) {
389
                        fprintf(stderr, "Failed to decode a block: Inconsistent headers.\n");
390
                        return 1;
391
                    }
392
                    xread_noeof(buffers[i], 1, sizes[i], input_des);
393
                    bytes_read += 8 + sizes[i];
394
                }
395
                bz3_decode_blocks(states, buffers, buffer_sizes, sizes, old_sizes, i);
396
                for (s32 j = 0; j < i; j++) {
397
                    if (bz3_last_error(states[j]) != BZ3_OK) {
398
                        fprintf(stderr, "Failed to decode data: %s\n", bz3_strerror(states[j]));
399
                        return 1;
400
                    }
401
                }
402
                for (s32 j = 0; j < i; j++) {
403
                    xwrite(buffers[j], old_sizes[j], 1, output_des);
404
                    bytes_written += old_sizes[j];
405
                }
406
            }
407
            fflush(output_des);
408
        } else if (mode == MODE_RECOVER) {
409
            while (!feof(input_des)) {
410
                s32 i = 0;
411
                for (; i < workers; i++) {
412
                    if (!xread_eofcheck(&byteswap_buf, 1, 4, input_des)) break;
413
                    sizes[i] = read_neutral_s32(byteswap_buf);
414
                    xread_noeof(&byteswap_buf, 1, 4, input_des);
415
                    old_sizes[i] = read_neutral_s32(byteswap_buf);
416
                    if (old_sizes[i] > bz3_bound(block_size) || sizes[i] > bz3_bound(block_size)) {
417
                        fprintf(stderr, "Failed to decode a block: Inconsistent headers.\n");
418
                        return 1;
419
                    }
420
                    xread_noeof(buffers[i], 1, sizes[i], input_des);
421
                    bytes_read += 8 + sizes[i];
422
                }
423
                bz3_decode_blocks(states, buffers, buffer_sizes, sizes, old_sizes, i);
424
                for (s32 j = 0; j < i; j++) {
425
                    if (bz3_last_error(states[j]) != BZ3_OK) {
426
                        fprintf(stderr, "Writing invalid block: %s\n", bz3_strerror(states[j]));
427
                    }
428
                }
429
                for (s32 j = 0; j < i; j++) {
430
                    xwrite(buffers[j], old_sizes[j], 1, output_des);
431
                    bytes_written += old_sizes[j];
432
                }
433
            }
434
            fflush(output_des);
435
        } else if (mode == MODE_TEST) {
436
            while (!feof(input_des)) {
437
                s32 i = 0;
438
                for (; i < workers; i++) {
439
                    if (!xread_eofcheck(&byteswap_buf, 1, 4, input_des)) break;
440
                    sizes[i] = read_neutral_s32(byteswap_buf);
441
                    xread_noeof(&byteswap_buf, 1, 4, input_des);
442
                    old_sizes[i] = read_neutral_s32(byteswap_buf);
443
                    if (old_sizes[i] > bz3_bound(block_size) || sizes[i] > bz3_bound(block_size)) {
444
                        fprintf(stderr, "Failed to decode a block: Inconsistent headers.\n");
445
                        return 1;
446
                    }
447
                    xread_noeof(buffers[i], 1, sizes[i], input_des);
448
                    bytes_read += 8 + sizes[i];
449
                    bytes_written += old_sizes[i];
450
                }
451
                bz3_decode_blocks(states, buffers, buffer_sizes, sizes, old_sizes, i);
452
                for (s32 j = 0; j < i; j++) {
453
                    if (bz3_last_error(states[j]) != BZ3_OK) {
454
                        fprintf(stderr, "Failed to decode data: %s\n", bz3_strerror(states[j]));
455
                        return 1;
456
                    }
457
                }
458
            }
459
        }
460
461
        for (s32 i = 0; i < workers; i++) {
462
            free(buffers[i]);
463
            bz3_free(states[i]);
464
        }
465
    }
466
#endif
467
468
    if (verbose) {
469
        if (file_name) fprintf(stderr, " %s:", file_name);
470
        if (mode == MODE_ENCODE)
471
            fprintf(stderr, "\t%" PRIu64 " -> %" PRIu64 " bytes, %.2f%%, %.2f bpb\n", bytes_read, bytes_written,
472
                    (double)bytes_written * 100.0 / bytes_read, (double)bytes_written * 8.0 / bytes_read);
473
        else if (mode == MODE_DECODE)
474
            fprintf(stderr, "\t%" PRIu64 " -> %" PRIu64 " bytes, %.2f%%, %.2f bpb\n", bytes_read, bytes_written,
475
                    (double)bytes_read * 100.0 / bytes_written, (double)bytes_read * 8.0 / bytes_written);
476
        else
477
            fprintf(stderr, "\tOK, %" PRIu64 " -> %" PRIu64 " bytes, %.2f%%, %.2f bpb\n", bytes_read, bytes_written,
478
                    (double)bytes_read * 100.0 / bytes_written, (double)bytes_read * 8.0 / bytes_written);
479
    }
480
481
    return 0;
482
}
483
484
static int is_dir(const char * path) {
485
    struct stat sb;
486
    if (stat(path, &sb) == 0 && S_ISDIR(sb.st_mode)) return 1;
487
    return 0;
488
}
489
490
static int is_numeric(const char * str) {
491
    for (; *str; str++)
492
        if (!isdigit(*str)) return 0;
493
    return 1;
494
}
495
496
static FILE * open_output(char * output, int force) {
497
    FILE * output_des = NULL;
498
499
    if (output != NULL) {
500
        if (is_dir(output)) {
501
            fprintf(stderr, "Error: output file `%s' is a directory.\n", output);
502
            exit(1);
503
        }
504
505
        if (access(output, F_OK) == 0) {
506
            if (!force) {
507
                fprintf(stderr, "Error: output file `%s' already exists. Use -f to force overwrite.\n", output);
508
                exit(1);
509
            }
510
        }
511
512
        output_des = fopen(output, "wb");
513
        if (output_des == NULL) {
514
            fprintf(stderr, "Error: failed to open output file `%s': %s\n", output, strerror(errno));
515
            exit(1);
516
        }
517
    } else {
518
        output_des = stdout;
519
    }
520
521
    return output_des;
522
}
523
524
static FILE * open_input(char * input) {
525
    FILE * input_des = NULL;
526
527
    if (input != NULL) {
528
        if (is_dir(input)) {
529
            fprintf(stderr, "Error: input `%s' is a directory.\n", input);
530
            exit(1);
531
        }
532
533
        input_des = fopen(input, "rb");
534
        if (input_des == NULL) {
535
            fprintf(stderr, "Error: failed to open input file `%s': %s\n", input, strerror(errno));
536
            exit(1);
537
        }
538
    } else {
539
        input_des = stdin;
540
    }
541
542
    return input_des;
543
}
544
545
int main(int argc, char * argv[]) {
546
    int mode = MODE_ENCODE;
547
548
    // input and output file names
549
    char *input = NULL, *output = NULL;
550
    char *f1 = NULL, *f2 = NULL;
551
    int force = 0;
552
553
    // command line arguments
554
    int force_stdstreams = 0, workers = 0, batch = 0, verbose = 0, remove_input_file = 0;
555
556
    // the block size
557
    u32 block_size = MiB(16);
558
559
    enum { RM_OPTION = CHAR_MAX + 1 };
560
561
    yarg_options opt[] = {
562
        {       'e', no_argument,       "encode" },
563
        {       'z', no_argument,       "encode" }, /* alias */
564
        {       'd', no_argument,       "decode" },
565
        {       't', no_argument,       "test" },
566
        {       'c', no_argument,       "stdout" },
567
        {       'f', no_argument,       "force" },
568
        {       'r', no_argument,       "recover" },
569
        {       'h', no_argument,       "help" },
570
        { RM_OPTION, no_argument,       "rm" },
571
        {       'k', no_argument,       "keep" },
572
        {       'V', no_argument,       "version" },
573
        {       'v', no_argument,       "verbose" },
574
        {       'b', required_argument, "block" },
575
        {       'B', no_argument,       "batch" },
576
#ifdef PTHREAD
577
        {       'j', required_argument, "jobs" },
578
#endif
579
        {         0, no_argument,       NULL }
580
    };
581
    yarg_settings settings = {
582
        .dash_dash = true,
583
        .style = YARG_STYLE_UNIX,
584
    };
585
    yarg_result * res = yarg_parse(argc, argv, opt, settings);
586
    if (!res) {
587
        fprintf(stderr, "bzip3: out of memory.\n");
588
        return 1;
589
    }
590
    if (res->error) {
591
        fputs(res->error, stderr);
592
        fputs("Try 'bzip3 --help' for more information.\n", stderr);
593
        return 1;
594
    }
595
    // `res' is not freed later on as it has the approximate lifetime
596
    // equal to the lifetime of the program overall.
597
    for (int i = 0; i < res->argc; i++) {
598
        switch(res->args[i].opt) {
599
            case 'e': case 'z': mode = MODE_ENCODE; break;
600
            case 'd': mode = MODE_DECODE; break;
601
            case 'r': mode = MODE_RECOVER; break;
602
            case 't': mode = MODE_TEST; break;
603
            case 'c': force_stdstreams = 1; break;
604
            case 'f': force = 1; break;
605
            case RM_OPTION: remove_input_file = 1; break;
606
            case 'k': break;
607
            case 'h': help(); return 0;
608
            case 'V': version(); return 0;
609
            case 'B': batch = 1; break;
610
            case 'v': verbose = 1; break;
611
            case 'b':
612
                if (!is_numeric(res->args[i].arg)) {
613
                    fprintf(stderr, "bzip3: invalid block size: %s\n", res->args[i].arg);
614
                    return 1;
615
                }
616
                block_size = MiB(atoi(res->args[i].arg));
617
                break;
618
#ifdef PTHREAD
619
            case 'j':
620
                if (!is_numeric(res->args[i].arg)) {
621
                    fprintf(stderr, "bzip3: invalid amount of jobs: %s\n", res->args[i].arg);
622
                    return 1;
623
                }
624
                workers = atoi(res->args[i].arg);
625
                break;
626
#endif
627
        }
628
    }
629
630
#if defined(__MSVCRT__)
631
    setmode(STDIN_FILENO, O_BINARY);
632
    setmode(STDOUT_FILENO, O_BINARY);
633
#endif
634
635
    if (block_size < KiB(65) || block_size > MiB(511)) {
636
        fprintf(stderr, "Block size must be between 65 KiB and 511 MiB.\n");
637
        return 1;
638
    }
639
640
    if (batch && res->pos_argc) {
641
        switch (mode) {
642
            case MODE_ENCODE:
643
                /* Encode each of the files. */
644
                for (int i = 0; i < res->pos_argc; i++) {
645
                    char * arg = res->pos_args[i];
646
647
                    FILE * input_des = open_input(arg);
648
                    char * output_name;
649
                    if (force_stdstreams)
650
                        output_name = NULL;
651
                    else {
652
                        output_name = malloc(strlen(arg) + 5);
653
                        if (!output_name) {
654
                            fprintf(stderr, "Failed to allocate memory.\n");
655
                            return 1;
656
                        }
657
                        strcpy(output_name, arg);
658
                        strcat(output_name, ".bz3");
659
                    }
660
661
                    FILE * output_des = open_output(output_name, force);
662
                    process(input_des, output_des, mode, block_size, workers, verbose, arg);
663
664
                    fclose(input_des);
665
                    close_out_file(output_des);
666
                    if (!force_stdstreams) free(output_name);
667
                    if (remove_input_file) {
668
                        remove_in_file(arg, output_des);
669
                    }
670
                }
671
                break;
672
            case MODE_RECOVER:
673
            case MODE_DECODE:
674
                /* Decode each of the files. */
675
                for (int i = 0; i < res->pos_argc; i++) {
676
                    char * arg = res->pos_args[i];
677
678
                    FILE * input_des = open_input(arg);
679
                    char * output_name;
680
                    if (force_stdstreams)
681
                        output_name = NULL;
682
                    else {
683
                        output_name = malloc(strlen(arg) + 1);
684
                        if (!output_name) {
685
                            fprintf(stderr, "Failed to allocate memory.\n");
686
                            return 1;
687
                        }
688
                        strcpy(output_name, arg);
689
                        if (strlen(output_name) > 4 && !strcmp(output_name + strlen(output_name) - 4, ".bz3"))
690
                            output_name[strlen(output_name) - 4] = 0;
691
                        else {
692
                            fprintf(stderr, "Warning: file %s has an unknown extension, skipping.\n", arg);
693
                            return 1;
694
                        }
695
                    }
696
697
                    FILE * output_des = open_output(output_name, force);
698
                    process(input_des, output_des, mode, block_size, workers, verbose, arg);
699
700
                    fclose(input_des);
701
                    close_out_file(output_des);
702
                    if (!force_stdstreams) free(output_name);
703
                    if (remove_input_file) {
704
                        remove_in_file(arg, output_des);
705
                    }
706
                }
707
                break;
708
            case MODE_TEST:
709
                /* Test each of the files. */
710
                for (int i = 0; i < res->pos_argc; i++) {
711
                    char * arg = res->pos_args[i];
712
713
                    FILE * input_des = open_input(arg);
714
                    process(input_des, NULL, mode, block_size, workers, verbose, arg);
715
                    fclose(input_des);
716
                }
717
                break;
718
        }
719
720
        if (fclose(stdout)) {
721
            fprintf(stderr, "Error: Failed on fclose(stdout): %s\n", strerror(errno));
722
            return 1;
723
        }
724
725
        return 0;
726
    }
727
728
    for (int i = 0; i < res->pos_argc; i++) {
729
        char * arg = res->pos_args[i];
730
731
        if (f1 != NULL && f2 != NULL) {
732
            fprintf(stderr, "Error: too many files specified.\n");
733
            return 1;
734
        }
735
736
        if (f1 == NULL)
737
            f1 = arg;
738
        else
739
            f2 = arg;
740
    }
741
742
    if (f1 == NULL && f2 == NULL)
743
        input = NULL, output = NULL;
744
    else if (mode == MODE_TEST)
745
        input = f1;
746
    else {
747
        if (mode == MODE_ENCODE) {
748
            if (f2 == NULL) {
749
                // encode from f1?
750
                input = f1;
751
                if (force_stdstreams)
752
                    output = NULL;
753
                else {
754
                    output = malloc(strlen(f1) + 5);
755
                    if (!output) {
756
                        fprintf(stderr, "Failed to allocate memory.\n");
757
                        return 1;
758
                    }
759
                    strcpy(output, f1);
760
                    strcat(output, ".bz3");
761
                }
762
            } else {
763
                // encode from f1 to f2.
764
                input = f1;
765
                output = f2;
766
            }
767
        } else if (mode == MODE_DECODE || mode == MODE_RECOVER) {
768
            if (f2 == NULL) {
769
                // decode from f1 to stdout.
770
                input = f1;
771
                if (force_stdstreams)
772
                    output = NULL;
773
                else {
774
                    output = malloc(strlen(f1) + 1);
775
                    if (!output) {
776
                        fprintf(stderr, "Failed to allocate memory.\n");
777
                        return 1;
778
                    }
779
                    strcpy(output, f1);
780
                    if (strlen(output) > 4 && !strcmp(output + strlen(output) - 4, ".bz3"))
781
                        output[strlen(output) - 4] = 0;
782
                    else {
783
                        fprintf(stderr, "Warning: file %s has an unknown extension, skipping.\n", f1);
784
                        return 1;
785
                    }
786
                }
787
            } else {
788
                // decode from f1 to f2.
789
                input = f1;
790
                output = f2;
791
            }
792
        }
793
    }
794
795
    FILE *input_des = NULL, *output_des = NULL;
796
797
    input_des = open_input(input);
798
    output_des = mode != MODE_TEST ? open_output(output, force) : NULL;
799
800
    if (output != f2) free(output);
801
802
    int r = process(input_des, output_des, mode, block_size, workers, verbose, input);
803
804
    fclose(input_des);
805
    close_out_file(output_des);
806
    if (fclose(stdout)) {
807
        fprintf(stderr, "Error: Failed on fclose(stdout): %s\n", strerror(errno));
808
        return 1;
809
    }
810
    if (remove_input_file) {
811
        remove_in_file(input, output_des);
812
    }
813
    return r;
814
}
tab: 248 wrap: offon