login

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

#define MIN(A, B) ((A) < (B) ? (A) : (B))
#define MAX(A, B) ((A) > (B) ? (A) : (B))

typedef struct Palette {
    int size;
    uint8_t colors[0x100 * 3];
} Palette;

typedef struct GCE {
    uint16_t delay;
    uint8_t tindex;
    uint8_t disposal;
    int input;
    int transparency;
} GCE;

typedef struct GIF {
    int fd;
    uint16_t width, height;
    uint16_t loop_count;
    GCE gce;
    Palette *palette;
    Palette lct, gct;
    uint8_t *frame;
} GIF;

typedef struct Entry {
    uint16_t length;
    uint16_t prefix;
    uint8_t  suffix;
} Entry;

typedef struct Table {
    int bulk;
    int nentries;
    Entry *entries;
} Table;

uint16_t
read_num(int fd)
{
    uint8_t bytes[2];

    read(fd, bytes, 2);
    return bytes[0] + (((uint16_t) bytes[1]) << 8);
}

GIF *
open_gif(const char *fname)
{
    int fd;
    uint8_t sigver[3];
    uint16_t width, height;
    uint8_t fdsz, bgidx, aspect;
    int gct_sz;
    GIF *gif;

    fd = open(fname, O_RDONLY);
    if (fd == -1) return NULL;
    /* Header */
    read(fd, sigver, 3);
    if (memcmp(sigver, "GIF", 3) != 0) {
        fprintf(stderr, "invalid signature\n");
        goto fail;
    }
    /* Version */
    read(fd, sigver, 3);
    if (memcmp(sigver, "89a", 3) != 0) {
        fprintf(stderr, "invalid version\n");
        goto fail;
    }
    /* Width x Height */
    width  = read_num(fd);
    height = read_num(fd);
    /* FDSZ */
    read(fd, &fdsz, 1);
    /* Presence of GCT */
    if (!(fdsz & 0x80)) {
        fprintf(stderr, "no global color table");
        goto fail;
    }
    /* Color Space's Depth */
    if (((fdsz >> 4) & 7) != 7) {
        fprintf(stderr, "depth of color space is not 8 bits");
        goto fail;
    }
    /* Ignore Sort Flag. */
    /* GCT Size */
    gct_sz = 1 << ((fdsz & 0x07) + 1);
    /* Background Color Index */
    read(fd, &bgidx, 1);
    /* Aspect Ratio */
    read(fd, &aspect, 1);
    /* Create GIF Structure. */
    gif = calloc(1, sizeof(*gif) + width * height);
    if (!gif) goto fail;
    gif->fd = fd;
    gif->width  = width;
    gif->height = height;
    /* Read GCT */
    gif->gct.size = gct_sz;
    read(fd, gif->gct.colors, 3 * gif->gct.size);
    gif->palette = &gif->gct;
    gif->frame = (uint8_t *) &gif[1];
    goto ok;
fail:
    close(fd);
ok:
    return gif;
}

void
discard_sub_blocks(GIF *gif)
{
    uint8_t size;

    do {
        read(gif->fd, &size, 1);
        lseek(gif->fd, size, SEEK_CUR);
    } while (size);
}

/* Ignored extension. */
void
read_plain_text_ext(GIF *gif)
{
    fprintf(stderr, "ignoring plain text extension\n");
    /* Discard plain text metadata. */
    lseek(gif->fd, 13, SEEK_CUR);
    /* Discard plain text sub-blocks. */
    discard_sub_blocks(gif);
}

void
read_graphic_control_ext(GIF *gif)
{
    uint8_t rdit;

    /* Discard block size (always 0x04). */
    lseek(gif->fd, 1, SEEK_CUR);
    read(gif->fd, &rdit, 1);
    gif->gce.disposal = (rdit >> 2) & 3;
    gif->gce.input = rdit & 2;
    gif->gce.transparency = rdit & 1;
    gif->gce.delay = read_num(gif->fd);
    read(gif->fd, &gif->gce.tindex, 1);
    /* Skip block terminator. */
    lseek(gif->fd, 1, SEEK_CUR);
}

/* Ignored extension. */
void
read_comment_ext(GIF *gif)
{
    fprintf(stderr, "ignoring comment extension\n");
    /* Discard comment sub-blocks. */
    discard_sub_blocks(gif);
}

void
read_application_ext(GIF *gif)
{
    char app_id[8];
    char app_auth_code[3];

    /* Discard block size (always 0x0B). */
    lseek(gif->fd, 1, SEEK_CUR);
    /* Application Identifier. */
    read(gif->fd, app_id, 8);
    /* Application Authentication Code. */
    read(gif->fd, app_auth_code, 3);
    if (!strncmp(app_id, "NETSCAPE", sizeof(app_id))) {
        /* Discard block size (0x03) and constant byte (0x01). */
        lseek(gif->fd, 2, SEEK_CUR);
        gif->loop_count = read_num(gif->fd);
        /* Skip block terminator. */
        lseek(gif->fd, 1, SEEK_CUR);
    } else {
        fprintf(stderr, "ignoring application extension: %.*s\n",
                (int) sizeof(app_id), app_id);
        discard_sub_blocks(gif);
    }
}

void
read_ext(GIF *gif)
{
    uint8_t label;

    read(gif->fd, &label, 1);
    switch (label) {
    case 0x01:
        read_plain_text_ext(gif);
        break;
    case 0xF9:
        read_graphic_control_ext(gif);
        break;
    case 0xFE:
        read_comment_ext(gif);
        break;
    case 0xFF:
        read_application_ext(gif);
        break;
    default:
        fprintf(stderr, "unknown extension: %02X\n", label);
    }
}

Table *
new_table(int key_size)
{
    int key;
    int init_bulk = MAX(1 << (key_size + 1), 0x100);
    Table *table = malloc(sizeof(*table) + sizeof(Entry) * init_bulk);
    if (table) {
        table->bulk = init_bulk;
        table->nentries = (1 << key_size) + 2;
        table->entries = (Entry *) &table[1];
        for (key = 0; key < (1 << key_size); key++)
            table->entries[key] = (Entry) {1, 0xFFF, key};
    }
    return table;
}

uint16_t
get_key(GIF *gif, int key_size, uint8_t *sub_len, uint8_t *byte)
{
    int bits_read;
    int rpad;
    int frag_size;
    uint16_t key;

    for (bits_read = 0; bits_read < key_size; bits_read += frag_size) {
        rpad = bits_read % 8;
        if (rpad == 0) {
            /* Update byte. */
            if (*sub_len == 0)
                read(gif->fd, sub_len, 1); /* Must be nonzero! */
            read(gif->fd, byte, 1);
            (*sub_len)--;
        }
        frag_size = MIN(key_size - bits_read, 8 - rpad);
        key |= ((uint16_t) ((*byte) >> rpad)) << bits_read;
    }
    /* Clear extra bits to the left. */
    key &= (1 << key_size) - 1;
    return key;
}

void
read_image_data(GIF *gif)
{
    uint8_t sub_len;
    uint8_t byte;
    int key_size;
    uint16_t key, clear, stop;
    Table *table;

    read(gif->fd, &byte, 1);
    key_size = (int) byte;
    clear = 1 << key_size;
    stop = clear + 1;
    table = new_table(key_size);
    key_size++;
    sub_len = 0;
    key = get_key(gif, key_size, &sub_len, &byte);
    printf("%03X == %03X\n", key, clear);
    exit(0);
}

void
read_image(GIF *gif)
{
    uint16_t x, y, w, h;
    uint8_t fisrz;
    int interlace;

    /* Image Descriptor. */
    x = read_num(gif->fd);
    y = read_num(gif->fd);
    w = read_num(gif->fd);
    h = read_num(gif->fd);
    read(gif->fd, &fisrz, 1);
    interlace = fisrz & 0x40;
    /* Ignore Sort Flag. */
    /* Local Color Table? */
    if (fisrz & 0x80) {
        /* Read LCT */
        gif->lct.size = 1 << ((fisrz & 0x07) + 1);
        read(gif->fd, gif->lct.colors, 3 * gif->lct.size);
        gif->palette = &gif->lct;
    } else
        gif->palette = &gif->gct;
    /* Image Data. */
    read_image_data(gif);
}

/* Return 1 if got a frame; 0 if got GIF trailer; -1 if error. */
int
get_frame(GIF *gif)
{
    char sep;

    read(gif->fd, &sep, 1);
    while (sep != ',') {
        if (sep == ';')
            return 0;
        if (sep == '!')
            read_ext(gif);
        else return -1;
        read(gif->fd, &sep, 1);
    }
    read_image(gif);
    return 1;
}

int
main()
{
    int nframes = 0;
    GIF *gif = open_gif("example.gif");
    if (gif) {
        printf("%ux%u\n", gif->width, gif->height);
        printf("%d colors\n", gif->palette->size);
        while (get_frame(gif)) nframes++;
        close(gif->fd);
        free(gif);
        printf("%d frames\n", nframes);
    }
    return 0;
}