void print_exth(const MOBIData *m) {
    if (m->eh == NULL) {
        return;
    }
    /* Linked list of MOBIExthHeader structures holds EXTH records */
    const MOBIExthHeader *curr = m->eh;
    if (curr != NULL) {
        printf("\nEXTH records:\n");
    }
    uint32_t val32;
    while (curr != NULL) {
        /* check if it is a known tag and get some more info if it is */
        MOBIExthMeta tag = mobi_get_exthtagmeta_by_tag(curr->tag);
        if (tag.tag == 0) {
            /* unknown tag */
            /* try to print the record both as string and numeric value */
            char *str = malloc(curr->size + 1);
            if (!str) {
                printf("Memory allocation failed\n");
                exit(1);
            }
            unsigned i = 0;
            unsigned char *p = curr->data;
            while (i < curr->size && isprint(*p)) {
                str[i] = (char)*p++;
                i++;
            }
            str[i] = '\0';
            val32 = mobi_decode_exthvalue(curr->data, curr->size);
            printf("Unknown (%i): %s (%u)\n", curr->tag, str, val32);
            free(str);
        } else {
            /* known tag */
            unsigned i = 0;
            size_t size = curr->size;
            unsigned char *data = curr->data;
            switch (tag.type) {
                    /* numeric */
                case EXTH_NUMERIC:
                    val32 = mobi_decode_exthvalue(data, size);
                    printf("%s (%i): %u\n", tag.name, tag.tag, val32);
                    break;
                    /* string */
                case EXTH_STRING:
                {
                    char *exth_string = mobi_decode_exthstring(m, data, size);
                    if (exth_string) {
                        printf("%s (%i): %s\n", tag.name, tag.tag, exth_string);
                        free(exth_string);
                    }
                    break;
                }
                    /* binary */
                case EXTH_BINARY:
                {
                    char *str = malloc(2 * size + 1);
                    if (!str) {
                        printf("Memory allocation failed\n");
                        exit(1);
                    }
                    str[0] = '\0';
                    while (size) {
                        uint8_t val8 = *data++;
                        sprintf(&str[i], "%02x", val8);
                        i += 2;
                        size--;
                    }
                    printf("%s (%i): 0x%s\n", tag.name, tag.tag, str);
                    free(str);
                    break;
                }
                default:
                    break;
            }
        }
        curr = curr->next;
    }
}
void print_exth(const MOBIData *m) {
    if (m->eh == NULL) {
        return;
    }
     
    const MOBIExthHeader *curr = m->eh;
    if (curr != NULL) {
        printf("\nEXTH records:\n");
    }
    uint32_t val32;
    while (curr != NULL) {
         
        MOBIExthMeta tag = mobi_get_exthtagmeta_by_tag(curr->tag);
        if (tag.tag == 0) {
             
             
            char *str = malloc(curr->size + 1);
            if (!str) {
                printf("Memory allocation failed\n");
                exit(1);
            }
            unsigned i = 0;
            unsigned char *p = curr->data;
            while (i < curr->size && isprint(*p)) {
                str[i] = (char)*p++;
                i++;
            }
            str[i] = '\0';
            val32 = mobi_decode_exthvalue(curr->data, curr->size);
            printf("Unknown (%i): %s (%u)\n", curr->tag, str, val32);
            free(str);
        } else {
             
            unsigned i = 0;
            size_t size = curr->size;
            unsigned char *data = curr->data;
            switch (tag.type) {
                     
                case EXTH_NUMERIC:
                    val32 = mobi_decode_exthvalue(data, size);
                    printf("%s (%i): %u\n", tag.name, tag.tag, val32);
                    break;
                     
                case EXTH_STRING:
                {
                    char *exth_string = mobi_decode_exthstring(m, data, size);
                    if (exth_string) {
                        printf("%s (%i): %s\n", tag.name, tag.tag, exth_string);
                        free(exth_string);
                    }
                    break;
                }
                     
                case EXTH_BINARY:
                {
                    char *str = malloc(2 * size + 1);
                    if (!str) {
                        printf("Memory allocation failed\n");
                        exit(1);
                    }
                    str[0] = '\0';
                    while (size) {
                        uint8_t val8 = *data++;
                        sprintf(&str[i], "%02x", val8);
                        i += 2;
                        size--;
                    }
                    printf("%s (%i): 0x%s\n", tag.name, tag.tag, str);
                    free(str);
                    break;
                }
                default:
                    break;
            }
        }
        curr = curr->next;
    }
}