aboutsummaryrefslogtreecommitdiff
path: root/analyze-gz.c
diff options
context:
space:
mode:
Diffstat (limited to 'analyze-gz.c')
-rw-r--r--analyze-gz.c280
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");
+
+
+}