xref: /netbsd-src/external/bsd/bzip2/dist/bzip2.c (revision 71c250d5702a925398e578844a51c626acd8de7a)
1 /*	$NetBSD: bzip2.c,v 1.6 2021/08/27 17:31:48 rillig Exp $	*/
2 
3 
4 /*-----------------------------------------------------------*/
5 /*--- A block-sorting, lossless compressor        bzip2.c ---*/
6 /*-----------------------------------------------------------*/
7 
8 /* ------------------------------------------------------------------
9    This file is part of bzip2/libbzip2, a program and library for
10    lossless, block-sorting data compression.
11 
12    bzip2/libbzip2 version 1.0.8 of 13 July 2019
13    Copyright (C) 1996-2019 Julian Seward <jseward@acm.org>
14 
15    Please read the WARNING, DISCLAIMER and PATENTS sections in the
16    README file.
17 
18    This program is released under the terms of the license contained
19    in the file LICENSE.
20    ------------------------------------------------------------------ */
21 
22 
23 /* Place a 1 beside your platform, and 0 elsewhere.
24    Generic 32-bit Unix.
25    Also works on 64-bit Unix boxes.
26    This is the default.
27 */
28 #define BZ_UNIX      1
29 
30 /*--
31   Win32, as seen by Jacob Navia's excellent
32   port of (Chris Fraser & David Hanson)'s excellent
33   lcc compiler.  Or with MS Visual C.
34   This is selected automatically if compiled by a compiler which
35   defines _WIN32, not including the Cygwin GCC.
36 --*/
37 #define BZ_LCCWIN32  0
38 
39 #if defined(_WIN32) && !defined(__CYGWIN__)
40 #undef  BZ_LCCWIN32
41 #define BZ_LCCWIN32 1
42 #undef  BZ_UNIX
43 #define BZ_UNIX 0
44 #endif
45 
46 
47 /*---------------------------------------------*/
48 /*--
49   Some stuff for all platforms.
50 --*/
51 
52 #include <stdio.h>
53 #include <stdlib.h>
54 #include <string.h>
55 #include <signal.h>
56 #include <math.h>
57 #include <errno.h>
58 #include <ctype.h>
59 #include "bzlib.h"
60 
61 #define ERROR_IF_EOF(i)       { if ((i) == EOF)  ioError(); }
62 #define ERROR_IF_NOT_ZERO(i)  { if ((i) != 0)    ioError(); }
63 #define ERROR_IF_MINUS_ONE(i) { if ((i) == (-1)) ioError(); }
64 
65 
66 /*---------------------------------------------*/
67 /*--
68    Platform-specific stuff.
69 --*/
70 
71 #if BZ_UNIX
72 #   include <fcntl.h>
73 #   include <sys/types.h>
74 #   include <utime.h>
75 #   include <unistd.h>
76 #   include <sys/stat.h>
77 #   include <sys/times.h>
78 
79 #   define PATH_SEP    '/'
80 #   define MY_LSTAT    lstat
81 #   define MY_STAT     stat
82 #   define MY_S_ISREG  S_ISREG
83 #   define MY_S_ISDIR  S_ISDIR
84 
85 #   define APPEND_FILESPEC(root, name) \
86       root=snocString((root), (name))
87 
88 #   define APPEND_FLAG(root, name) \
89       root=snocString((root), (name))
90 
91 #   define SET_BINARY_MODE(fd) /**/
92 
93 #   ifdef __GNUC__
94 #      define NORETURN __attribute__ ((noreturn))
95 #   else
96 #      define NORETURN /**/
97 #   endif
98 
99 #   ifdef __DJGPP__
100 #     include <io.h>
101 #     include <fcntl.h>
102 #     undef MY_LSTAT
103 #     undef MY_STAT
104 #     define MY_LSTAT stat
105 #     define MY_STAT stat
106 #     undef SET_BINARY_MODE
107 #     define SET_BINARY_MODE(fd)                        \
108         do {                                            \
109            int retVal = setmode ( fileno ( fd ),        \
110                                   O_BINARY );           \
111            ERROR_IF_MINUS_ONE ( retVal );               \
112         } while ( 0 )
113 #   endif
114 
115 #   ifdef __CYGWIN__
116 #     include <io.h>
117 #     include <fcntl.h>
118 #     undef SET_BINARY_MODE
119 #     define SET_BINARY_MODE(fd)                        \
120         do {                                            \
121            int retVal = setmode ( fileno ( fd ),        \
122                                   O_BINARY );           \
123            ERROR_IF_MINUS_ONE ( retVal );               \
124         } while ( 0 )
125 #   endif
126 #endif /* BZ_UNIX */
127 
128 
129 
130 #if BZ_LCCWIN32
131 #   include <io.h>
132 #   include <fcntl.h>
133 #   include <sys/stat.h>
134 
135 #   define NORETURN       /**/
136 #   define PATH_SEP       '\\'
137 #   define MY_LSTAT       _stati64
138 #   define MY_STAT        _stati64
139 #   define MY_S_ISREG(x)  ((x) & _S_IFREG)
140 #   define MY_S_ISDIR(x)  ((x) & _S_IFDIR)
141 
142 #   define APPEND_FLAG(root, name) \
143       root=snocString((root), (name))
144 
145 #   define APPEND_FILESPEC(root, name)                \
146       root = snocString ((root), (name))
147 
148 #   define SET_BINARY_MODE(fd)                        \
149       do {                                            \
150          int retVal = setmode ( fileno ( fd ),        \
151                                 O_BINARY );           \
152          ERROR_IF_MINUS_ONE ( retVal );               \
153       } while ( 0 )
154 
155 #endif /* BZ_LCCWIN32 */
156 
157 
158 /*---------------------------------------------*/
159 /*--
160   Some more stuff for all platforms :-)
161 --*/
162 
163 typedef char            Char;
164 typedef unsigned char   Bool;
165 typedef unsigned char   UChar;
166 typedef int             Int32;
167 typedef unsigned int    UInt32;
168 typedef short           Int16;
169 typedef unsigned short  UInt16;
170 
171 #define True  ((Bool)1)
172 #define False ((Bool)0)
173 
174 /*--
175   IntNative is your platform's `native' int size.
176   Only here to avoid probs with 64-bit platforms.
177 --*/
178 typedef int IntNative;
179 
180 
181 /*---------------------------------------------------*/
182 /*--- Misc (file handling) data decls             ---*/
183 /*---------------------------------------------------*/
184 
185 Int32   verbosity;
186 Bool    keepInputFiles, smallMode, deleteOutputOnInterrupt;
187 Bool    forceOverwrite, testFailsExist, unzFailsExist, noisy;
188 Int32   numFileNames, numFilesProcessed, blockSize100k;
189 Int32   exitValue;
190 
191 /*-- source modes; F==file, I==stdin, O==stdout --*/
192 #define SM_I2O           1
193 #define SM_F2O           2
194 #define SM_F2F           3
195 
196 /*-- operation modes --*/
197 #define OM_Z             1
198 #define OM_UNZ           2
199 #define OM_TEST          3
200 
201 Int32   opMode;
202 Int32   srcMode;
203 
204 #define FILE_NAME_LEN 1034
205 
206 Int32   longestFileName;
207 Char    inName [FILE_NAME_LEN];
208 Char    outName[FILE_NAME_LEN];
209 Char    tmpName[FILE_NAME_LEN];
210 Char    *progName;
211 Char    progNameReally[FILE_NAME_LEN];
212 FILE    *outputHandleJustInCase;
213 Int32   workFactor;
214 
215 static void    panic                 ( const Char* ) NORETURN;
216 static void    ioError               ( void )        NORETURN;
217 static void    outOfMemory           ( void )        NORETURN;
218 static void    configError           ( void )        NORETURN;
219 static void    crcError              ( void )        NORETURN;
220 static void    cleanUpAndFail        ( Int32 )       NORETURN;
221 static void    compressedStreamEOF   ( void )        NORETURN;
222 
223 static void    copyFileName ( Char*, const Char* );
224 static void*   myMalloc     ( Int32 );
225 static void    applySavedFileAttrToOutputFile ( IntNative fd );
226 
227 
228 static FILE* fopen_output_safely ( Char*, const char* );
229 
230 /*---------------------------------------------------*/
231 /*--- An implementation of 64-bit ints.  Sigh.    ---*/
232 /*--- Roll on widespread deployment of ANSI C9X ! ---*/
233 /*---------------------------------------------------*/
234 
235 typedef
236    struct { UChar b[8]; }
237    UInt64;
238 
239 
240 static
uInt64_from_UInt32s(UInt64 * n,UInt32 lo32,UInt32 hi32)241 void uInt64_from_UInt32s ( UInt64* n, UInt32 lo32, UInt32 hi32 )
242 {
243    n->b[7] = (UChar)((hi32 >> 24) & 0xFF);
244    n->b[6] = (UChar)((hi32 >> 16) & 0xFF);
245    n->b[5] = (UChar)((hi32 >> 8)  & 0xFF);
246    n->b[4] = (UChar) (hi32        & 0xFF);
247    n->b[3] = (UChar)((lo32 >> 24) & 0xFF);
248    n->b[2] = (UChar)((lo32 >> 16) & 0xFF);
249    n->b[1] = (UChar)((lo32 >> 8)  & 0xFF);
250    n->b[0] = (UChar) (lo32        & 0xFF);
251 }
252 
253 
254 static
uInt64_to_double(UInt64 * n)255 double uInt64_to_double ( UInt64* n )
256 {
257    Int32  i;
258    double base = 1.0;
259    double sum  = 0.0;
260    for (i = 0; i < 8; i++) {
261       sum  += base * (double)(n->b[i]);
262       base *= 256.0;
263    }
264    return sum;
265 }
266 
267 
268 static
uInt64_isZero(UInt64 * n)269 Bool uInt64_isZero ( UInt64* n )
270 {
271    Int32 i;
272    for (i = 0; i < 8; i++)
273       if (n->b[i] != 0) return 0;
274    return 1;
275 }
276 
277 
278 /* Divide *n by 10, and return the remainder.  */
279 static
uInt64_qrm10(UInt64 * n)280 Int32 uInt64_qrm10 ( UInt64* n )
281 {
282    UInt32 rem, tmp;
283    Int32  i;
284    rem = 0;
285    for (i = 7; i >= 0; i--) {
286       tmp = rem * 256 + n->b[i];
287       n->b[i] = tmp / 10;
288       rem = tmp % 10;
289    }
290    return rem;
291 }
292 
293 
294 /* ... and the Whole Entire Point of all this UInt64 stuff is
295    so that we can supply the following function.
296 */
297 static
uInt64_toAscii(char * outbuf,UInt64 * n)298 void uInt64_toAscii ( char* outbuf, UInt64* n )
299 {
300    Int32  i, q;
301    UChar  buf[32];
302    Int32  nBuf   = 0;
303    UInt64 n_copy = *n;
304    do {
305       q = uInt64_qrm10 ( &n_copy );
306       buf[nBuf] = q + '0';
307       nBuf++;
308    } while (!uInt64_isZero(&n_copy));
309    outbuf[nBuf] = 0;
310    for (i = 0; i < nBuf; i++)
311       outbuf[i] = buf[nBuf-i-1];
312 }
313 
314 
315 /*---------------------------------------------------*/
316 /*--- Processing of complete files and streams    ---*/
317 /*---------------------------------------------------*/
318 
319 /*---------------------------------------------*/
320 static
myfeof(FILE * f)321 Bool myfeof ( FILE* f )
322 {
323    Int32 c = fgetc ( f );
324    if (c == EOF) return True;
325    ungetc ( c, f );
326    return False;
327 }
328 
329 
330 /*---------------------------------------------*/
331 static
compressStream(FILE * stream,FILE * zStream)332 void compressStream ( FILE *stream, FILE *zStream )
333 {
334    BZFILE* bzf = NULL;
335    UChar   ibuf[5000];
336    Int32   nIbuf;
337    UInt32  nbytes_in_lo32, nbytes_in_hi32;
338    UInt32  nbytes_out_lo32, nbytes_out_hi32;
339    Int32   bzerr, bzerr_dummy, ret;
340 
341    SET_BINARY_MODE(stream);
342    SET_BINARY_MODE(zStream);
343 
344    if (ferror(stream)) goto errhandler_io;
345    if (ferror(zStream)) goto errhandler_io;
346 
347    bzf = BZ2_bzWriteOpen ( &bzerr, zStream,
348                            blockSize100k, verbosity, workFactor );
349    if (bzerr != BZ_OK) goto errhandler;
350 
351    if (verbosity >= 2) fprintf ( stderr, "\n" );
352 
353    while (True) {
354 
355       if (myfeof(stream)) break;
356       nIbuf = fread ( ibuf, sizeof(UChar), 5000, stream );
357       if (ferror(stream)) goto errhandler_io;
358       if (nIbuf > 0) BZ2_bzWrite ( &bzerr, bzf, (void*)ibuf, nIbuf );
359       if (bzerr != BZ_OK) goto errhandler;
360 
361    }
362 
363    BZ2_bzWriteClose64 ( &bzerr, bzf, 0,
364                         &nbytes_in_lo32, &nbytes_in_hi32,
365                         &nbytes_out_lo32, &nbytes_out_hi32 );
366    if (bzerr != BZ_OK) goto errhandler;
367 
368    if (ferror(zStream)) goto errhandler_io;
369    ret = fflush ( zStream );
370    if (ret == EOF) goto errhandler_io;
371    if (zStream != stdout) {
372       Int32 fd = fileno ( zStream );
373       if (fd < 0) goto errhandler_io;
374       applySavedFileAttrToOutputFile ( fd );
375       ret = fclose ( zStream );
376       outputHandleJustInCase = NULL;
377       if (ret == EOF) goto errhandler_io;
378    }
379    outputHandleJustInCase = NULL;
380    if (ferror(stream)) goto errhandler_io;
381    ret = fclose ( stream );
382    if (ret == EOF) goto errhandler_io;
383 
384    if (verbosity >= 1) {
385       if (nbytes_in_lo32 == 0 && nbytes_in_hi32 == 0) {
386 	 fprintf ( stderr, " no data compressed.\n");
387       } else {
388 	 Char   buf_nin[32], buf_nout[32];
389 	 UInt64 nbytes_in,   nbytes_out;
390 	 double nbytes_in_d, nbytes_out_d;
391 	 uInt64_from_UInt32s ( &nbytes_in,
392 			       nbytes_in_lo32, nbytes_in_hi32 );
393 	 uInt64_from_UInt32s ( &nbytes_out,
394 			       nbytes_out_lo32, nbytes_out_hi32 );
395 	 nbytes_in_d  = uInt64_to_double ( &nbytes_in );
396 	 nbytes_out_d = uInt64_to_double ( &nbytes_out );
397 	 uInt64_toAscii ( buf_nin, &nbytes_in );
398 	 uInt64_toAscii ( buf_nout, &nbytes_out );
399 	 fprintf ( stderr, "%6.3f:1, %6.3f bits/byte, "
400 		   "%5.2f%% saved, %s in, %s out.\n",
401 		   nbytes_in_d / nbytes_out_d,
402 		   (8.0 * nbytes_out_d) / nbytes_in_d,
403 		   100.0 * (1.0 - nbytes_out_d / nbytes_in_d),
404 		   buf_nin,
405 		   buf_nout
406 		 );
407       }
408    }
409 
410    return;
411 
412    errhandler:
413    BZ2_bzWriteClose64 ( &bzerr_dummy, bzf, 1,
414                         &nbytes_in_lo32, &nbytes_in_hi32,
415                         &nbytes_out_lo32, &nbytes_out_hi32 );
416    switch (bzerr) {
417       case BZ_CONFIG_ERROR:
418          configError(); break;
419       case BZ_MEM_ERROR:
420          outOfMemory (); break;
421       case BZ_IO_ERROR:
422          errhandler_io:
423          ioError(); break;
424       default:
425          panic ( "compress:unexpected error" );
426    }
427 
428    panic ( "compress:end" );
429    /*notreached*/
430 }
431 
432 
433 
434 /*---------------------------------------------*/
435 static
uncompressStream(FILE * zStream,FILE * stream)436 Bool uncompressStream ( FILE *zStream, FILE *stream )
437 {
438    BZFILE* bzf = NULL;
439    Int32   bzerr, bzerr_dummy, ret, nread, streamNo, i;
440    UChar   obuf[5000];
441    UChar   unused[BZ_MAX_UNUSED];
442    Int32   nUnused;
443    void*   unusedTmpV = NULL;
444    UChar*  unusedTmp;
445 
446    nUnused = 0;
447    streamNo = 0;
448 
449    SET_BINARY_MODE(stream);
450    SET_BINARY_MODE(zStream);
451 
452    if (ferror(stream)) goto errhandler_io;
453    if (ferror(zStream)) goto errhandler_io;
454 
455    while (True) {
456 
457       bzf = BZ2_bzReadOpen (
458                &bzerr, zStream, verbosity,
459                (int)smallMode, unused, nUnused
460             );
461       if (bzf == NULL || bzerr != BZ_OK) goto errhandler;
462       streamNo++;
463 
464       while (bzerr == BZ_OK) {
465          nread = BZ2_bzRead ( &bzerr, bzf, obuf, 5000 );
466          if (bzerr == BZ_DATA_ERROR_MAGIC) goto trycat;
467          if ((bzerr == BZ_OK || bzerr == BZ_STREAM_END) && nread > 0)
468             fwrite ( obuf, sizeof(UChar), nread, stream );
469          if (ferror(stream)) goto errhandler_io;
470       }
471       if (bzerr != BZ_STREAM_END) goto errhandler;
472 
473       BZ2_bzReadGetUnused ( &bzerr, bzf, (void*)(&unusedTmpV), &nUnused );
474       if (bzerr != BZ_OK) panic ( "decompress:bzReadGetUnused" );
475 
476       unusedTmp = (UChar*)unusedTmpV;
477       for (i = 0; i < nUnused; i++) unused[i] = unusedTmp[i];
478 
479       BZ2_bzReadClose ( &bzerr, bzf );
480       if (bzerr != BZ_OK) panic ( "decompress:bzReadGetUnused" );
481 
482       if (nUnused == 0 && myfeof(zStream)) break;
483    }
484 
485    closeok:
486    if (ferror(zStream)) goto errhandler_io;
487    if (stream != stdout) {
488       Int32 fd = fileno ( stream );
489       if (fd < 0) goto errhandler_io;
490       applySavedFileAttrToOutputFile ( fd );
491    }
492    ret = fclose ( zStream );
493    if (ret == EOF) goto errhandler_io;
494 
495    if (ferror(stream)) goto errhandler_io;
496    ret = fflush ( stream );
497    if (ret != 0) goto errhandler_io;
498    if (stream != stdout) {
499       ret = fclose ( stream );
500       outputHandleJustInCase = NULL;
501       if (ret == EOF) goto errhandler_io;
502    }
503    outputHandleJustInCase = NULL;
504    if (verbosity >= 2) fprintf ( stderr, "\n    " );
505    return True;
506 
507    trycat:
508    if (forceOverwrite) {
509       rewind(zStream);
510       while (True) {
511       	 if (myfeof(zStream)) break;
512       	 nread = fread ( obuf, sizeof(UChar), 5000, zStream );
513       	 if (ferror(zStream)) goto errhandler_io;
514       	 if (nread > 0) fwrite ( obuf, sizeof(UChar), nread, stream );
515       	 if (ferror(stream)) goto errhandler_io;
516       }
517       goto closeok;
518    }
519 
520    errhandler:
521    BZ2_bzReadClose ( &bzerr_dummy, bzf );
522    switch (bzerr) {
523       case BZ_CONFIG_ERROR:
524          configError(); break;
525       case BZ_IO_ERROR:
526          errhandler_io:
527          ioError(); break;
528       case BZ_DATA_ERROR:
529          crcError();
530       case BZ_MEM_ERROR:
531          outOfMemory();
532       case BZ_UNEXPECTED_EOF:
533          compressedStreamEOF();
534       case BZ_DATA_ERROR_MAGIC:
535          if (zStream != stdin) fclose(zStream);
536          if (stream != stdout) fclose(stream);
537          if (streamNo == 1) {
538             return False;
539          } else {
540             if (noisy)
541             fprintf ( stderr,
542                       "\n%s: %s: trailing garbage after EOF ignored\n",
543                       progName, inName );
544             return True;
545          }
546       default:
547          panic ( "decompress:unexpected error" );
548    }
549 
550    panic ( "decompress:end" );
551    return True; /*notreached*/
552 }
553 
554 
555 /*---------------------------------------------*/
556 static
testStream(FILE * zStream)557 Bool testStream ( FILE *zStream )
558 {
559    BZFILE* bzf = NULL;
560    Int32   bzerr, bzerr_dummy, ret, streamNo, i;
561    UChar   obuf[5000];
562    UChar   unused[BZ_MAX_UNUSED];
563    Int32   nUnused;
564    void*   unusedTmpV = NULL;
565    UChar*  unusedTmp = NULL;
566 
567    nUnused = 0;
568    streamNo = 0;
569 
570    SET_BINARY_MODE(zStream);
571    if (ferror(zStream)) goto errhandler_io;
572 
573    while (True) {
574 
575       bzf = BZ2_bzReadOpen (
576                &bzerr, zStream, verbosity,
577                (int)smallMode, unused, nUnused
578             );
579       if (bzf == NULL || bzerr != BZ_OK) goto errhandler;
580       streamNo++;
581 
582       while (bzerr == BZ_OK) {
583          (void)BZ2_bzRead ( &bzerr, bzf, obuf, 5000 );
584          if (bzerr == BZ_DATA_ERROR_MAGIC) goto errhandler;
585       }
586       if (bzerr != BZ_STREAM_END) goto errhandler;
587 
588       BZ2_bzReadGetUnused ( &bzerr, bzf, (void*)(&unusedTmpV), &nUnused );
589       if (bzerr != BZ_OK) panic ( "test:bzReadGetUnused" );
590 
591       unusedTmp = (UChar*)unusedTmpV;
592       for (i = 0; i < nUnused; i++) unused[i] = unusedTmp[i];
593 
594       BZ2_bzReadClose ( &bzerr, bzf );
595       if (bzerr != BZ_OK) panic ( "test:bzReadGetUnused" );
596       if (nUnused == 0 && myfeof(zStream)) break;
597 
598    }
599 
600    if (ferror(zStream)) goto errhandler_io;
601    ret = fclose ( zStream );
602    if (ret == EOF) goto errhandler_io;
603 
604    if (verbosity >= 2) fprintf ( stderr, "\n    " );
605    return True;
606 
607    errhandler:
608    BZ2_bzReadClose ( &bzerr_dummy, bzf );
609    if (verbosity == 0)
610       fprintf ( stderr, "%s: %s: ", progName, inName );
611    switch (bzerr) {
612       case BZ_CONFIG_ERROR:
613          configError(); break;
614       case BZ_IO_ERROR:
615          errhandler_io:
616          ioError(); break;
617       case BZ_DATA_ERROR:
618          fprintf ( stderr,
619                    "data integrity (CRC) error in data\n" );
620          return False;
621       case BZ_MEM_ERROR:
622          outOfMemory();
623       case BZ_UNEXPECTED_EOF:
624          fprintf ( stderr,
625                    "file ends unexpectedly\n" );
626          return False;
627       case BZ_DATA_ERROR_MAGIC:
628          if (zStream != stdin) fclose(zStream);
629          if (streamNo == 1) {
630           fprintf ( stderr,
631                     "bad magic number (file not created by bzip2)\n" );
632             return False;
633          } else {
634             if (noisy)
635             fprintf ( stderr,
636                       "trailing garbage after EOF ignored\n" );
637             return True;
638          }
639       default:
640          panic ( "test:unexpected error" );
641    }
642 
643    panic ( "test:end" );
644    return True; /*notreached*/
645 }
646 
647 
648 /*---------------------------------------------------*/
649 /*--- Error [non-] handling grunge                ---*/
650 /*---------------------------------------------------*/
651 
652 /*---------------------------------------------*/
653 static
setExit(Int32 v)654 void setExit ( Int32 v )
655 {
656    if (v > exitValue) exitValue = v;
657 }
658 
659 
660 /*---------------------------------------------*/
661 static
cadvise(void)662 void cadvise ( void )
663 {
664    if (noisy)
665    fprintf (
666       stderr,
667       "\nIt is possible that the compressed file(s) have become corrupted.\n"
668         "You can use the -tvv option to test integrity of such files.\n\n"
669         "You can use the `bzip2recover' program to attempt to recover\n"
670         "data from undamaged sections of corrupted files.\n\n"
671     );
672 }
673 
674 
675 /*---------------------------------------------*/
676 static
showFileNames(void)677 void showFileNames ( void )
678 {
679    if (noisy)
680    fprintf (
681       stderr,
682       "\tInput file = %s, output file = %s\n",
683       inName, outName
684    );
685 }
686 
687 
688 /*---------------------------------------------*/
689 static
cleanUpAndFail(Int32 ec)690 void cleanUpAndFail ( Int32 ec )
691 {
692    IntNative      retVal;
693    struct MY_STAT statBuf;
694 
695    if ( srcMode == SM_F2F
696         && opMode != OM_TEST
697         && deleteOutputOnInterrupt ) {
698 
699       /* Check whether input file still exists.  Delete output file
700          only if input exists to avoid loss of data.  Joerg Prante, 5
701          January 2002.  (JRS 06-Jan-2002: other changes in 1.0.2 mean
702          this is less likely to happen.  But to be ultra-paranoid, we
703          do the check anyway.)  */
704       retVal = MY_STAT ( inName, &statBuf );
705       if (retVal == 0) {
706          if (noisy)
707             fprintf ( stderr,
708                       "%s: Deleting output file %s, if it exists.\n",
709                       progName, outName );
710          if (outputHandleJustInCase != NULL)
711             fclose ( outputHandleJustInCase );
712          retVal = remove ( outName );
713          if (retVal != 0)
714             fprintf ( stderr,
715                       "%s: WARNING: deletion of output file "
716                       "(apparently) failed.\n",
717                       progName );
718       } else {
719          fprintf ( stderr,
720                    "%s: WARNING: deletion of output file suppressed\n",
721                     progName );
722          fprintf ( stderr,
723                    "%s:    since input file no longer exists.  Output file\n",
724                    progName );
725          fprintf ( stderr,
726                    "%s:    `%s' may be incomplete.\n",
727                    progName, outName );
728          fprintf ( stderr,
729                    "%s:    I suggest doing an integrity test (bzip2 -tv)"
730                    " of it.\n",
731                    progName );
732       }
733    }
734 
735    if (noisy && numFileNames > 0 && numFilesProcessed < numFileNames) {
736       fprintf ( stderr,
737                 "%s: WARNING: some files have not been processed:\n"
738                 "%s:    %d specified on command line, %d not processed yet.\n\n",
739                 progName, progName,
740                 numFileNames, numFileNames - numFilesProcessed );
741    }
742    setExit(ec);
743    exit(exitValue);
744 }
745 
746 
747 /*---------------------------------------------*/
748 static
panic(const Char * s)749 void panic ( const Char* s )
750 {
751    fprintf ( stderr,
752              "\n%s: PANIC -- internal consistency error:\n"
753              "\t%s\n"
754              "\tThis is a BUG.  Please report it to:\n"
755              "\tbzip2-devel@sourceware.org\n",
756              progName, s );
757    showFileNames();
758    cleanUpAndFail( 3 );
759 }
760 
761 
762 /*---------------------------------------------*/
763 static
crcError(void)764 void crcError ( void )
765 {
766    fprintf ( stderr,
767              "\n%s: Data integrity error when decompressing.\n",
768              progName );
769    showFileNames();
770    cadvise();
771    cleanUpAndFail( 2 );
772 }
773 
774 
775 /*---------------------------------------------*/
776 static
compressedStreamEOF(void)777 void compressedStreamEOF ( void )
778 {
779   if (noisy) {
780     fprintf ( stderr,
781 	      "\n%s: Compressed file ends unexpectedly;\n\t"
782 	      "perhaps it is corrupted?  *Possible* reason follows.\n",
783 	      progName );
784     perror ( progName );
785     showFileNames();
786     cadvise();
787   }
788   cleanUpAndFail( 2 );
789 }
790 
791 
792 /*---------------------------------------------*/
793 static
ioError(void)794 void ioError ( void )
795 {
796    fprintf ( stderr,
797              "\n%s: I/O or other error, bailing out.  "
798              "Possible reason follows.\n",
799              progName );
800    perror ( progName );
801    showFileNames();
802    cleanUpAndFail( 1 );
803 }
804 
805 
806 /*---------------------------------------------*/
807 NORETURN static
mySignalCatcher(IntNative n)808 void mySignalCatcher ( IntNative n )
809 {
810    fprintf ( stderr,
811              "\n%s: Control-C or similar caught, quitting.\n",
812              progName );
813    cleanUpAndFail(1);
814 }
815 
816 
817 /*---------------------------------------------*/
818 #ifndef SMALL
819 NORETURN static
mySIGSEGVorSIGBUScatcher(IntNative n)820 void mySIGSEGVorSIGBUScatcher ( IntNative n )
821 {
822    if (opMode == OM_Z)
823       fprintf (
824       stderr,
825       "\n%s: Caught a SIGSEGV or SIGBUS whilst compressing.\n"
826       "\n"
827       "   Possible causes are (most likely first):\n"
828       "   (1) This computer has unreliable memory or cache hardware\n"
829       "       (a surprisingly common problem; try a different machine.)\n"
830       "   (2) A bug in the compiler used to create this executable\n"
831       "       (unlikely, if you didn't compile bzip2 yourself.)\n"
832       "   (3) A real bug in bzip2 -- I hope this should never be the case.\n"
833       "   The user's manual, Section 4.3, has more info on (1) and (2).\n"
834       "   \n"
835       "   If you suspect this is a bug in bzip2, or are unsure about (1)\n"
836       "   or (2), feel free to report it to: bzip2-devel@sourceware.org.\n"
837       "   Section 4.3 of the user's manual describes the info a useful\n"
838       "   bug report should have.  If the manual is available on your\n"
839       "   system, please try and read it before mailing me.  If you don't\n"
840       "   have the manual or can't be bothered to read it, mail me anyway.\n"
841       "\n",
842       progName );
843       else
844       fprintf (
845       stderr,
846       "\n%s: Caught a SIGSEGV or SIGBUS whilst decompressing.\n"
847       "\n"
848       "   Possible causes are (most likely first):\n"
849       "   (1) The compressed data is corrupted, and bzip2's usual checks\n"
850       "       failed to detect this.  Try bzip2 -tvv my_file.bz2.\n"
851       "   (2) This computer has unreliable memory or cache hardware\n"
852       "       (a surprisingly common problem; try a different machine.)\n"
853       "   (3) A bug in the compiler used to create this executable\n"
854       "       (unlikely, if you didn't compile bzip2 yourself.)\n"
855       "   (4) A real bug in bzip2 -- I hope this should never be the case.\n"
856       "   The user's manual, Section 4.3, has more info on (2) and (3).\n"
857       "   \n"
858       "   If you suspect this is a bug in bzip2, or are unsure about (2)\n"
859       "   or (3), feel free to report it to: bzip2-devel@sourceware.org.\n"
860       "   Section 4.3 of the user's manual describes the info a useful\n"
861       "   bug report should have.  If the manual is available on your\n"
862       "   system, please try and read it before mailing me.  If you don't\n"
863       "   have the manual or can't be bothered to read it, mail me anyway.\n"
864       "\n",
865       progName );
866 
867    showFileNames();
868    if (opMode == OM_Z)
869       cleanUpAndFail( 3 ); else
870       { cadvise(); cleanUpAndFail( 2 ); }
871 }
872 #endif
873 
874 
875 /*---------------------------------------------*/
876 static
outOfMemory(void)877 void outOfMemory ( void )
878 {
879    fprintf ( stderr,
880              "\n%s: couldn't allocate enough memory\n",
881              progName );
882    showFileNames();
883    cleanUpAndFail(1);
884 }
885 
886 
887 /*---------------------------------------------*/
888 static
configError(void)889 void configError ( void )
890 {
891    fprintf ( stderr,
892              "bzip2: I'm not configured correctly for this platform!\n"
893              "\tI require Int32, Int16 and Char to have sizes\n"
894              "\tof 4, 2 and 1 bytes to run properly, and they don't.\n"
895              "\tProbably you can fix this by defining them correctly,\n"
896              "\tand recompiling.  Bye!\n" );
897    setExit(3);
898    exit(exitValue);
899 }
900 
901 
902 /*---------------------------------------------------*/
903 /*--- The main driver machinery                   ---*/
904 /*---------------------------------------------------*/
905 
906 /* All rather crufty.  The main problem is that input files
907    are stat()d multiple times before use.  This should be
908    cleaned up.
909 */
910 
911 /*---------------------------------------------*/
912 static
pad(Char * s)913 void pad ( Char *s )
914 {
915    Int32 i;
916    if ( (Int32)strlen(s) >= longestFileName ) return;
917    for (i = 1; i <= longestFileName - (Int32)strlen(s); i++)
918       fprintf ( stderr, " " );
919 }
920 
921 
922 /*---------------------------------------------*/
923 static
copyFileName(Char * to,const Char * from)924 void copyFileName ( Char* to, const Char* from )
925 {
926    if ( strlen(from) > FILE_NAME_LEN-10 )  {
927       fprintf (
928          stderr,
929          "bzip2: file name\n`%s'\n"
930          "is suspiciously (more than %d chars) long.\n"
931          "Try using a reasonable file name instead.  Sorry! :-)\n",
932          from, FILE_NAME_LEN-10
933       );
934       setExit(1);
935       exit(exitValue);
936    }
937 
938   strncpy(to,from,FILE_NAME_LEN-10);
939   to[FILE_NAME_LEN-10]='\0';
940 }
941 
942 
943 /*---------------------------------------------*/
944 static
fileExists(Char * name)945 Bool fileExists ( Char* name )
946 {
947    FILE *tmp   = fopen ( name, "rb" );
948    Bool exists = (tmp != NULL);
949    if (tmp != NULL) fclose ( tmp );
950    return exists;
951 }
952 
953 
954 /*---------------------------------------------*/
955 /* Open an output file safely with O_EXCL and good permissions.
956    This avoids a race condition in versions < 1.0.2, in which
957    the file was first opened and then had its interim permissions
958    set safely.  We instead use open() to create the file with
959    the interim permissions required. (--- --- rw-).
960 
961    For non-Unix platforms, if we are not worrying about
962    security issues, simple this simply behaves like fopen.
963 */
964 static
fopen_output_safely(Char * name,const char * mode)965 FILE* fopen_output_safely ( Char* name, const char* mode )
966 {
967 #  if BZ_UNIX
968    FILE*     fp;
969    IntNative fh;
970    fh = open(name, O_WRONLY|O_CREAT|O_EXCL, S_IWUSR|S_IRUSR);
971    if (fh == -1) return NULL;
972    fp = fdopen(fh, mode);
973    if (fp == NULL) close(fh);
974    return fp;
975 #  else
976    return fopen(name, mode);
977 #  endif
978 }
979 
980 
981 /*---------------------------------------------*/
982 /*--
983   if in doubt, return True
984 --*/
985 static
notAStandardFile(Char * name)986 Bool notAStandardFile ( Char* name )
987 {
988    IntNative      i;
989    struct MY_STAT statBuf;
990 
991    i = MY_LSTAT ( name, &statBuf );
992    if (i != 0) return True;
993    if (MY_S_ISREG(statBuf.st_mode)) return False;
994    return True;
995 }
996 
997 
998 /*---------------------------------------------*/
999 /*--
1000   rac 11/21/98 see if file has hard links to it
1001 --*/
1002 static
countHardLinks(Char * name)1003 Int32 countHardLinks ( Char* name )
1004 {
1005    IntNative      i;
1006    struct MY_STAT statBuf;
1007 
1008    i = MY_LSTAT ( name, &statBuf );
1009    if (i != 0) return 0;
1010    return (statBuf.st_nlink - 1);
1011 }
1012 
1013 
1014 /*---------------------------------------------*/
1015 /* Copy modification date, access date, permissions and owner from the
1016    source to destination file.  We have to copy this meta-info off
1017    into fileMetaInfo before starting to compress / decompress it,
1018    because doing it afterwards means we get the wrong access time.
1019 
1020    To complicate matters, in compress() and decompress() below, the
1021    sequence of tests preceding the call to saveInputFileMetaInfo()
1022    involves calling fileExists(), which in turn establishes its result
1023    by attempting to fopen() the file, and if successful, immediately
1024    fclose()ing it again.  So we have to assume that the fopen() call
1025    does not cause the access time field to be updated.
1026 
1027    Reading of the man page for stat() (man 2 stat) on RedHat 7.2 seems
1028    to imply that merely doing open() will not affect the access time.
1029    Therefore we merely need to hope that the C library only does
1030    open() as a result of fopen(), and not any kind of read()-ahead
1031    cleverness.
1032 
1033    It sounds pretty fragile to me.  Whether this carries across
1034    robustly to arbitrary Unix-like platforms (or even works robustly
1035    on this one, RedHat 7.2) is unknown to me.  Nevertheless ...
1036 */
1037 #if BZ_UNIX
1038 static
1039 struct MY_STAT fileMetaInfo;
1040 #endif
1041 
1042 static
saveInputFileMetaInfo(Char * srcName)1043 void saveInputFileMetaInfo ( Char *srcName )
1044 {
1045 #  if BZ_UNIX
1046    IntNative retVal;
1047    /* Note use of stat here, not lstat. */
1048    retVal = MY_STAT( srcName, &fileMetaInfo );
1049    ERROR_IF_NOT_ZERO ( retVal );
1050 #  endif
1051 }
1052 
1053 
1054 static
applySavedTimeInfoToOutputFile(Char * dstName)1055 void applySavedTimeInfoToOutputFile ( Char *dstName )
1056 {
1057 #  if BZ_UNIX
1058    IntNative      retVal;
1059    struct utimbuf uTimBuf;
1060 
1061    uTimBuf.actime = fileMetaInfo.st_atime;
1062    uTimBuf.modtime = fileMetaInfo.st_mtime;
1063 
1064    retVal = utime ( dstName, &uTimBuf );
1065    ERROR_IF_NOT_ZERO ( retVal );
1066 #  endif
1067 }
1068 
1069 static
applySavedFileAttrToOutputFile(IntNative fd)1070 void applySavedFileAttrToOutputFile ( IntNative fd )
1071 {
1072 #  if BZ_UNIX
1073    IntNative retVal;
1074 
1075    retVal = fchmod ( fd, fileMetaInfo.st_mode );
1076    ERROR_IF_NOT_ZERO ( retVal );
1077 
1078    (void) fchown ( fd, fileMetaInfo.st_uid, fileMetaInfo.st_gid );
1079    /* chown() will in many cases return with EPERM, which can
1080       be safely ignored.
1081    */
1082 #  endif
1083 }
1084 
1085 
1086 /*---------------------------------------------*/
1087 static
containsDubiousChars(Char * name)1088 Bool containsDubiousChars ( Char* name )
1089 {
1090 #  if BZ_UNIX
1091    /* On unix, files can contain any characters and the file expansion
1092     * is performed by the shell.
1093     */
1094    return False;
1095 #  else /* ! BZ_UNIX */
1096    /* On non-unix (Win* platforms), wildcard characters are not allowed in
1097     * filenames.
1098     */
1099    for (; *name != '\0'; name++)
1100       if (*name == '?' || *name == '*') return True;
1101    return False;
1102 #  endif /* BZ_UNIX */
1103 }
1104 
1105 
1106 /*---------------------------------------------*/
1107 #define BZ_N_SUFFIX_PAIRS 4
1108 
1109 const Char* zSuffix[BZ_N_SUFFIX_PAIRS]
1110    = { ".bz2", ".bz", ".tbz2", ".tbz" };
1111 const Char* unzSuffix[BZ_N_SUFFIX_PAIRS]
1112    = { "", "", ".tar", ".tar" };
1113 
1114 static
hasSuffix(Char * s,const Char * suffix)1115 Bool hasSuffix ( Char* s, const Char* suffix )
1116 {
1117    Int32 ns = strlen(s);
1118    Int32 nx = strlen(suffix);
1119    if (ns < nx) return False;
1120    if (strcmp(s + ns - nx, suffix) == 0) return True;
1121    return False;
1122 }
1123 
1124 static
mapSuffix(Char * name,const Char * oldSuffix,const Char * newSuffix)1125 Bool mapSuffix ( Char* name,
1126                  const Char* oldSuffix,
1127                  const Char* newSuffix )
1128 {
1129    if (!hasSuffix(name,oldSuffix)) return False;
1130    name[strlen(name)-strlen(oldSuffix)] = 0;
1131    strcat ( name, newSuffix );
1132    return True;
1133 }
1134 
1135 
1136 /*---------------------------------------------*/
1137 static
compress(Char * name)1138 void compress ( Char *name )
1139 {
1140    FILE  *inStr;
1141    FILE  *outStr;
1142    Int32 n, i;
1143    struct MY_STAT statBuf;
1144 
1145    deleteOutputOnInterrupt = False;
1146 
1147    if (name == NULL && srcMode != SM_I2O)
1148       panic ( "compress: bad modes\n" );
1149 
1150    switch (srcMode) {
1151       case SM_I2O:
1152          copyFileName ( inName, "(stdin)" );
1153          copyFileName ( outName, "(stdout)" );
1154          break;
1155       case SM_F2F:
1156          copyFileName ( inName, name );
1157          copyFileName ( outName, name );
1158          strcat ( outName, ".bz2" );
1159          break;
1160       case SM_F2O:
1161          copyFileName ( inName, name );
1162          copyFileName ( outName, "(stdout)" );
1163          break;
1164    }
1165 
1166    if ( srcMode != SM_I2O && containsDubiousChars ( inName ) ) {
1167       if (noisy)
1168       fprintf ( stderr, "%s: There are no files matching `%s'.\n",
1169                 progName, inName );
1170       setExit(1);
1171       return;
1172    }
1173    if ( srcMode != SM_I2O && !fileExists ( inName ) ) {
1174       fprintf ( stderr, "%s: Can't open input file %s: %s.\n",
1175                 progName, inName, strerror(errno) );
1176       setExit(1);
1177       return;
1178    }
1179    for (i = 0; i < BZ_N_SUFFIX_PAIRS; i++) {
1180       if (hasSuffix(inName, zSuffix[i])) {
1181          if (noisy)
1182          fprintf ( stderr,
1183                    "%s: Input file %s already has %s suffix.\n",
1184                    progName, inName, zSuffix[i] );
1185          setExit(1);
1186          return;
1187       }
1188    }
1189    if ( srcMode == SM_F2F || srcMode == SM_F2O ) {
1190       MY_STAT(inName, &statBuf);
1191       if ( MY_S_ISDIR(statBuf.st_mode) ) {
1192          fprintf( stderr,
1193                   "%s: Input file %s is a directory.\n",
1194                   progName,inName);
1195          setExit(1);
1196          return;
1197       }
1198    }
1199    if ( srcMode == SM_F2F && !forceOverwrite && notAStandardFile ( inName )) {
1200       if (noisy)
1201       fprintf ( stderr, "%s: Input file %s is not a normal file.\n",
1202                 progName, inName );
1203       setExit(1);
1204       return;
1205    }
1206    if ( srcMode == SM_F2F && fileExists ( outName ) ) {
1207       if (forceOverwrite) {
1208 	 remove(outName);
1209       } else {
1210 	 fprintf ( stderr, "%s: Output file %s already exists.\n",
1211 		   progName, outName );
1212 	 setExit(1);
1213 	 return;
1214       }
1215    }
1216    if ( srcMode == SM_F2F && !forceOverwrite &&
1217         (n=countHardLinks ( inName )) > 0) {
1218       fprintf ( stderr, "%s: Input file %s has %d other link%s.\n",
1219                 progName, inName, n, n > 1 ? "s" : "" );
1220       setExit(1);
1221       return;
1222    }
1223 
1224    if ( srcMode == SM_F2F ) {
1225       /* Save the file's meta-info before we open it.  Doing it later
1226          means we mess up the access times. */
1227       saveInputFileMetaInfo ( inName );
1228    }
1229 
1230    switch ( srcMode ) {
1231 
1232       case SM_I2O:
1233          inStr = stdin;
1234          outStr = stdout;
1235          if ( isatty ( fileno ( stdout ) ) ) {
1236             fprintf ( stderr,
1237                       "%s: I won't write compressed data to a terminal.\n",
1238                       progName );
1239             fprintf ( stderr, "%s: For help, type: `%s --help'.\n",
1240                               progName, progName );
1241             setExit(1);
1242             return;
1243          };
1244          break;
1245 
1246       case SM_F2O:
1247          inStr = fopen ( inName, "rb" );
1248          outStr = stdout;
1249          if ( isatty ( fileno ( stdout ) ) ) {
1250             fprintf ( stderr,
1251                       "%s: I won't write compressed data to a terminal.\n",
1252                       progName );
1253             fprintf ( stderr, "%s: For help, type: `%s --help'.\n",
1254                               progName, progName );
1255             if ( inStr != NULL ) fclose ( inStr );
1256             setExit(1);
1257             return;
1258          };
1259          if ( inStr == NULL ) {
1260             fprintf ( stderr, "%s: Can't open input file %s: %s.\n",
1261                       progName, inName, strerror(errno) );
1262             setExit(1);
1263             return;
1264          };
1265          break;
1266 
1267       case SM_F2F:
1268          inStr = fopen ( inName, "rb" );
1269          outStr = fopen_output_safely ( outName, "wb" );
1270          if ( outStr == NULL) {
1271             fprintf ( stderr, "%s: Can't create output file %s: %s.\n",
1272                       progName, outName, strerror(errno) );
1273             if ( inStr != NULL ) fclose ( inStr );
1274             setExit(1);
1275             return;
1276          }
1277          if ( inStr == NULL ) {
1278             fprintf ( stderr, "%s: Can't open input file %s: %s.\n",
1279                       progName, inName, strerror(errno) );
1280             if ( outStr != NULL ) fclose ( outStr );
1281             setExit(1);
1282             return;
1283          };
1284          break;
1285 
1286       default:
1287          panic ( "compress: bad srcMode" );
1288          break;
1289    }
1290 
1291    if (verbosity >= 1) {
1292       fprintf ( stderr,  "  %s: ", inName );
1293       pad ( inName );
1294       fflush ( stderr );
1295    }
1296 
1297    /*--- Now the input and output handles are sane.  Do the Biz. ---*/
1298    outputHandleJustInCase = outStr;
1299    deleteOutputOnInterrupt = True;
1300    compressStream ( inStr, outStr );
1301    outputHandleJustInCase = NULL;
1302 
1303    /*--- If there was an I/O error, we won't get here. ---*/
1304    if ( srcMode == SM_F2F ) {
1305       applySavedTimeInfoToOutputFile ( outName );
1306       deleteOutputOnInterrupt = False;
1307       if ( !keepInputFiles ) {
1308          IntNative retVal = remove ( inName );
1309          ERROR_IF_NOT_ZERO ( retVal );
1310       }
1311    }
1312 
1313    deleteOutputOnInterrupt = False;
1314 }
1315 
1316 
1317 /*---------------------------------------------*/
1318 static
uncompress(Char * name)1319 void uncompress ( Char *name )
1320 {
1321    FILE  *inStr;
1322    FILE  *outStr;
1323    Int32 n, i;
1324    Bool  magicNumberOK;
1325    Bool  cantGuess;
1326    struct MY_STAT statBuf;
1327 
1328    deleteOutputOnInterrupt = False;
1329 
1330    if (name == NULL && srcMode != SM_I2O)
1331       panic ( "uncompress: bad modes\n" );
1332 
1333    cantGuess = False;
1334    switch (srcMode) {
1335       case SM_I2O:
1336          copyFileName ( inName, "(stdin)" );
1337          copyFileName ( outName, "(stdout)" );
1338          break;
1339       case SM_F2F:
1340          copyFileName ( inName, name );
1341          copyFileName ( outName, name );
1342          for (i = 0; i < BZ_N_SUFFIX_PAIRS; i++)
1343             if (mapSuffix(outName,zSuffix[i],unzSuffix[i]))
1344                goto zzz;
1345          cantGuess = True;
1346          strcat ( outName, ".out" );
1347          break;
1348       case SM_F2O:
1349          copyFileName ( inName, name );
1350          copyFileName ( outName, "(stdout)" );
1351          break;
1352    }
1353 
1354    zzz:
1355    if ( srcMode != SM_I2O && containsDubiousChars ( inName ) ) {
1356       if (noisy)
1357       fprintf ( stderr, "%s: There are no files matching `%s'.\n",
1358                 progName, inName );
1359       setExit(1);
1360       return;
1361    }
1362    if ( srcMode != SM_I2O && !fileExists ( inName ) ) {
1363       fprintf ( stderr, "%s: Can't open input file %s: %s.\n",
1364                 progName, inName, strerror(errno) );
1365       setExit(1);
1366       return;
1367    }
1368    if ( srcMode == SM_F2F || srcMode == SM_F2O ) {
1369       MY_STAT(inName, &statBuf);
1370       if ( MY_S_ISDIR(statBuf.st_mode) ) {
1371          fprintf( stderr,
1372                   "%s: Input file %s is a directory.\n",
1373                   progName,inName);
1374          setExit(1);
1375          return;
1376       }
1377    }
1378    if ( srcMode == SM_F2F && !forceOverwrite && notAStandardFile ( inName )) {
1379       if (noisy)
1380       fprintf ( stderr, "%s: Input file %s is not a normal file.\n",
1381                 progName, inName );
1382       setExit(1);
1383       return;
1384    }
1385    if ( /* srcMode == SM_F2F implied && */ cantGuess ) {
1386       if (noisy)
1387       fprintf ( stderr,
1388                 "%s: Can't guess original name for %s -- using %s\n",
1389                 progName, inName, outName );
1390       /* just a warning, no return */
1391    }
1392    if ( srcMode == SM_F2F && fileExists ( outName ) ) {
1393       if (forceOverwrite) {
1394 	remove(outName);
1395       } else {
1396         fprintf ( stderr, "%s: Output file %s already exists.\n",
1397                   progName, outName );
1398         setExit(1);
1399         return;
1400       }
1401    }
1402    if ( srcMode == SM_F2F && !forceOverwrite &&
1403         (n=countHardLinks ( inName ) ) > 0) {
1404       fprintf ( stderr, "%s: Input file %s has %d other link%s.\n",
1405                 progName, inName, n, n > 1 ? "s" : "" );
1406       setExit(1);
1407       return;
1408    }
1409 
1410    if ( srcMode == SM_F2F ) {
1411       /* Save the file's meta-info before we open it.  Doing it later
1412          means we mess up the access times. */
1413       saveInputFileMetaInfo ( inName );
1414    }
1415 
1416    switch ( srcMode ) {
1417 
1418       case SM_I2O:
1419          inStr = stdin;
1420          outStr = stdout;
1421          if ( isatty ( fileno ( stdin ) ) ) {
1422             fprintf ( stderr,
1423                       "%s: I won't read compressed data from a terminal.\n",
1424                       progName );
1425             fprintf ( stderr, "%s: For help, type: `%s --help'.\n",
1426                               progName, progName );
1427             setExit(1);
1428             return;
1429          };
1430          break;
1431 
1432       case SM_F2O:
1433          inStr = fopen ( inName, "rb" );
1434          outStr = stdout;
1435          if ( inStr == NULL ) {
1436             fprintf ( stderr, "%s: Can't open input file %s:%s.\n",
1437                       progName, inName, strerror(errno) );
1438             if ( inStr != NULL ) fclose ( inStr );
1439             setExit(1);
1440             return;
1441          };
1442          break;
1443 
1444       case SM_F2F:
1445          inStr = fopen ( inName, "rb" );
1446          outStr = fopen_output_safely ( outName, "wb" );
1447          if ( outStr == NULL) {
1448             fprintf ( stderr, "%s: Can't create output file %s: %s.\n",
1449                       progName, outName, strerror(errno) );
1450             if ( inStr != NULL ) fclose ( inStr );
1451             setExit(1);
1452             return;
1453          }
1454          if ( inStr == NULL ) {
1455             fprintf ( stderr, "%s: Can't open input file %s: %s.\n",
1456                       progName, inName, strerror(errno) );
1457             if ( outStr != NULL ) fclose ( outStr );
1458             setExit(1);
1459             return;
1460          };
1461          break;
1462 
1463       default:
1464          panic ( "uncompress: bad srcMode" );
1465          break;
1466    }
1467 
1468    if (verbosity >= 1) {
1469       fprintf ( stderr, "  %s: ", inName );
1470       pad ( inName );
1471       fflush ( stderr );
1472    }
1473 
1474    /*--- Now the input and output handles are sane.  Do the Biz. ---*/
1475    outputHandleJustInCase = outStr;
1476    deleteOutputOnInterrupt = True;
1477    magicNumberOK = uncompressStream ( inStr, outStr );
1478    outputHandleJustInCase = NULL;
1479 
1480    /*--- If there was an I/O error, we won't get here. ---*/
1481    if ( magicNumberOK ) {
1482       if ( srcMode == SM_F2F ) {
1483          applySavedTimeInfoToOutputFile ( outName );
1484          deleteOutputOnInterrupt = False;
1485          if ( !keepInputFiles ) {
1486             IntNative retVal = remove ( inName );
1487             ERROR_IF_NOT_ZERO ( retVal );
1488          }
1489       }
1490    } else {
1491       unzFailsExist = True;
1492       deleteOutputOnInterrupt = False;
1493       if ( srcMode == SM_F2F ) {
1494          IntNative retVal = remove ( outName );
1495          ERROR_IF_NOT_ZERO ( retVal );
1496       }
1497    }
1498    deleteOutputOnInterrupt = False;
1499 
1500    if ( magicNumberOK ) {
1501       if (verbosity >= 1)
1502          fprintf ( stderr, "done\n" );
1503    } else {
1504       setExit(2);
1505       if (verbosity >= 1)
1506          fprintf ( stderr, "not a bzip2 file.\n" ); else
1507          fprintf ( stderr,
1508                    "%s: %s is not a bzip2 file.\n",
1509                    progName, inName );
1510    }
1511 
1512 }
1513 
1514 
1515 /*---------------------------------------------*/
1516 static
testf(Char * name)1517 void testf ( Char *name )
1518 {
1519    FILE *inStr;
1520    Bool allOK;
1521    struct MY_STAT statBuf;
1522 
1523    deleteOutputOnInterrupt = False;
1524 
1525    if (name == NULL && srcMode != SM_I2O)
1526       panic ( "testf: bad modes\n" );
1527 
1528    copyFileName ( outName, "(none)" );
1529    switch (srcMode) {
1530       case SM_I2O: copyFileName ( inName, "(stdin)" ); break;
1531       case SM_F2F: copyFileName ( inName, name ); break;
1532       case SM_F2O: copyFileName ( inName, name ); break;
1533    }
1534 
1535    if ( srcMode != SM_I2O && containsDubiousChars ( inName ) ) {
1536       if (noisy)
1537       fprintf ( stderr, "%s: There are no files matching `%s'.\n",
1538                 progName, inName );
1539       setExit(1);
1540       return;
1541    }
1542    if ( srcMode != SM_I2O && !fileExists ( inName ) ) {
1543       fprintf ( stderr, "%s: Can't open input %s: %s.\n",
1544                 progName, inName, strerror(errno) );
1545       setExit(1);
1546       return;
1547    }
1548    if ( srcMode != SM_I2O ) {
1549       MY_STAT(inName, &statBuf);
1550       if ( MY_S_ISDIR(statBuf.st_mode) ) {
1551          fprintf( stderr,
1552                   "%s: Input file %s is a directory.\n",
1553                   progName,inName);
1554          setExit(1);
1555          return;
1556       }
1557    }
1558 
1559    switch ( srcMode ) {
1560 
1561       case SM_I2O:
1562          if ( isatty ( fileno ( stdin ) ) ) {
1563             fprintf ( stderr,
1564                       "%s: I won't read compressed data from a terminal.\n",
1565                       progName );
1566             fprintf ( stderr, "%s: For help, type: `%s --help'.\n",
1567                               progName, progName );
1568             setExit(1);
1569             return;
1570          };
1571          inStr = stdin;
1572          break;
1573 
1574       case SM_F2O: case SM_F2F:
1575          inStr = fopen ( inName, "rb" );
1576          if ( inStr == NULL ) {
1577             fprintf ( stderr, "%s: Can't open input file %s:%s.\n",
1578                       progName, inName, strerror(errno) );
1579             setExit(1);
1580             return;
1581          };
1582          break;
1583 
1584       default:
1585          panic ( "testf: bad srcMode" );
1586          break;
1587    }
1588 
1589    if (verbosity >= 1) {
1590       fprintf ( stderr, "  %s: ", inName );
1591       pad ( inName );
1592       fflush ( stderr );
1593    }
1594 
1595    /*--- Now the input handle is sane.  Do the Biz. ---*/
1596    outputHandleJustInCase = NULL;
1597    allOK = testStream ( inStr );
1598 
1599    if (allOK && verbosity >= 1) fprintf ( stderr, "ok\n" );
1600    if (!allOK) testFailsExist = True;
1601 }
1602 
1603 
1604 /*---------------------------------------------*/
1605 static
license(void)1606 void license ( void )
1607 {
1608    fprintf ( stderr,
1609 
1610     "bzip2, a block-sorting file compressor.  "
1611     "Version %s.\n"
1612     "   \n"
1613     "   Copyright (C) 1996-2019 by Julian Seward.\n"
1614     "   \n"
1615     "   This program is free software; you can redistribute it and/or modify\n"
1616     "   it under the terms set out in the LICENSE file, which is included\n"
1617     "   in the bzip2 source distribution.\n"
1618     "   \n"
1619     "   This program is distributed in the hope that it will be useful,\n"
1620     "   but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
1621     "   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n"
1622     "   LICENSE file for more details.\n"
1623     "   \n",
1624     BZ2_bzlibVersion()
1625    );
1626 }
1627 
1628 
1629 /*---------------------------------------------*/
1630 static
usage(Char * fullProgName)1631 void usage ( Char *fullProgName )
1632 {
1633    fprintf (
1634       stderr,
1635       "bzip2, a block-sorting file compressor.  "
1636       "Version %s.\n"
1637       "\n   usage: %s [flags and input files in any order]\n"
1638       "\n"
1639       "   -h --help           print this message\n"
1640       "   -d --decompress     force decompression\n"
1641       "   -z --compress       force compression\n"
1642       "   -k --keep           keep (don't delete) input files\n"
1643       "   -f --force          overwrite existing output files\n"
1644       "   -t --test           test compressed file integrity\n"
1645       "   -c --stdout         output to standard out\n"
1646       "   -q --quiet          suppress noncritical error messages\n"
1647       "   -v --verbose        be verbose (a 2nd -v gives more)\n"
1648       "   -L --license        display software version & license\n"
1649       "   -V --version        display software version & license\n"
1650       "   -s --small          use less memory (at most 2500k)\n"
1651       "   -1 .. -9            set block size to 100k .. 900k\n"
1652       "   --fast              alias for -1\n"
1653       "   --best              alias for -9\n"
1654       "\n"
1655       "   If invoked as `bzip2', default action is to compress.\n"
1656       "              as `bunzip2',  default action is to decompress.\n"
1657       "              as `bzcat', default action is to decompress to stdout.\n"
1658       "\n"
1659       "   If no file names are given, bzip2 compresses or decompresses\n"
1660       "   from standard input to standard output.  You can combine\n"
1661       "   short flags, so `-v -4' means the same as -v4 or -4v, &c.\n"
1662 #     if BZ_UNIX
1663       "\n"
1664 #     endif
1665       ,
1666 
1667       BZ2_bzlibVersion(),
1668       fullProgName
1669    );
1670 }
1671 
1672 
1673 /*---------------------------------------------*/
1674 static
redundant(Char * flag)1675 void redundant ( Char* flag )
1676 {
1677    fprintf (
1678       stderr,
1679       "%s: %s is redundant in versions 0.9.5 and above\n",
1680       progName, flag );
1681 }
1682 
1683 
1684 /*---------------------------------------------*/
1685 /*--
1686   All the garbage from here to main() is purely to
1687   implement a linked list of command-line arguments,
1688   into which main() copies argv[1 .. argc-1].
1689 
1690   The purpose of this exercise is to facilitate
1691   the expansion of wildcard characters * and ? in
1692   filenames for OSs which don't know how to do it
1693   themselves, like MSDOS, Windows 95 and NT.
1694 
1695   The actual Dirty Work is done by the platform-
1696   specific macro APPEND_FILESPEC.
1697 --*/
1698 
1699 typedef
1700    struct zzzz {
1701       Char        *name;
1702       struct zzzz *link;
1703    }
1704    Cell;
1705 
1706 
1707 /*---------------------------------------------*/
1708 static
myMalloc(Int32 n)1709 void *myMalloc ( Int32 n )
1710 {
1711    void* p;
1712 
1713    p = malloc ( (size_t)n );
1714    if (p == NULL) outOfMemory ();
1715    return p;
1716 }
1717 
1718 
1719 /*---------------------------------------------*/
1720 static
mkCell(void)1721 Cell *mkCell ( void )
1722 {
1723    Cell *c;
1724 
1725    c = (Cell*) myMalloc ( sizeof ( Cell ) );
1726    c->name = NULL;
1727    c->link = NULL;
1728    return c;
1729 }
1730 
1731 
1732 /*---------------------------------------------*/
1733 static
snocString(Cell * root,Char * name)1734 Cell *snocString ( Cell *root, Char *name )
1735 {
1736    if (root == NULL) {
1737       Cell *tmp = mkCell();
1738       tmp->name = (Char*) myMalloc ( 5 + strlen(name) );
1739       strcpy ( tmp->name, name );
1740       return tmp;
1741    } else {
1742       Cell *tmp = root;
1743       while (tmp->link != NULL) tmp = tmp->link;
1744       tmp->link = snocString ( tmp->link, name );
1745       return root;
1746    }
1747 }
1748 
1749 
1750 /*---------------------------------------------*/
1751 static
addFlagsFromEnvVar(Cell ** argList,const Char * varName)1752 void addFlagsFromEnvVar ( Cell** argList, const Char* varName )
1753 {
1754    Int32 i, j, k;
1755    Char *envbase, *p;
1756 
1757    envbase = getenv(varName);
1758    if (envbase != NULL) {
1759       p = envbase;
1760       i = 0;
1761       while (True) {
1762          if (p[i] == 0) break;
1763          p += i;
1764          i = 0;
1765          while (isspace((UChar)(p[0]))) p++;
1766          while (p[i] != 0 && !isspace((UChar)(p[i]))) i++;
1767          if (i > 0) {
1768             k = i; if (k > FILE_NAME_LEN-10) k = FILE_NAME_LEN-10;
1769             for (j = 0; j < k; j++) tmpName[j] = p[j];
1770             tmpName[k] = 0;
1771             APPEND_FLAG(*argList, tmpName);
1772          }
1773       }
1774    }
1775 }
1776 
1777 
1778 /*---------------------------------------------*/
1779 #define ISFLAG(s) (strcmp(aa->name, (s))==0)
1780 
main(IntNative argc,Char * argv[])1781 IntNative main ( IntNative argc, Char *argv[] )
1782 {
1783    Int32  i, j;
1784    Char   *tmp;
1785    Cell   *argList;
1786    Cell   *aa;
1787    Bool   decode;
1788 
1789    /*-- Be really really really paranoid :-) --*/
1790    if (sizeof(Int32) != 4 || sizeof(UInt32) != 4  ||
1791        sizeof(Int16) != 2 || sizeof(UInt16) != 2  ||
1792        sizeof(Char)  != 1 || sizeof(UChar)  != 1)
1793       configError();
1794 
1795    /*-- Initialise --*/
1796    outputHandleJustInCase  = NULL;
1797    smallMode               = False;
1798    keepInputFiles          = False;
1799    forceOverwrite          = False;
1800    noisy                   = True;
1801    verbosity               = 0;
1802    blockSize100k           = 9;
1803    testFailsExist          = False;
1804    unzFailsExist           = False;
1805    numFileNames            = 0;
1806    numFilesProcessed       = 0;
1807    workFactor              = 30;
1808    deleteOutputOnInterrupt = False;
1809    exitValue               = 0;
1810    i = j = 0; /* avoid bogus warning from egcs-1.1.X */
1811 
1812 #ifndef SMALL
1813    /*-- Set up signal handlers for mem access errors --*/
1814    signal (SIGSEGV, mySIGSEGVorSIGBUScatcher);
1815 #  if BZ_UNIX
1816 #  ifndef __DJGPP__
1817    signal (SIGBUS,  mySIGSEGVorSIGBUScatcher);
1818 #  endif
1819 #  endif
1820 #endif
1821 
1822    copyFileName ( inName,  "(none)" );
1823    copyFileName ( outName, "(none)" );
1824 
1825    copyFileName ( progNameReally, argv[0] );
1826    progName = &progNameReally[0];
1827    for (tmp = &progNameReally[0]; *tmp != '\0'; tmp++)
1828       if (*tmp == PATH_SEP) progName = tmp + 1;
1829 
1830 
1831    /*-- Copy flags from env var BZIP2, and
1832         expand filename wildcards in arg list.
1833    --*/
1834    argList = NULL;
1835    addFlagsFromEnvVar ( &argList,  "BZIP2" );
1836    addFlagsFromEnvVar ( &argList,  "BZIP" );
1837    for (i = 1; i <= argc-1; i++)
1838       APPEND_FILESPEC(argList, argv[i]);
1839 
1840 
1841    /*-- Find the length of the longest filename --*/
1842    longestFileName = 7;
1843    numFileNames    = 0;
1844    decode          = True;
1845    for (aa = argList; aa != NULL; aa = aa->link) {
1846       if (ISFLAG("--")) { decode = False; continue; }
1847       if (aa->name[0] == '-' && decode) continue;
1848       numFileNames++;
1849       if (longestFileName < (Int32)strlen(aa->name) )
1850          longestFileName = (Int32)strlen(aa->name);
1851    }
1852 
1853 
1854    /*-- Determine source modes; flag handling may change this too. --*/
1855    if (numFileNames == 0)
1856       srcMode = SM_I2O; else srcMode = SM_F2F;
1857 
1858 
1859    /*-- Determine what to do (compress/uncompress/test/cat). --*/
1860    /*-- Note that subsequent flag handling may change this. --*/
1861    opMode = OM_Z;
1862 
1863    if ( (strstr ( progName, "unzip" ) != 0) ||
1864         (strstr ( progName, "UNZIP" ) != 0) )
1865       opMode = OM_UNZ;
1866 
1867    if ( (strstr ( progName, "z2cat" ) != 0) ||
1868         (strstr ( progName, "Z2CAT" ) != 0) ||
1869         (strstr ( progName, "zcat" ) != 0)  ||
1870         (strstr ( progName, "ZCAT" ) != 0) )  {
1871       opMode = OM_UNZ;
1872       srcMode = (numFileNames == 0) ? SM_I2O : SM_F2O;
1873    }
1874 
1875 
1876    /*-- Look at the flags. --*/
1877    for (aa = argList; aa != NULL; aa = aa->link) {
1878       if (ISFLAG("--")) break;
1879       if (aa->name[0] == '-' && aa->name[1] != '-') {
1880          for (j = 1; aa->name[j] != '\0'; j++) {
1881             switch (aa->name[j]) {
1882                case 'c': srcMode          = SM_F2O; break;
1883                case 'd': opMode           = OM_UNZ; break;
1884                case 'z': opMode           = OM_Z; break;
1885                case 'f': forceOverwrite   = True; break;
1886                case 't': opMode           = OM_TEST; break;
1887                case 'k': keepInputFiles   = True; break;
1888                case 's': smallMode        = True; break;
1889                case 'q': noisy            = False; break;
1890                case '1': blockSize100k    = 1; break;
1891                case '2': blockSize100k    = 2; break;
1892                case '3': blockSize100k    = 3; break;
1893                case '4': blockSize100k    = 4; break;
1894                case '5': blockSize100k    = 5; break;
1895                case '6': blockSize100k    = 6; break;
1896                case '7': blockSize100k    = 7; break;
1897                case '8': blockSize100k    = 8; break;
1898                case '9': blockSize100k    = 9; break;
1899                case 'V':
1900                case 'L': license();            break;
1901                case 'v': verbosity++; break;
1902                case 'h': usage ( progName );
1903                          exit ( 0 );
1904                          break;
1905                default:  fprintf ( stderr, "%s: Bad flag `%s'\n",
1906                                    progName, aa->name );
1907                          usage ( progName );
1908                          exit ( 1 );
1909                          break;
1910             }
1911          }
1912       }
1913    }
1914 
1915    /*-- And again ... --*/
1916    for (aa = argList; aa != NULL; aa = aa->link) {
1917       if (ISFLAG("--")) break;
1918       if (ISFLAG("--stdout"))            srcMode          = SM_F2O;  else
1919       if (ISFLAG("--decompress"))        opMode           = OM_UNZ;  else
1920       if (ISFLAG("--compress"))          opMode           = OM_Z;    else
1921       if (ISFLAG("--force"))             forceOverwrite   = True;    else
1922       if (ISFLAG("--test"))              opMode           = OM_TEST; else
1923       if (ISFLAG("--keep"))              keepInputFiles   = True;    else
1924       if (ISFLAG("--small"))             smallMode        = True;    else
1925       if (ISFLAG("--quiet"))             noisy            = False;   else
1926       if (ISFLAG("--version"))           license();                  else
1927       if (ISFLAG("--license"))           license();                  else
1928       if (ISFLAG("--exponential"))       workFactor = 1;             else
1929       if (ISFLAG("--repetitive-best"))   redundant(aa->name);        else
1930       if (ISFLAG("--repetitive-fast"))   redundant(aa->name);        else
1931       if (ISFLAG("--fast"))              blockSize100k = 1;          else
1932       if (ISFLAG("--best"))              blockSize100k = 9;          else
1933       if (ISFLAG("--verbose"))           verbosity++;                else
1934       if (ISFLAG("--help"))              { usage ( progName ); exit ( 0 ); }
1935          else
1936          if (strncmp ( aa->name, "--", 2) == 0) {
1937             fprintf ( stderr, "%s: Bad flag `%s'\n", progName, aa->name );
1938             usage ( progName );
1939             exit ( 1 );
1940          }
1941    }
1942 
1943    if (verbosity > 4) verbosity = 4;
1944    if (opMode == OM_Z && smallMode && blockSize100k > 2)
1945       blockSize100k = 2;
1946 
1947    if (opMode == OM_TEST && srcMode == SM_F2O) {
1948       fprintf ( stderr, "%s: -c and -t cannot be used together.\n",
1949                 progName );
1950       exit ( 1 );
1951    }
1952 
1953    if (srcMode == SM_F2O && numFileNames == 0)
1954       srcMode = SM_I2O;
1955 
1956    if (opMode != OM_Z) blockSize100k = 0;
1957 
1958    if (srcMode == SM_F2F) {
1959       signal (SIGINT,  mySignalCatcher);
1960       signal (SIGTERM, mySignalCatcher);
1961 #     if BZ_UNIX
1962       signal (SIGHUP,  mySignalCatcher);
1963 #     endif
1964    }
1965 
1966    if (opMode == OM_Z) {
1967      if (srcMode == SM_I2O) {
1968         compress ( NULL );
1969      } else {
1970         decode = True;
1971         for (aa = argList; aa != NULL; aa = aa->link) {
1972            if (ISFLAG("--")) { decode = False; continue; }
1973            if (aa->name[0] == '-' && decode) continue;
1974            numFilesProcessed++;
1975            compress ( aa->name );
1976         }
1977      }
1978    }
1979    else
1980 
1981    if (opMode == OM_UNZ) {
1982       unzFailsExist = False;
1983       if (srcMode == SM_I2O) {
1984          uncompress ( NULL );
1985       } else {
1986          decode = True;
1987          for (aa = argList; aa != NULL; aa = aa->link) {
1988             if (ISFLAG("--")) { decode = False; continue; }
1989             if (aa->name[0] == '-' && decode) continue;
1990             numFilesProcessed++;
1991             uncompress ( aa->name );
1992          }
1993       }
1994       if (unzFailsExist) {
1995          setExit(2);
1996          exit(exitValue);
1997       }
1998    }
1999 
2000    else {
2001       testFailsExist = False;
2002       if (srcMode == SM_I2O) {
2003          testf ( NULL );
2004       } else {
2005          decode = True;
2006          for (aa = argList; aa != NULL; aa = aa->link) {
2007 	    if (ISFLAG("--")) { decode = False; continue; }
2008             if (aa->name[0] == '-' && decode) continue;
2009             numFilesProcessed++;
2010             testf ( aa->name );
2011 	 }
2012       }
2013       if (testFailsExist) {
2014 	 if (noisy) {
2015             fprintf ( stderr,
2016                "\n"
2017                "You can use the `bzip2recover' program to attempt to recover\n"
2018                "data from undamaged sections of corrupted files.\n\n"
2019             );
2020 	 }
2021          setExit(2);
2022          exit(exitValue);
2023       }
2024    }
2025 
2026    /* Free the argument list memory to mollify leak detectors
2027       (eg) Purify, Checker.  Serves no other useful purpose.
2028    */
2029    aa = argList;
2030    while (aa != NULL) {
2031       Cell* aa2 = aa->link;
2032       if (aa->name != NULL) free(aa->name);
2033       free(aa);
2034       aa = aa2;
2035    }
2036 
2037    return exitValue;
2038 }
2039 
2040 
2041 /*-----------------------------------------------------------*/
2042 /*--- end                                         bzip2.c ---*/
2043 /*-----------------------------------------------------------*/
2044