diff --git a/.gitignore b/.gitignore index b43a45da8f5da56f47189b6fd95604144557744e..ff631349d57d051192090b7b979270d847e27da0 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ /myunzip0 /inflate /huffman +/myunzip diff --git a/Makefile b/Makefile index 4df46b940405ec0b1cc6ea5e170df8a1ac5e5611..b92daedbffffd80234196b8d8523732ebaa618c8 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -all : myzip0 myunzip0 huffman inflate test_inflate +all : myzip0 myunzip0 huffman inflate test_inflate myunzip myzip0 : myzip0.c cc myzip0.c -o myzip0 @@ -12,9 +12,12 @@ huffman : huffman.c inflate : include/inflate.h src/inflate/main.c src/inflate/inflate.c gcc -I include src/inflate/main.c src/inflate/inflate.c -o inflate +myunzip : include/inflate.h src/inflate/inflate.c src/myunzip/myunzip.c + gcc -I include src/inflate/inflate.c src/myunzip/myunzip.c -o myunzip + test_inflate : src/inflate/inflate.c tests/inflate_test.cpp g++ -I include src/inflate/inflate.c tests/inflate_test.cpp -o test_inflate -l gtest ./test_inflate clean : - rm myzip0 myunzip0 huffman inflate test_inflate + rm myzip0 myunzip0 huffman inflate test_inflate myunzip diff --git a/include/inflate.h b/include/inflate.h index 1afc34a6721e194a06d779df6a38d443373684fe..9e4d1e5fe7004a4fa98712dbba8c4ae051db9728 100644 --- a/include/inflate.h +++ b/include/inflate.h @@ -1,3 +1,5 @@ +#include <stdbool.h> + #ifndef _INFLATE_H_ #define _INFLATE_H_ @@ -67,11 +69,11 @@ int *read_lens(char *buf, huffman_t *hf_codes, * Read a single block from the buffer, and write the deflated * results to "out". */ -void read_block(char *buf, FILE *out); +bool read_block(char *buf, FILE *out); /* * Inflate the file fp. */ -void inflate(FILE *fp, char *fname); +void inflate(FILE *fp, long size, char *fname); #endif diff --git a/src/inflate/inflate.c b/src/inflate/inflate.c index c75a2fa8e459c9979bd54134f51841cc29ce2aca..e53928755974ce0a46002bf95512c01a328469f3 100644 --- a/src/inflate/inflate.c +++ b/src/inflate/inflate.c @@ -283,14 +283,14 @@ int *read_lens(char *buf, huffman_t *hf_codes, int num_symbols, int num_codes) { return lens; } -void read_block(char *buf, FILE *out) { +bool read_block(char *buf, FILE *out) { /* First bit is the BFINAL flag */ bool bfinal = get_next_bit(buf); /* Next two bits are BTYPE */ int btype = get_n_bits(buf, 2, true); - + /* By default, use fixed mapping */ huffman_t *hf = &HUFFMAN_FIXED; huffman_t *hf_dist = &HUFFMAN_FIXED_DISTS; @@ -346,7 +346,6 @@ void read_block(char *buf, FILE *out) { { /* Match a huffman code */ chunk_val = read_chunk(buf, *hf); - printf("%d\n", chunk_val); /* Literal, just write to output buffer */ if (chunk_val < 256) { fwrite(&chunk_val, 1, sizeof(char), out); @@ -381,32 +380,15 @@ void read_block(char *buf, FILE *out) { destroy_huffman(hf); destroy_huffman(hf_dist); } -} - -void truncate_suffix(char *fname) { - char *dot = strrchr(fname, '.'); - - const char *suffix = ".deflate"; - for (int i = 0; i < 8; i++) { - if (dot == NULL || *(dot + i) != suffix[i]) { - fprintf(stderr, "error: must be a .deflate file"); - exit(1); - } - } - *dot = '\0'; + return bfinal; } -void inflate(FILE *fp, char *fname) { - truncate_suffix(fname); - +void inflate(FILE *fp, long size, char *fname) { /* Create output file */ FILE *out = fopen(fname, "wb+"); /* Read the file into a buffer */ - fseek(fp, 0, SEEK_END); - long size = ftell(fp); - rewind(fp); char *buf = (char *) malloc(size); if (!buf) { fprintf(stderr, "error: memory error\n"); @@ -414,9 +396,8 @@ void inflate(FILE *fp, char *fname) { } fread(buf, 1, size, fp); - while (_CUR_BIT < 8 * size) { - read_block(buf, out); - } + /* Read until last block */ + while (!read_block(buf, out)) {} fclose(out); free(buf); diff --git a/src/inflate/main.c b/src/inflate/main.c index 852bd9fecbc63081c08f3dbfe1cc2ce11188d1db..58d27e1eb87041f44b9e9be76a3cceb105da7b74 100644 --- a/src/inflate/main.c +++ b/src/inflate/main.c @@ -1,8 +1,24 @@ #include <stdio.h> #include <stdbool.h> +#include <string.h> +#include <stdlib.h> #include <inflate.h> +void truncate_suffix(char *fname) { + char *dot = strrchr(fname, '.'); + + const char *suffix = ".deflate"; + + for (int i = 0; i < 8; i++) { + if (dot == NULL || *(dot + i) != suffix[i]) { + fprintf(stderr, "error: must be a .deflate file\n"); + exit(1); + } + } + *dot = '\0'; +} + int main(int argc, char *argv[]) { if (argc != 2) { fprintf(stderr, "usage: inflate [file]\n"); @@ -12,13 +28,18 @@ int main(int argc, char *argv[]) { char *fname = argv[1]; FILE *fp = fopen(fname, "rb"); - + if (!fp) { fprintf(stderr, "error: file not found\n"); return 1; } - inflate(fp, fname); + fseek(fp, 0, SEEK_END); + long size = ftell(fp); + rewind(fp); + + truncate_suffix(fname); + inflate(fp, size, fname); fclose(fp); return 0; diff --git a/src/myunzip/myunzip.c b/src/myunzip/myunzip.c new file mode 100644 index 0000000000000000000000000000000000000000..bb8d64e25d2da52227fb9dfa0a7a5c54bda15c6c --- /dev/null +++ b/src/myunzip/myunzip.c @@ -0,0 +1,89 @@ +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <stdint.h> + +#include <inflate.h> + +struct file_record { + uint32_t signature; + uint16_t version; + uint16_t flag; + uint16_t compression; + uint16_t mod_time; + uint16_t mod_date; + uint32_t crc32; + uint32_t compressed_size; + uint32_t uncompressed_size; + uint16_t file_name_len; + uint16_t extra_len; +} +/* Don't pad this struct, i.e. guaranteed that sizeof(struct file_record) = + * sum of the sizes of its elements. This makes the code a bit nicer; + * we can just fread() the first sizeof(struct file_record) bytes in the + * file directly into the struct. + */ +__attribute__((packed)); + +typedef struct file_record file_record_t; + +const int UNCOMPRESSED = 0; +const int DEFLATE = 8; + +void unzip(FILE *fp) { + file_record_t *record = (file_record_t *) malloc(sizeof(file_record_t)); + if (!record) { + fprintf(stderr, "error: memory error\n"); + exit(1); + } + + /* Read metadata */ + fread(record, 1, sizeof(file_record_t), fp); + + char *fname = (char *) malloc(record->file_name_len); + fread(fname, 1, record->file_name_len, fp); + + /* Move pointer to start of file data */ + fseek(fp, record->extra_len, SEEK_CUR); + + int size; + if (record->compression == UNCOMPRESSED) { + + FILE *out = fopen(fname, "wb"); + size = record->uncompressed_size; + char *buf = (char *) malloc(size * sizeof(char)); + fread(buf, 1, size, fp); + fwrite(buf, 1, size, out); + fclose(out); + + } else if (record->compression == DEFLATE) { + inflate(fp, record->compressed_size, fname); + + } else { + fprintf(stderr, "error: invalid compression\n"); + exit(1); + } + + free(record); +} + +int main(int argc, char *argv[]) { + FILE *fp; + + if (argc != 2) { + fprintf(stderr, "usage: myunzip0 [filename]\n"); + return 1; + } + + fp = fopen(argv[1], "rb"); + + if (!fp) { + fprintf(stderr, "error: no such file\n"); + return 1; + } + + unzip(fp); + fclose(fp); + + return 0; +}