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
163typedef char            Char;
164typedef unsigned char   Bool;
165typedef unsigned char   UChar;
166typedef int             Int32;
167typedef unsigned int    UInt32;
168typedef short           Int16;
169typedef 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--*/
178typedef int IntNative;
179
180
181/*---------------------------------------------------*/
182/*--- Misc (file handling) data decls             ---*/
183/*---------------------------------------------------*/
184
185Int32   verbosity;
186Bool    keepInputFiles, smallMode, deleteOutputOnInterrupt;
187Bool    forceOverwrite, testFailsExist, unzFailsExist, noisy;
188Int32   numFileNames, numFilesProcessed, blockSize100k;
189Int32   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
201Int32   opMode;
202Int32   srcMode;
203
204#define FILE_NAME_LEN 1034
205
206Int32   longestFileName;
207Char    inName [FILE_NAME_LEN];
208Char    outName[FILE_NAME_LEN];
209Char    tmpName[FILE_NAME_LEN];
210Char    *progName;
211Char    progNameReally[FILE_NAME_LEN];
212FILE    *outputHandleJustInCase;
213Int32   workFactor;
214
215static void    panic                 ( const Char* ) NORETURN;
216static void    ioError               ( void )        NORETURN;
217static void    outOfMemory           ( void )        NORETURN;
218static void    configError           ( void )        NORETURN;
219static void    crcError              ( void )        NORETURN;
220static void    cleanUpAndFail        ( Int32 )       NORETURN;
221static void    compressedStreamEOF   ( void )        NORETURN;
222
223static void    copyFileName ( Char*, const Char* );
224static void*   myMalloc     ( Int32 );
225static void    applySavedFileAttrToOutputFile ( IntNative fd );
226
227
228static 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
235typedef
236   struct { UChar b[8]; }
237   UInt64;
238
239
240static
241void 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
254static
255double 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
268static
269Bool 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.  */
279static
280Int32 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*/
297static
298void 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/*---------------------------------------------*/
320static
321Bool 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/*---------------------------------------------*/
331static
332void 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/*---------------------------------------------*/
435static
436Bool 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/*---------------------------------------------*/
556static
557Bool 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/*---------------------------------------------*/
653static
654void setExit ( Int32 v )
655{
656   if (v > exitValue) exitValue = v;
657}
658
659
660/*---------------------------------------------*/
661static
662void 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/*---------------------------------------------*/
676static
677void 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/*---------------------------------------------*/
689static
690void 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/*---------------------------------------------*/
748static
749void 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/*---------------------------------------------*/
763static
764void 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/*---------------------------------------------*/
776static
777void 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/*---------------------------------------------*/
793static
794void 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/*---------------------------------------------*/
807NORETURN static
808void 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
819NORETURN static
820void 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/*---------------------------------------------*/
876static
877void 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/*---------------------------------------------*/
888static
889void 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/*---------------------------------------------*/
912static
913void 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/*---------------------------------------------*/
923static
924void 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/*---------------------------------------------*/
944static
945Bool 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*/
964static
965FILE* 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--*/
985static
986Bool 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--*/
1002static
1003Int32 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
1038static
1039struct MY_STAT fileMetaInfo;
1040#endif
1041
1042static
1043void 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
1054static
1055void 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
1069static
1070void 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/*---------------------------------------------*/
1087static
1088Bool 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
1109const Char* zSuffix[BZ_N_SUFFIX_PAIRS]
1110   = { ".bz2", ".bz", ".tbz2", ".tbz" };
1111const Char* unzSuffix[BZ_N_SUFFIX_PAIRS]
1112   = { "", "", ".tar", ".tar" };
1113
1114static
1115Bool 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
1124static
1125Bool 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/*---------------------------------------------*/
1137static
1138void 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/*---------------------------------------------*/
1318static
1319void 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/*---------------------------------------------*/
1516static
1517void 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/*---------------------------------------------*/
1605static
1606void 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/*---------------------------------------------*/
1630static
1631void 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/*---------------------------------------------*/
1674static
1675void 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
1699typedef
1700   struct zzzz {
1701      Char        *name;
1702      struct zzzz *link;
1703   }
1704   Cell;
1705
1706
1707/*---------------------------------------------*/
1708static
1709void *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/*---------------------------------------------*/
1720static
1721Cell *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/*---------------------------------------------*/
1733static
1734Cell *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/*---------------------------------------------*/
1751static
1752void 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
1781IntNative 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