myunzip0.c 2.15 KB
#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; 
}