diff options
Diffstat (limited to 'analyze-gz.c')
-rw-r--r-- | analyze-gz.c | 280 |
1 files changed, 280 insertions, 0 deletions
diff --git a/analyze-gz.c b/analyze-gz.c new file mode 100644 index 0000000..a0c6d40 --- /dev/null +++ b/analyze-gz.c @@ -0,0 +1,280 @@ +#define _POSIX_C_SOURCE 200809L +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> +#include <string.h> +#include <stdbool.h> +#include <assert.h> +#include <ctype.h> + +#define ID1 31 +#define ID2 139 + +enum compression_method { + DEFLATE = 8, +}; + +enum flag { + FTEXT = 1 << 0, + FHCRC = 1 << 1, + FEXTRA = 1 << 2, + FNAME = 1 << 3, + FCOMMENT = 1 << 4, +}; + +enum operating_system { + FAT = 0, + AMIGA, + VMS, + UNIX, + CMS, + ATARI_TOS, + HPFS, + MACINTOSH, + Z_SYSTEM, + CP_M, + TOPS20, + NTFS, + QDOS, + ACORN_RISCOS, +}; + +const char *os_str(enum operating_system os) { + switch (os) { + case FAT: return "FAT filesystem (MS-DOS, OS/2, NT/Win32)"; + case AMIGA: return "Amiga"; + case VMS: return "VMS (or OpenVMS)"; + case UNIX: return "Unix"; + case CMS: return "VM/CMS"; + case ATARI_TOS: return "Atari TOS"; + case HPFS: return "HPFS filesystem (OS/2, NT)"; + case MACINTOSH: return "Macintosh"; + case Z_SYSTEM: return "Z-System"; + case CP_M: return "CP/M"; + case TOPS20: return "TOPS-20"; + case NTFS: return "NTFS filesystem (NT)"; + case QDOS: return "QDOS"; + case ACORN_RISCOS: return "Acorn RISCOS"; + default: return "unknown"; + } +} + +struct member { + uint8_t id1, id2, cm, flg; + uint32_t mtime; + uint8_t xfl, os; +} __attribute__((packed)); + + +enum color { + BLACK = 0, + RED, + GREEN, + YELLOW, + BLUE, + PURPLE, + CYAN, + WHITE, +}; + + +struct segment { + enum color c; + size_t len; + uint8_t *data; +}; + +enum color next_color(void) { + static uint8_t cc = 0; + cc += 1; + cc %= 6; + return cc + 1; +} + +void color_put(enum color c) { + printf("\x1b[0;3%im", c); +} + +void color_reset(void) { + printf("\x1b[m"); +} + +#define LINELEN 0x8 +struct segment line_segments[LINELEN]; +size_t file_idx = 0; +size_t line_idx = 0; +size_t current_segment = 0; + +void cleanup_line(void) { + for (int i = 0; i < current_segment; i++) { + free(line_segments[i].data); + } +} + +void put_byte(uint8_t byte) { + static bool flip = 0; + printf("%02x", byte); + flip = !flip; + if (! flip) { + printf(" "); + } +} + +void flush_line() { + printf("%06lx: ", file_idx); + file_idx += LINELEN; + for (int i = 0; i < current_segment; i++) { + color_put(line_segments[i].c); + for (int b = 0; b < line_segments[i].len; b++) { + put_byte(line_segments[i].data[b]); + } + color_reset(); + } + for (int i = 0; i < current_segment; i++) { + color_put(line_segments[i].c); + for (int b = 0; b < line_segments[i].len; b++) { + uint8_t ch = line_segments[i].data[b]; + if (isprint(ch)) { + printf("%c", ch); + } else { + printf("."); + } + } + color_reset(); + } + printf("\n"); +} + +void add_segment(struct segment segment) { + line_segments[current_segment++] = segment; + line_idx += segment.len; + if (line_idx == LINELEN) { + flush_line(); + cleanup_line(); + current_segment = 0; + line_idx = 0; + } +} + +/* + * Buf MUST be a malloc:ed buffer, which is invalidated on this call. + */ +void write_chunk(uint8_t *buf, size_t len) { + if (line_idx + len >= LINELEN) { + enum color c = next_color(); + size_t len1 = LINELEN - line_idx, + len2 = len - len1; + uint8_t *buf1 = malloc(len1), + *buf2 = malloc(len2); + memcpy(buf1, buf, len1); + memcpy(buf2, buf + len1, len2); + free(buf); + struct segment + seg1 = { + .c = c, + .len = len1, + .data = buf1, + }, + seg2 = { + .c = c, + .len = len2, + .data = buf2, + }; + add_segment(seg1); + add_segment(seg2); + } else { + struct segment seg = { + .c = next_color(), + .len = len, + .data = buf, + }; + add_segment(seg); + } +} + +void write_chunk_s(uint8_t *buf, size_t len) { + assert (len >= 0); + uint8_t *true_buf = malloc(len); + memcpy(true_buf, buf, len); + write_chunk(true_buf, len); +} + +int main(int argc, char *argv[]) { + if (argc == 1) { + fprintf(stderr, "Usage: %s <filename.gz>\n", argv[0]); + return 1; + } + + FILE *f = fopen(argv[1], "rb"); + size_t len; + + struct member header; + uint8_t *extra_fields = NULL; + uint16_t crc16 = 0; + char *filename = NULL; + char *file_comment = NULL; + + len = fread(&header, sizeof(header), 1, f); + + if (len != 1) { + fprintf(stderr, "Unexpected end of header\n"); + return 1; + } + + write_chunk_s((uint8_t[2]) {header.id1, header.id2}, 2); + write_chunk_s(&header.cm, 1); + write_chunk_s(&header.flg, 1); + { + size_t s = sizeof(header.mtime); + uint8_t *buf = malloc(s); + memcpy(buf, &header.mtime, s); + write_chunk(buf, s); + } + write_chunk_s(&header.xfl, 1); + write_chunk_s(&header.os, 1); + + if (header.flg & FEXTRA) { + uint16_t xlen; + len = fread(&xlen, sizeof(xlen), 1, f); + { + uint8_t *buf = malloc(len); + memcpy(buf, &xlen, len); + write_chunk(buf, len); + } + extra_fields = malloc(xlen); + len = fread(extra_fields, 1, xlen, f); + write_chunk(extra_fields, xlen); + } + + if (header.flg & FNAME) { + size_t n; + len = getdelim(&filename, &n, '\0', f); + write_chunk((uint8_t*) filename, n); + } + + if (header.flg & FCOMMENT) { + size_t n; + len = getdelim(&file_comment, &n, '\0', f); + write_chunk((uint8_t*) file_comment, n); + } + + if (header.flg & FHCRC) { + len = fread(&crc16, sizeof(crc16), 1, f); + { + uint8_t *buf = malloc(len); + memcpy(buf, &crc16, len); + write_chunk(buf, len); + } + } + +// cleanup: + if (extra_fields) free(extra_fields); + if (filename) free(filename); + if (file_comment) free(file_comment); + flush_line(); + cleanup_line(); + + printf("\n"); + + +} |