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
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdint.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) {
size = record->uncompressed_size;
} else if (record->compression == DEFLATE) {
size = record->compressed_size;
strcat(fname, ".deflate");
} else {
fprintf(stderr, "error: invalid compression\n");
exit(1);
}
FILE *out = fopen(fname, "wb");
/* TODO is there a better way to do this?
* worried about storing a huge buffer for large files... */
char *buf = (char *) malloc(size * sizeof(char));
fread(buf, 1, size, fp);
fwrite(buf, 1, size, out);
fclose(out);
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;
}