1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
|
/* Lzd - Educational decompressor for lzip files
Copyright (C) 2013 Antonio Diaz Diaz.
This program is free software: you have unlimited permission
to copy, distribute and modify it.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*/
/*
Return values: 0 for a normal exit, 1 for environmental problems
(file not found, invalid flags, I/O errors, etc), 2 to indicate a
corrupt or invalid input file.
*/
#include <algorithm>
#include <cerrno>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <stdint.h>
#include <unistd.h>
#if defined(__MSVCRT__) || defined(__OS2__)
#include <io.h>
#endif
#include "decoder.cc"
enum { min_dictionary_size = 1 << 12,
max_dictionary_size = 1 << 29 };
typedef uint8_t File_header[6]; // 0-3 magic, 4 version, 5 coded_dict_size
typedef uint8_t File_trailer[20];
// 0-3 CRC32 of the uncompressed data
// 4-11 size of the uncompressed data
// 12-19 member size including header and trailer
int main( const int argc, const char * const argv[] )
{
if( argc > 1 )
{
std::printf( "Lzd %s - Educational decompressor for lzip files.\n",
PROGVERSION );
std::printf( "Study the source to learn how a simple lzip decompressor works.\n"
"It is not safe to use it for any real work.\n"
"\nUsage: %s < file.lz > file\n", argv[0] );
std::printf( "Lzd decompresses from standard input to standard output.\n"
"\nCopyright (C) 2013 Antonio Diaz Diaz.\n"
"This is free software: you are free to change and redistribute it.\n"
"There is NO WARRANTY, to the extent permitted by law.\n"
"Report bugs to lzip-bug@nongnu.org\n"
"Lzip home page: http://www.nongnu.org/lzip/lzip.html\n" );
return 0;
}
#if defined(__MSVCRT__) || defined(__OS2__)
setmode( STDIN_FILENO, O_BINARY );
setmode( STDOUT_FILENO, O_BINARY );
#endif
if( isatty( STDIN_FILENO ) )
{
std::fprintf( stderr, "I won't read compressed data from a terminal.\n"
"Try '%s --help' for more information.\n", argv[0] );
return 1;
}
for( bool first_member = true; ; first_member = false )
{
File_header header;
for( int i = 0; i < 6; ++i )
header[i] = std::getc( stdin );
if( std::feof( stdin ) || std::memcmp( header, "LZIP", 4 ) != 0 )
{
if( first_member )
{ std::fprintf( stderr, "Bad magic number (file not in lzip format)\n" );
return 2; }
break;
}
if( header[4] != 1 )
{
std::fprintf( stderr, "Version %d member format not supported.\n",
header[4] );
return 2;
}
unsigned dict_size = 1 << ( header[5] & 0x1F );
dict_size -= ( dict_size / 16 ) * ( ( header[5] >> 5 ) & 7 );
if( dict_size < min_dictionary_size || dict_size > max_dictionary_size )
{ std::fprintf( stderr, "Invalid dictionary size in member header\n" );
return 2; }
LZ_decoder decoder( dict_size );
if( !decoder.decode_member() )
{ std::fprintf( stderr, "Data error\n" ); return 2; }
File_trailer trailer;
for( int i = 0; i < 20; ++i ) trailer[i] = std::getc( stdin );
unsigned crc = 0;
for( int i = 3; i >= 0; --i ) { crc <<= 8; crc += trailer[i]; }
unsigned long long data_size = 0;
for( int i = 11; i >= 4; --i ) { data_size <<= 8; data_size += trailer[i]; }
if( crc != decoder.crc() || data_size != decoder.data_position() )
{ std::fprintf( stderr, "CRC error\n" ); return 2; }
}
if( std::fclose( stdout ) != 0 )
{ std::fprintf( stderr, "Can't close stdout: %s\n", std::strerror( errno ) );
return 1; }
return 0;
}
|