diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..c797814 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,77 @@ +# Image-View Changelog + +## v0.1.x - **The PNG Update** + +Incremental support for PNG image types is being added + +### v0.1.7 + +* Extract chunk validation to macro + * Instead of validating the chunk type (and data) manually, the `PNG_VALIDATE(chunkType, defaultReturn)` macro should be used + +### v0.1.6 + +* Added support for remaining miscellaneous chunk types + * `bKGD` chunks can be processed through the `png_bKGD_*()` functions + * `hIST` chunks can be processed through the `png_hIST_*()` functions + * `sPLT` chunks can be processed through the `png_sPLT_*()` functions + +### v0.1.5a + +* Refactor functions to more accurately represent chunk order, as defined in the International Standard + +### v0.1.5 + +* Added support for color space information chunks: + * `cHRM` chunks can be processed through the `png_cHRM_*()` functions + * `gAMA` chunks can be processed through the `png_gAMA*()` functions + * `iCCP` chunks can be processed through the `png_iCCP_*()` functions + * `sBIT` chunks can be processed through the `png_sBIT_*()` functions + * `sRGB` chunks can be processed through the `png_sRGB_get()` function + +### v0.1.4 + +* Added support for transparency ancillary chunks: + * `tRNS` chunks can be processed through the `png_tRNS_*()` functions + +### v0.1.3 + +* Added command-line option for a deep scan: + * A normal scan (options `-s` or `--scan`) only reveal the chunks and their length + * A deep scan reveals information pertinent to each chunk (first 10 palette entries, textual/date information, etc.) + +### v0.1.2 + +* Added support for remaining critical chunks: + * `PLTE` chunks can be processed through the `png_PLTE_*()` functions + * `IDAT` chunks can be processed through the `png_IDAT_*()` functions + * `IEND` chunks can be validated with the `png_IEND_valid()` function + +### v0.1.1 + +* Added extraction for other image attributes: + * Bit depth with `png_attr_bit_depth()` + * Color type with `png_attr_col_type()` + * Compression method with `png_attr_comp_method()` + * Filter method with `png_attr_filter_method()` + * Interlace method with `png_attr_il_method()` +* Added ability to quickly extract timestamp from `tIME` chunks in ISO 8601 format with `png_tIME_iso8601()` +* Added support for two new chunks: + 1. `pHYs` chunks are supported through `png_pHYs_*()` functions + 2. `zTXt` chunks are supported through `png_zTXt_*()` functions +* Other various improvements were made: + * Memory management was cleaned up + * Logging was enhanced and messaging was removed, to be implemented elsewhere + +### v0.1.0 + +* Scan through PNG chunks with `png_open()`, `png_chunk_next()`; online documentation coming soon +* Some textual metadata supported: + 1. `tEXt` chunks are supported through `png_tEXt_*()` functions + 2. `iTXt` chunks are partially supported for keyword, language and text fields through `png_iTXt_*()` functions + 3. `tIME` chunks are supported for extracting each timestamp attribute through `png_tIME_*()` functions +* Easy attribute extraction for image width and height: `png_attr_w()` and `png_attr_h()`, respectively + +## v0.0.0 + +* No features have been implemented yet diff --git a/formats/png.c b/formats/png.c index dd96fb9..ae689d5 100644 --- a/formats/png.c +++ b/formats/png.c @@ -1,14 +1,27 @@ #include #include #include +#include #include "png.h" #include "../utils/version.h" +/* + * Initialize an empty PNG image + * + * Returns: NULL on error + * Initialized Formant_PNG type + */ Format_PNG png_new() { Format_PNG png = (Format_PNG) malloc(sizeof(struct format_png_t)); + if (png == NULL) return NULL; + // TODO Initialize PNG image + png->IHDR = NULL; + png->fin = NULL; + png->name = NULL; + png->bytes = 0; return png; } @@ -21,7 +34,7 @@ Format_PNG png_new() { * > 0 - file is not of type PNG */ int png_open(Format_PNG png, char *filename) { - char buf[512]; + char buf[PNG_SIG_SZ]; int rv; if (filename == NULL) { png->name = NULL; @@ -41,13 +54,16 @@ int png_open(Format_PNG png, char *filename) { rv = fread(buf, sizeof(char), PNG_SIG_SZ, png->fin); if (rv != PNG_SIG_SZ) return 1; + png->bytes += sizeof(char) * rv; /* Validate the signature */ - if ((*(long long*) (&buf)) - SIG_PNG != 0) return 1; + if ((*(long long*) (&buf)) - PNG_SIG != 0) return 1; png->IHDR = png_chunk_next(png); - if (png->IHDR == NULL) { + if (png->IHDR == (png_chunk_t *) -1) { + free(png->name); + fclose(png->fin); return -1; } @@ -56,36 +72,55 @@ int png_open(Format_PNG png, char *filename) { /* * Close the png file and free the resources associated with it + * + * Returns: void */ void png_close(Format_PNG png) { if (png->name) free(png->name); if (png->fin) fclose(png->fin); + if (png->IHDR) png_chunk_free(png->IHDR); free(png); } +/* + * Extract the next chunk from the PNG image + * + * Returns: -1 on error + * NULL when a valid chunk cannot be read (EOF) + * Valid chunk from PNG image + */ png_chunk_t *png_chunk_next(Format_PNG png) { - if (feof(png->fin) || ferror(png->fin)) return NULL; + int read = 0; int rv; + if (feof(png->fin) || ferror(png->fin)) return (png_chunk_t *) -1; png_chunk_t *chunk = (png_chunk_t *) malloc(sizeof(png_chunk_t)); /* Read chunk length */ rv = fread(&chunk->length, PNG_CHNK_LEN, 1, png->fin); - if (rv != 1) { + if (feof(png->fin)) { free(chunk); return NULL; } + if (rv != 1) { + free(chunk); + + return (png_chunk_t *) -1; + } + read += sizeof(char) * rv; /* Normalize length */ chunk->length = ntohl(chunk->length); /* Read chunk type */ - rv = fread(chunk->type, 1, PNG_CHNK_TYPE_SZ, png->fin); + rv = fread(chunk->type, sizeof(char), PNG_CHNK_TYPE_SZ, png->fin); if (rv != PNG_CHNK_TYPE_SZ) { + fseek(png->fin, -1 * read, SEEK_CUR); fprintf(stderr, "[%s/png]: Unexpected EOF in reading chunk type, read %d/%d chunk type bytes\n", IV_PROGRAM_NAME, rv, PNG_CHNK_TYPE_SZ); free(chunk); return NULL; } + read += sizeof(char) * rv; /* Allocate memory for chunk data */ chunk->data = malloc(chunk->length); @@ -94,55 +129,1066 @@ png_chunk_t *png_chunk_next(Format_PNG png) { if (chunk->length != 0) { rv = fread(chunk->data, 1, chunk->length, png->fin); if (rv != chunk->length) { + fseek(png->fin, -1 * read, SEEK_CUR); fprintf(stderr, "[%s/png]: Unexpected EOF in reading chunk '%.4s', read %d/%d chunk data bytes\n", IV_PROGRAM_NAME, chunk->type, rv, chunk->length); free(chunk->data); free(chunk); return NULL; } - } else chunk->data = NULL; + read += rv; + } else { + free(chunk->data); + chunk->data = NULL; + } /* Read CRC */ rv = fread(&chunk->CRC, sizeof(int), 1, png->fin); if (rv != 1) { - fprintf(stderr, "[img"); + fseek(png->fin, -1 * read, SEEK_CUR); + fprintf(stderr, "[%s/png]: Unexpected EOF in reading CRC for chunk '%.4s', read %d/%d bytes\n", IV_PROGRAM_NAME, chunk->type, rv, 4); free(chunk->data); free(chunk); return NULL; } + read += sizeof(int) * rv; + + chunk->png = png; return chunk; } +/* + * Free the memory allocated to a png chunk + * + * Returns: void + */ +void png_chunk_free(png_chunk_t *chunk) { + if (chunk->data) free(chunk->data); + + free(chunk); +} + +/* + * Fetch the palette as a two-dimensional array: + * [n][0] - Red channel + * [n][1] - Green channel + * [n][2] - Blue channel + * + * Returns: NULL on error + * (char **) -1 if chunk length is not divisible by 3 (required by Internation Standard section 11.2.3) + */ +char **png_PLTE_get(png_chunk_t *chunk) { + PNG_VALIDATE(PNG_CHUNK_PALETTE, NULL); + + int n = chunk->length % 3, i; + + if (n != 0) return (char **) -1; + + n = chunk->length / 3; + char **palette = (char **) malloc(sizeof(char *) * n); + + for (i = 0; i < n; i++) { + palette[i] = malloc(sizeof(char) * 3); + palette[i][0] = ((char *) chunk->data)[3*i]; + palette[i][1] = ((char *) chunk->data)[3*i+1]; + palette[i][2] = ((char *) chunk->data)[3*i+2]; + } + + return palette; +} + +/* + * Fetch the number of entries in a palette chunk + * + * Returns: -1 on error + * 0 if chunk does not conform to International Standard (section 11.2.3) + * + */ +int png_PLTE_length(png_chunk_t *chunk) { + PNG_VALIDATE(PNG_CHUNK_PALETTE, -1); + + if (chunk->length % 3 != 0) return 0; + + return chunk->length / 3; +} + +/* + * Free the memory allocated for a palette + * + * Returns: void + */ +void png_PLTE_free(char **palette, int len) { + if (palette == NULL || palette[0] == NULL) return; + + if (len <= 0) return; + + while (len--) { + free(palette[len]); + } + free(palette); +} + +/* + * Get the included datastream for the IDAT chunk + * + * Returns: NULL on error + */ +void *png_IDAT_get(png_chunk_t *chunk) { + PNG_VALIDATE(PNG_CHUNK_DATA, NULL); + + void *data = malloc(chunk->length); + + memcpy(data, chunk->data, chunk->length); + + return data; +} + +/* + * Get the length of the datastream for this IDAT chunk + * + * Returns: -1 on error + */ +int png_IDAT_length(png_chunk_t *chunk) { + PNG_VALIDATE(PNG_CHUNK_DATA, -1); + + return chunk->length; +} + +/* + * Validate an IEND chunk. An IEND chunk is considered valid if it has no length and no data + * + * Returns: 1 if chunk is invalid + * 0 if chunk is valid + */ +int png_IEND_valid(png_chunk_t *chunk) { + if (strncmp(chunk->type, PNG_CHUNK_END, PNG_CHNK_LEN) != 0) return 1; + + if (chunk->length != 0 || chunk->data != NULL) return 1; + + return 0; +} + +/* + * Fetch transparency from tRNS chunk (color type 0 - grayscale) + * + * Returns: -1 on error (image could be of the wrong color type) + */ +short png_tRNS_gray(png_chunk_t *chunk) { + PNG_VALIDATE(PNG_CHUNK_TRANSPARENCY, -1); + + if (png_attr_col_type(chunk->png) != 0) return -1; + + return ntohs(*((short *)chunk->data)); +} + +/* + * Fetch transparency samples from tRNS chunk (color type 2 - truecolor) + * + * Returns: NULL on error + * Array of 3 sample values (R, G, B); must be free'd + */ +short *png_tRNS_true(png_chunk_t *chunk) { + PNG_VALIDATE(PNG_CHUNK_TRANSPARENCY, NULL); + + if (png_attr_col_type(chunk->png) != 2) return NULL; + + short *array = (short *) malloc(sizeof(short) * 3); + int i; + + for (i = 0; i < 3; i++) { + array[i] = ntohs(*(((short *) chunk->data) + i)); + } + + return array; +} + +/* + * Fetch transparency palette from tRNS chunk (color type 3 - indexed-color) + * + * Returns: NULL on error + * Array of alpha channel for palette indexes; must be free'd + */ +char *png_tRNS_index(png_chunk_t *chunk) { + PNG_VALIDATE(PNG_CHUNK_TRANSPARENCY, NULL); + + if (png_attr_col_type(chunk->png) != 3) return NULL; + + char *palette = (char *) malloc(sizeof(char) * chunk->length); + int i; + + for (i = 0; i < chunk->length; i++) { + palette[i] = ((char *)(chunk->data))[i]; + } + + return palette; +} + +/* + * Fetch the length of the transparency palette from tRNS chunk (color type 3 - indexed-color) + * + * Returns: -1 on error + * Length of array returned by `png_tRNS_index()` + */ +int png_tRNS_index_length(png_chunk_t *chunk) { + PNG_VALIDATE(PNG_CHUNK_TRANSPARENCY, -1); + + if (png_attr_col_type(chunk->png) != 3) return -1; + + return chunk->length; +} + +/* + * Fetch the white point X from the cHRM chunk + * + * Returns: -1 on error + */ +float png_cHRM_whiteX(png_chunk_t *chunk) { + PNG_VALIDATE(PNG_CHUNK_CHROMACITY, -1); + + int whiteX = *((int *) chunk->data); + whiteX = ntohl(whiteX); + + return whiteX / PNG_RATIO; +} + +/* + * Fetch the white point Y from the cHRM chunk + * + * Returns: -1 on error + */ +float png_cHRM_whiteY(png_chunk_t *chunk) { + PNG_VALIDATE(PNG_CHUNK_CHROMACITY, -1); + + int whiteY = *(((int *)chunk->data)+1); + whiteY = ntohl(whiteY); + + return whiteY / PNG_RATIO; +} + +/* + * Fetch the red point X from the cHRM chunk + * + * Returns: -1 on error + */ +float png_cHRM_redX(png_chunk_t *chunk) { + PNG_VALIDATE(PNG_CHUNK_CHROMACITY, -1); + + int redX = *(((int *)chunk->data)+2); + redX = ntohl(redX); + + return redX / PNG_RATIO; +} + +/* + * Fetch the red point Y from the cHRM chunk + * + * Returns: -1 on error + */ +float png_cHRM_redY(png_chunk_t *chunk) { + PNG_VALIDATE(PNG_CHUNK_CHROMACITY, -1); + + int redY = *(((int *)chunk->data)+3); + redY = ntohl(redY); + + return redY / PNG_RATIO; +} + +/* + * Fetch the green point X from the cHRM chunk + * + * Returns: -1 on error + */ +float png_cHRM_greenX(png_chunk_t *chunk) { + PNG_VALIDATE(PNG_CHUNK_CHROMACITY, -1); + + int greenX = *(((int *)chunk->data)+4); + greenX = ntohl(greenX); + + return greenX / PNG_RATIO; +} + +/* + * Fetch the green point Y from the cHRM chunk + * + * Returns: -1 on error + */ +float png_cHRM_greenY(png_chunk_t *chunk) { + PNG_VALIDATE(PNG_CHUNK_CHROMACITY, -1); + + int greenY = *(((int *)chunk->data)+5); + greenY = ntohl(greenY); + + return greenY / PNG_RATIO; +} + +/* + * Fetch the blue point X from the cHRM chunk + * + * Returns: -1 on error + */ +float png_cHRM_blueX(png_chunk_t *chunk) { + PNG_VALIDATE(PNG_CHUNK_CHROMACITY, -1); + + int blueX = *(((int *)chunk->data)+6); + blueX = ntohl(blueX); + + return blueX / PNG_RATIO; +} + +/* + * Fetch the blue point Y from the cHRM chunk + * + * Returns: -1 on error + */ +float png_cHRM_blueY(png_chunk_t *chunk) { + PNG_VALIDATE(PNG_CHUNK_CHROMACITY, -1); + + int blueY = *(((int *)chunk->data)+7); + blueY = ntohl(blueY); + + return blueY / PNG_RATIO; +} + +/* + * Calculate the image gamma from the gAMA chunk + * + * Returns: -1 on error + */ +float png_gAMA(png_chunk_t *chunk) { + PNG_VALIDATE(PNG_CHUNK_GAMMA, -1); + + int gamma = *((int *)chunk->data); + gamma = ntohl(gamma); + + return gamma / PNG_RATIO; +} + +/* + * Fetch the raw image gamma from the gAMA chunk (gamma times 100000) + * + * Returns: -1 on error + */ +int png_gAMA_raw(png_chunk_t *chunk) { + PNG_VALIDATE(PNG_CHUNK_GAMMA, -1); + + int gamma = *((int *)chunk->data); + gamma = ntohl(gamma); + + return gamma; +} + +/* + * Fetch the name of the embedded ICC (International Color Consortium) profile + * + * Returns: NULL on error + * Profile name on success; must be free'd + */ +char *png_iCCP_name(png_chunk_t *chunk) { + PNG_VALIDATE(PNG_CHUNK_ICCP, NULL); + + return strdup((char *)chunk->data); +} + +/* + * Fetch the compression method of the embedded ICC (International Color Consortium) profile + * + * Returns: -1 on error + */ +char png_iCCP_method(png_chunk_t *chunk) { + PNG_VALIDATE(PNG_CHUNK_ICCP, -1); + + int offset = strlen(chunk->data) + 1; + + return *((char *)chunk->data+offset); +} + +/* + * Fetch the compressed embedded ICC (International Color Consortium) profile + * + * Returns: NULL on error + * Datastream containing profile; must be free'd + */ +void *png_iCCP_profile(png_chunk_t *chunk) { + PNG_VALIDATE(PNG_CHUNK_ICCP, NULL); + + int offset = strlen(chunk->data) + 2; + int size = chunk->length - offset; + void *profile = malloc(size); + + memcpy(profile, chunk->data + offset, size); + + return profile; +} + +/* + * Fetch the length of the compressed embedded ICC (International Color Consortium) profile + * + * Returns: -1 on error + */ +int png_iCCP_profile_len(png_chunk_t *chunk) { + PNG_VALIDATE(PNG_CHUNK_ICCP, -1); + + return chunk->length - (strlen(chunk->data) + 2); +} + +/* + * Fetch the significant bits from the sBIT chunk + * + * Returns: NULL on error + */ +char *png_sBIT_get(png_chunk_t *chunk) { + PNG_VALIDATE(PNG_CHUNK_SIGBITS, NULL); + + char *bits; + int len; + + switch (png_attr_col_type(chunk->png)) { + case 0: + len = 1; + break; + case 2: + case 3: + len = 3; + break; + case 4: + len = 2; + break; + case 6: + len = 4; + break; + default: + len = -1; + } + + if (len == -1) return NULL; + + bits = (char *) malloc(len); + + memcpy(bits, chunk->data, len); + + return bits; +} + +/* + * Fetch the length of the significant bits array from the sBIT chunk + * + * Returns: -1 on error + */ +int png_sBIT_len(png_chunk_t *chunk) { + PNG_VALIDATE(PNG_CHUNK_SIGBITS, -1); + + switch (png_attr_col_type(chunk->png)) { + case 0: + return 1; + case 2: + case 3: + return 3; + case 4: + return 2; + case 6: + return 4; + default: + return -1; + } +} + +/* + * Fetch the rendering intent from the sRGB chunk + * + * Returns: -1 on error + */ +char png_sRGB_get(png_chunk_t *chunk) { + PNG_VALIDATE(PNG_CHUNK_SRGB, -1); + + return *((char *)chunk->data); +} + +/* + * Extract the keyword from an iTXt chunk + * + * Returns: NULL on error + * keyword on success + */ +char *png_iTXt_keyword(png_chunk_t *chunk) { + PNG_VALIDATE(PNG_CHUNK_TEXT_INT, NULL); + + return strdup((char *) chunk->data); +} + +/* + * Extract the language tag from an iTXt chunk + * + * Returns: NULL on error + * Empty string if no language tag available; must be free'd + * string containing ISO 646 hyphen-separated words; must be free'd + */ +char *png_iTXt_lang(png_chunk_t *chunk) { + PNG_VALIDATE(PNG_CHUNK_TEXT_INT, NULL); + + return strdup(chunk->data + strlen(chunk->data) + 3); +} + +/* + * Extract the text value from an iTXt chunk + * + * Returns: NULL on error + * string containing text from an iTXt chunk on success; must be free'd + */ +char *png_iTXt_text(png_chunk_t *chunk) { + PNG_VALIDATE(PNG_CHUNK_TEXT_INT, NULL); + + int c = strlen(chunk->data) + 3; /* Keyword + comp. flag + comp. method */ + char *p = chunk->data + c; + char *buf; + + c += strlen(p) + 1; /* Language tag */ + p = chunk->data + c; + + c += strlen(p) + 1; /* Translated keyword */ + p = chunk->data + c; + + c = chunk->length - c; + + buf = (char *) malloc(sizeof(char) * (c + 1)); + + strncpy(buf, p, c); + buf[c] = 0; + + return buf; +} + +/* + * Extract the keyword from a tEXt chunk + * + * Returns: NULL on error + * keyword on success; must be free'd + */ +char *png_tEXt_keyword(png_chunk_t *chunk) { + PNG_VALIDATE(PNG_CHUNK_TEXT, NULL); + + return strdup(chunk->data); +} + +/* + * Extract the text from a tEXt chunk + * + * Returns: NULL on error + * keyword on success; must be free'd + */ +char *png_tEXt_text(png_chunk_t *chunk) { + PNG_VALIDATE(PNG_CHUNK_TEXT, NULL); + + int c = strlen(chunk->data) + 1; + char *text = (char *) malloc(chunk->length - c + 1); + + strncpy(text, chunk->data + c, chunk->length - c); + text[chunk->length - c] = 0; + + return text; +} + +/* + * Extract the keyword from a zTXt chunk + * + * Returns: NULL on error + * keyword on success; must be free'd + */ +char *png_zTXt_keyword(png_chunk_t *chunk) { + PNG_VALIDATE(PNG_CHUNK_TEXT, NULL); + + return strdup(chunk->data); +} + +/* + * Extract the compression method from a zTXt chunk + * + * Returns: -1 on error + * compression method (>= 0) + */ +char png_zTXt_method(png_chunk_t *chunk) { + PNG_VALIDATE(PNG_CHUNK_TEXT_COMPRESSED, -1); + + return *((char *) chunk->data + strlen(chunk->data) + 1); +} + +/* + * Extract the compressed text datastream + * + * Returns: NULL on error + * Pointer to block containing datastream; must be free'd + */ +void *png_zTXt_data(png_chunk_t *chunk) { + PNG_VALIDATE(PNG_CHUNK_TEXT_COMPRESSED, NULL); + + int len = strlen(chunk->data) + 2; + int n = chunk->length - len; + void *data = malloc(n); + + return memcpy(data, chunk->data + len, n); +} + +/* + * Extract the length of the compressed text datastream + * + * Returns: -1 on error + * Length of datastream + */ +int png_zTXt_length(png_chunk_t *chunk) { + PNG_VALIDATE(PNG_CHUNK_TEXT_COMPRESSED, -1); + + return chunk->length - (strlen(chunk->data) + 2); +} + +/* + * Fetch the background color information of the image + * + * Returns: NULL on error + * Array of values corresponding to appropriate background color channels; must be free'd + */ +void *png_bKGD_get(png_chunk_t *chunk) { + PNG_VALIDATE(PNG_CHUNK_BACKGROUND, NULL); + + int type = png_attr_col_type(chunk->png); + void *array = NULL; + + if (type == 0 || type == 4) { + array = malloc(sizeof(short)); + + short s = *((short *)chunk->data); + ((short *)array)[0] = ntohs(s); + } else if (type == 2 || type == 6) { + array = malloc(sizeof(short) * 3); + + short *s = (short *)chunk->data; + ((short *)array)[0] = ntohs(s[0]); + ((short *)array)[1] = ntohs(s[1]); + ((short *)array)[2] = ntohs(s[2]); + } else if (type == 3) { + array == malloc(sizeof(char)); + + ((char *)array)[0] = *((char *)chunk->data); + } + + return array; +} + +/* + * Fetch the length of the array returned by `png_bKGD_get()`; this is the length of the array in bytes, not elements + * + * Returns: -1 on error + */ +int png_bKGD_len(png_chunk_t *chunk) { + PNG_VALIDATE(PNG_CHUNK_BACKGROUND, -1); + + switch (png_attr_col_type(chunk->png)) { + case 0: + case 4: + return 2; + case 2: + case 6: + return 6; + case 3: + return 1; + default: + return -1; + } +} + +/* + * Fetch the histogram of the image + * + * Returns: NULL on error + * Array of values representing the frequencies of the colors contained in the PLTE chunk; must be free'd + */ +unsigned short *png_hIST_get(png_chunk_t *chunk) { + PNG_VALIDATE(PNG_CHUNK_HISTOGRAM, NULL); + + switch (png_attr_col_type(chunk->png)) { + case 2: + case 3: + case 6: + break; + default: + return NULL; + } + + unsigned short *array = (unsigned short *) malloc(chunk->length); + + memcpy(array, chunk->data, chunk->length); + + return array; +} + +/* + * Fetch the length of the histogram of the image + * + * Returns: -1 on error + */ +int png_hIST_len(png_chunk_t *chunk) { + PNG_VALIDATE(PNG_CHUNK_HISTOGRAM, -1); + + switch (png_attr_col_type(chunk->png)) { + case 2: + case 3: + case 6: + break; + default: + return -1; + } + + return chunk->length / sizeof(unsigned short); +} + +/* + * Extract the pixels per unit along the X axis + * + * Returns: -1 on error + * pixels per unit on success + */ +int png_pHYs_ppuX(png_chunk_t *chunk) { + PNG_VALIDATE(PNG_CHUNK_PHYS, -1); + + return ntohl(*((int *)chunk->data)); +} + +/* + * Extract the pixels per unit along the Y axis + * + * Returns: -1 on error + * pixels per unit on success + */ +int png_pHYs_ppuY(png_chunk_t *chunk) { + PNG_VALIDATE(PNG_CHUNK_PHYS, -1); + + return ntohl(*((int *)(chunk->data + 4))); +} + +/* + * Extract the unit for pixels per unit + * + * Returns: -1 on error + * 0 for unknown unit + * 1 for meter + */ +char png_pHYs_unit(png_chunk_t *chunk) { + PNG_VALIDATE(PNG_CHUNK_PHYS, -1); + + return *((char *) chunk->data + 9); +} + +/* + * Fetch the name of the suggested palette + * + * Returns: NULL on error + * Name of the palette; must be free'd + */ +char *png_sPLT_name(png_chunk_t *chunk) { + PNG_VALIDATE(PNG_CHUNK_SPALETTE, NULL); + + return strdup(chunk->data); +} + +/* + * Fetch the sample depth of the suggested palette + * + * Returns: -1 on error + */ +char png_sPLT_sample_depth(png_chunk_t *chunk) { + PNG_VALIDATE(PNG_CHUNK_SPALETTE, -1); + + int offset = strlen(chunk->data) + 1; + + return *((char *)chunk->data + offset); +} + +/* + * Fetch an array of entries in the suggested palette + * + * Returns: NULL on error + * Array of entries on success; must be free'd + */ +palette_entry_t *png_sPLT_entries(png_chunk_t *chunk) { + PNG_VALIDATE(PNG_CHUNK_SPALETTE, NULL); + + int offset = strlen(chunk->data) + 2, i; + int size = chunk->length - offset, entry_size = 10; + int num_entries; + void *data = chunk->data + offset; + + if (size % 6 == 0) { + num_entries = size / 6; + size = 6; + } else if (size % 10 == 0) { + num_entries = size / 10; + size = 10; + } else { + return NULL; + } + + palette_entry_t *entries = (palette_entry_t *) malloc(sizeof(palette_entry_t) * num_entries); + + if (size == 6) { + for (i = 0; i < num_entries; i++) { + entries[i].red = *((char *)(data + (i * size + 0))); + entries[i].green = *((char *)(data + (i * size + 1))); + entries[i].blue = *((char *)(data + (i * size + 2))); + entries[i].alpha = *((char *)(data + (i * size + 3))); + entries[i].frequency = ntohs(*((short *)(data + (i * size + 4)))); + } + } else { + for (i = 0; i < num_entries; i++) { + entries[i].red = ntohs(*((short *)(data + (i * size + 0)))); + entries[i].green = ntohs(*((short *)(data + (i * size + 2)))); + entries[i].blue = ntohs(*((short *)(data + (i * size + 4)))); + entries[i].alpha = ntohs(*((short *)(data + (i * size + 6)))); + entries[i].frequency = ntohs(*((short *)(data + (i * size + 8)))); + } + } + + return entries; +} + +/* + * Fetch the number of entries in the suggested palette + * + * Returns: -1 on error + */ +int png_sPLT_entries_len(png_chunk_t *chunk) { + PNG_VALIDATE(PNG_CHUNK_SPALETTE, -1); + + int size = chunk->length - (strlen(chunk->data) + 2); + + if (size % 6 == 0) { + return size / 6; + } else if (size % 10 == 0) { + return size / 10; + } else { + return -1; + } +} + +/* + * Fetch the minimum amount of space (in bytes) required to store one entry in the suggested palette + * + * Returns: -1 on error + * 6 if the rgba channels can be stored in one byte + * 10 if the rgba channels can be stored in two bytes + */ +int png_sPLT_entry_size(png_chunk_t *chunk) { + PNG_VALIDATE(PNG_CHUNK_SPALETTE, -1); + + int sample_depth = *((int *) (chunk->data + strlen(chunk->data) + 1)); + + if (sample_depth == 8) { + return 6; + } else if (sample_depth == 16) { + return 10; + } else { + return -1; + } +} + +/* + * Extract the year from the last modified date + * + * Returns: -1 on error + * Complete year (eg. 1995, not 95) on success + */ +short png_tIME_year(png_chunk_t *chunk) { + PNG_VALIDATE(PNG_CHUNK_TIME, -1); + + return ntohs(*((short *) (chunk->data))); +} + +/* + * Extract the month from the last modified date + * + * Returns: -1 on error + * Month (1-12) on success + */ +char png_tIME_month(png_chunk_t *chunk) { + PNG_VALIDATE(PNG_CHUNK_TIME, -1); + + return *((char *) (chunk->data + 2)); +} + +/* + * Extract the day from the last modified date + * + * Returns: -1 on error + * Day (1-31) on success + */ +char png_tIME_day(png_chunk_t *chunk) { + PNG_VALIDATE(PNG_CHUNK_TIME, -1); + + return *((char *) (chunk->data + 3)); +} + +/* + * Extract the hour from the last modified date + * + * Returns: -1 on error + * Hour (0-23) on success + */ +char png_tIME_hour(png_chunk_t *chunk) { + PNG_VALIDATE(PNG_CHUNK_TIME, -1); + + return *((char *) (chunk->data + 4)); +} + +/* + * Extract the minute from the last modified date + * + * Returns: -1 on error + * Minute (0-59) on success + */ +char png_tIME_minute(png_chunk_t *chunk) { + PNG_VALIDATE(PNG_CHUNK_TIME, -1); + + return *((char *) (chunk->data + 5)); +} + +/* + * Extract the second from the last modified date + * + * Returns: -1 on error + * Second (0-60 to allow for leap seconds) on success + */ +char png_tIME_second(png_chunk_t *chunk) { + PNG_VALIDATE(PNG_CHUNK_TIME, -1); + + return *((char *) (chunk->data + 6)); +} + +/* + * Extract the last modified date in ISO 8601 format + * + * Returns: NULL on error + * Last modified date in ISO 8601 format: YYYY-MM-DDTHH:MM:SS+00:00 + */ +char *png_tIME_iso8601(png_chunk_t *chunk) { + PNG_VALIDATE(PNG_CHUNK_TIME, NULL); + + char *time = (char *) malloc(sizeof(char) * 26); // Prefer not to hardcode the length in + + sprintf(time, "%04hd-%02hhd-%02hhdT%02hhd:%02hhd:%02hhd+00:00", + ntohs(*((short *) (chunk->data))), + *((char *) (chunk->data + 2)), + *((char *) (chunk->data + 3)), + *((char *) (chunk->data + 4)), + *((char *) (chunk->data + 5)), + *((char *) (chunk->data + 6))); +} + /* * Fetch the width of the given image + * + * Returns: -1 on error + * width of image (in pixels) on success */ -int png_attr_w(Format_PNG png) { +int png_attr_width(Format_PNG png) { + if (png == NULL) return -1; + + if (png->IHDR == NULL || png->IHDR->data == NULL) return -1; + + if (png->IHDR->length < 4) return -1; + return ntohl(*((int *) png->IHDR->data)); } /* * Fetch the height of the given image + * + * Returns: -1 on error + * height of image (in pixels) on success */ -int png_attr_h(Format_PNG png) { +int png_attr_height(Format_PNG png) { + if (png == NULL) return -1; + + if (png->IHDR == NULL || png->IHDR->data == NULL) return -1; + + if (png->IHDR->length < 8) return -1; + return ntohl(*((int *) (png->IHDR->data + 4))); } -// TODO Remove when no longer needed -void png_debug(Format_PNG png) { - printf("%-25s | %u x %u pixels\n", png->name, png_attr_w(png), png_attr_h(png)); +/* + * Fetch the bit depth of the given image + * + * Returns: -1 on error + * bit depth on success + */ +char png_attr_bit_depth(Format_PNG png) { + if (png == NULL) return -1; + + if (png->IHDR == NULL || png->IHDR->data == NULL) return -1; + + if (png->IHDR->length < 9) return -1; + + return *((char *) png->IHDR->data + 8); } -/* - * Extract all metadata from a PNG image +/* + * Fetch the color type of the given image * - * Returns: > 0 - total number of metadata items extracted - * 0 - metadata already extracted - * < 0 - an error occurred + * Returns: -1 on error + * color type on success */ -int png_extract_meta_all(Format_PNG png) { +char png_attr_col_type(Format_PNG png) { + if (png == NULL) return -1; + + if (png->IHDR == NULL || png->IHDR->data == NULL) return -1; + if (png->IHDR->length < 10) return -1; + + return *((char *) png->IHDR->data + 9); } -int png_extract_meta(Format_PNG png, char *key, IVValue val) { +/* + * Fetch the compression method of the given image + * + * Returns: -1 on error + * compression method on success + */ +char png_attr_comp_method(Format_PNG png) { + if (png == NULL) return -1; + + if (png->IHDR == NULL || png->IHDR->data == NULL) return -1; + + if (png->IHDR->length < 11) return -1; + + return *((char *) png->IHDR->data + 10); +} + +/* + * Fetch the filter method of the given image + * + * Returns: -1 on error + * filter method on success + */ +char png_attr_filter_method(Format_PNG png) { + if (png == NULL) return -1; + + if (png->IHDR == NULL || png->IHDR->data == NULL) return -1; + + if (png->IHDR->length < 12) return -1; + + return *((char *) png->IHDR->data + 11); +} + +/* + * Fetch the interlace method of the given image + * + * Returns: -1 on error + * 0 on success (no interlace) + * 1 on success (Adam7 interlace) + */ +char png_attr_il_method(Format_PNG png) { + if (png == NULL) return -1; + + if (png->IHDR == NULL || png->IHDR->data == NULL) return -1; + if (png->IHDR->length < 13) return -1; + + return *((char *) png->IHDR->data + 12); } \ No newline at end of file diff --git a/formats/png.h b/formats/png.h index 90b076d..7c8b614 100644 --- a/formats/png.h +++ b/formats/png.h @@ -1,16 +1,49 @@ #ifndef __IV_FORMAT_PNG #define __IV_FORMAT_PNG #include -#include "../utils/types.h" -#define SIG_PNG 0x0A1A0A0D474E5089 +#define PNG_SIG 0x0A1A0A0D474E5089 #define PNG_SIG_SZ 8 #define PNG_CHNK_LEN 4 #define PNG_CHNK_TYPE_SZ 4 #define PNG_CHNK_IHDR 0x52444849 +#define PNG_RATIO 100000.0F + +#define PNG_CHUNK_HEADER "IHDR" +#define PNG_CHUNK_PALETTE "PLTE" +#define PNG_CHUNK_DATA "IDAT" +#define PNG_CHUNK_END "IEND" +#define PNG_CHUNK_TRANSPARENCY "tRNS" +#define PNG_CHUNK_CHROMACITY "cHRM" +#define PNG_CHUNK_GAMMA "gAMA" +#define PNG_CHUNK_ICCP "iCCP" +#define PNG_CHUNK_SIGBITS "sBIT" +#define PNG_CHUNK_SRGB "sRGB" +#define PNG_CHUNK_TEXT_INT "iTXt" +#define PNG_CHUNK_TEXT "tEXt" +#define PNG_CHUNK_TEXT_COMPRESSED "zTXt" +#define PNG_CHUNK_BACKGROUND "bKGD" +#define PNG_CHUNK_HISTOGRAM "hIST" +#define PNG_CHUNK_PHYS "pHYs" +#define PNG_CHUNK_SPALETTE "sPLT" +#define PNG_CHUNK_TIME "tIME" #define PNG_ISCRITICAL(chunk) (chunk->type[0] & 0x20 != 0) #define PNG_ISANCILLARY(chunk) (chunk->type[0] & 0x20 == 0) +#define PNG_VALIDATE(chunkType, defaultReturn) if (strncmp(chunk->type, chunkType, PNG_CHNK_LEN) != 0) return defaultReturn;\ + if (chunk->length == 0 || chunk->data == NULL) return defaultReturn; + +typedef unsigned char byte; +typedef unsigned short ushort; +typedef unsigned int uint; + +typedef struct palette_entry { + ushort red; + ushort green; + ushort blue; + ushort alpha; + ushort frequency; +} palette_entry_t; typedef struct png_chunk { unsigned int length; @@ -18,7 +51,7 @@ typedef struct png_chunk { void *data; unsigned int CRC; - struct png_chunk *next; + struct format_png_t *png; } png_chunk_t; typedef struct format_png_t { @@ -27,6 +60,7 @@ typedef struct format_png_t { png_chunk_t *IHDR; FILE *fin; + unsigned long long bytes; } *Format_PNG; /* Function signatures */ @@ -38,13 +72,87 @@ void png_close(Format_PNG); /* PNG chunks */ png_chunk_t *png_chunk_next(Format_PNG); +void png_chunk_free(png_chunk_t *); -/* Image attributes */ -int png_attr_w(Format_PNG); -int png_attr_h(Format_PNG); +char **png_PLTE_get(png_chunk_t *); +int png_PLTE_length(png_chunk_t *); +void png_PLTE_free(char **, int); + +void *png_IDAT_get(png_chunk_t *); +int png_IDAT_length(png_chunk_t *); + +int png_IEND_valid(png_chunk_t *); + +short png_tRNS_gray(png_chunk_t *); +short *png_tRNS_true(png_chunk_t *); +char *png_tRNS_index(png_chunk_t *); +int png_tRNS_index_length(png_chunk_t *); + +float png_cHRM_whiteX(png_chunk_t *); +float png_cHRM_whiteY(png_chunk_t *); +float png_cHRM_redX(png_chunk_t *); +float png_cHRM_redY(png_chunk_t *); +float png_cHRM_greenX(png_chunk_t *); +float png_cHRM_greenY(png_chunk_t *); +float png_cHRM_blueX(png_chunk_t *); +float png_cHRM_blueY(png_chunk_t *); + +float png_gAMA(png_chunk_t *); +int png_gAMA_raw(png_chunk_t *); + +char *png_iCCP_name(png_chunk_t *); +char png_iCCP_method(png_chunk_t *); +void *png_iCCP_profile(png_chunk_t *); +int png_iCCP_profile_len(png_chunk_t *); -void png_debug(Format_PNG); +char *png_sBIT_get(png_chunk_t *); +int png_sBIT_len(png_chunk_t *); -int png_extract_meta(Format_PNG, char *, IVValue); +char png_sRGB_get(png_chunk_t *); + +char *png_iTXt_keyword(png_chunk_t *); +char *png_iTXt_lang(png_chunk_t *); +char *png_iTXt_text(png_chunk_t *); + +char *png_tEXt_keyword(png_chunk_t *); +char *png_tEXt_text(png_chunk_t *); + +char *png_zTXt_keyword(png_chunk_t *); +char png_zTXt_method(png_chunk_t *); +void *png_zTXt_data(png_chunk_t *); +int png_zTXt_length(png_chunk_t *); + +void *png_bKGD_get(png_chunk_t *); +int png_bKGD_len(png_chunk_t *); + +unsigned short *png_hIST_get(png_chunk_t *); +int png_hIST_len(png_chunk_t *); + +int png_pHYs_ppuX(png_chunk_t *); +int png_pHYs_ppuY(png_chunk_t *); +char png_pHYs_unit(png_chunk_t *); + +char *png_sPLT_name(png_chunk_t *); +char png_sPLT_sample_depth(png_chunk_t *); +palette_entry_t *png_sPLT_entries(png_chunk_t *); +int png_sPLT_entries_len(png_chunk_t *); +int png_sPLT_entry_size(png_chunk_t *); + +short png_tIME_year(png_chunk_t *); +char png_tIME_month(png_chunk_t *); +char png_tIME_day(png_chunk_t *); +char png_tIME_hour(png_chunk_t *); +char png_tIME_minute(png_chunk_t *); +char png_tIME_second(png_chunk_t *); +char *png_tIME_iso8601(png_chunk_t *); + +/* Image attributes */ +int png_attr_width(Format_PNG); +int png_attr_height(Format_PNG); +char png_attr_bit_depth(Format_PNG); +char png_attr_col_type(Format_PNG); +char png_attr_comp_method(Format_PNG); +char png_attr_filter_method(Format_PNG); +char png_attr_il_method(Format_PNG); #endif \ No newline at end of file diff --git a/main.c b/main.c index e15c053..ba5c0f7 100644 --- a/main.c +++ b/main.c @@ -3,7 +3,10 @@ #include #include #include +#include +#include +#include "utils/iv_opts.h" #include "utils/types.h" #include "utils/version.h" #include "formats/png.h" @@ -13,7 +16,9 @@ void usage(int exit_code) { "Usage: %s [options...] [command] [file2...]\n\n" "Options:\n" "-h, --help [command] Get help on %s (or [command])\n" - "-v, --version Print version information\n", + "-v, --version Print version information\n" + "-s, --scan View the chunks of each image\n" + "-d, --scan-deep View details of the chunks of each image\n", USAGE_PROGRAM_NAME, USAGE_PROGRAM_NAME); exit(exit_code); } @@ -36,24 +41,174 @@ void help() { } void handle_png(Format_PNG png) { - int rv; + png_chunk_t *chunk; + + while (chunk = png_chunk_next(png)) { + printf(" Chunk: "); + if (chunk == ((png_chunk_t *) -1)) { + printf("error\n"); + break; + } + printf("%.4s (%d)\n", chunk->type, chunk->length); + if (iv_opts.deep) { + if (strncmp(chunk->type, PNG_CHUNK_TEXT_INT, 4) == 0) { + char *text = png_iTXt_keyword(chunk); + printf(" %s", text); free(text); + text = png_iTXt_text(chunk); + printf(": %s", text); free(text); + + text = png_iTXt_lang(chunk); + if (text && *text) { + printf(" (lang: %s)", text); + } + printf("\n"); + free(text); + } else if (strncmp(chunk->type, PNG_CHUNK_TEXT, PNG_CHNK_LEN) == 0) { + char *text = png_tEXt_keyword(chunk); + printf(" %s", text); free(text); + text = png_tEXt_text(chunk); + printf(": %s\n", text); free(text); + } else if (strncmp(chunk->type, PNG_CHUNK_TIME, PNG_CHNK_LEN) == 0) { + printf(" Timestamp: %d-%02d-%02dT%02d:%02d:%02d\n", + png_tIME_year(chunk), + png_tIME_month(chunk), + png_tIME_day(chunk), + png_tIME_hour(chunk), + png_tIME_minute(chunk), + png_tIME_second(chunk)); + } else if (strncmp(chunk->type, PNG_CHUNK_PALETTE, PNG_CHNK_LEN) == 0) { + int len = png_PLTE_length(chunk), i; + char **palette = png_PLTE_get(chunk); + printf(" Entries: %d\n", len); + for (i = 0; i < 10 && i < len; i++) { + printf(" #%d: 0x%02hhx%02hhx%02hhx\n", i, palette[i][0], palette[i][1], palette[i][2]); + } + if (i != len) printf(" ...\n"); + png_PLTE_free(palette, len); + } else if (strncmp(chunk->type, PNG_CHUNK_TRANSPARENCY, PNG_CHNK_LEN) == 0) { + int type = png_attr_col_type(png); + + if (type == 0) { + printf(" Grayscale sample: %hx\n", png_tRNS_gray(chunk)); + } else if (type == 2) { + short *rgb = png_tRNS_true(chunk); + printf(" Truecolor sample:\n R = %hx\n G = %hx\n B = %hx\n", rgb[0], rgb[1], rgb[2]); + free(rgb); + } else if (type == 3) { + int len = png_tRNS_index_length(chunk), i; + char *palette = png_tRNS_index(chunk); + printf(" Entries: %d\n", len); + for (i = 0; i < 10 && i < len; i++) { + printf(" #%d: %hhu\n", i, palette[i]); + } + if (i != len) printf(" ...\n"); + free(palette); + } else { + printf(" [Error]: Invalid color type %d\n", type); + } + } else if (strncmp(chunk->type, PNG_CHUNK_CHROMACITY, PNG_CHNK_LEN) == 0) { + printf(" Chromacities:\n"); + printf(" White point (x): %.5f\n" + " White point (y): %.5f\n" + " Red (x): %.5f\n" + " Red (y): %.5f\n" + " Green (x): %.5f\n" + " Green (y): %.5f\n" + " Blue (x): %.5f\n" + " Blue (y): %.5f\n", + png_cHRM_whiteX(chunk), + png_cHRM_whiteX(chunk), + png_cHRM_redX(chunk), + png_cHRM_redY(chunk), + png_cHRM_greenX(chunk), + png_cHRM_greenY(chunk), + png_cHRM_blueX(chunk), + png_cHRM_blueY(chunk)); + } else if (strncmp(chunk->type, PNG_CHUNK_GAMMA, PNG_CHNK_LEN) == 0) { + printf(" Gamma: %.5f (0x%08x)\n", png_gAMA(chunk), png_gAMA_raw(chunk)); + } else if (strncmp(chunk->type, PNG_CHUNK_ICCP, PNG_CHNK_LEN) == 0) { + char *profile = png_iCCP_name(chunk); + + printf(" Profile: '%s' (%d bytes, %s compression)\n", profile, png_iCCP_profile_len(chunk), png_iCCP_method(chunk) ? "unknown" : "zlib"); + + free(profile); + } else if (strncmp(chunk->type, PNG_CHUNK_SIGBITS, PNG_CHNK_LEN) == 0) { + int type = png_attr_col_type(png); + char *bits = png_sBIT_get(chunk); + + if (type == 0 || type == 4) { + printf(" Grayscale: 0x%hhx\n", bits[0]); + } else if (type == 2 || type == 3 || type == 6) { + printf(" Red: 0x%hhx\n" + " Green: 0x%hhx\n" + " Blue: 0x%hhx\n", + bits[0], bits[1], bits[2]); + } + if (type == 4 || type == 6) { + printf(" Alpha: 0x%hhx\n", bits[type == 4 ? 2 : 3]); + } + + free(bits); + } else if (strncmp(chunk->type, PNG_CHUNK_SRGB, PNG_CHNK_LEN) == 0) { + char intent = png_sRGB_get(chunk); + char *s = "error"; + + switch (intent) { + case 0: + s = "perceptual"; + break; + case 1: + s = "relative colorimetric"; + break; + case 2: + s = "saturation"; + break; + case 3: + s = "absolute colorimetric"; + break; + } - png_debug(png); + printf(" Rendering intent: %s\n", s); + + } else if (strncmp(chunk->type, PNG_CHUNK_BACKGROUND, PNG_CHNK_LEN) == 0) { + int size = png_bKGD_len(chunk); + void *array = png_bKGD_get(chunk); + short *s = (short *) array; + + if (size == 2) { + printf(" Grayscale: 0x%hx\n", s[0]); + } else if (size == 6) { + printf(" HTML Code: 0x%hx%hx%hx\n", s[0], s[1], s[2]); + } else if (size == 1) { + printf(" Palette index: %hhd\n", *((char *)array)); + } + + free(array); + } + } + + png_chunk_free(chunk); + } } int main(int argc, char **argv) { - int i, rv; + int i, rv, n, len = 0; const struct option options[] = { { "version", no_argument, NULL, 'v' }, { "help", optional_argument, NULL, 'h' }, { "verbose", optional_argument, NULL, 'V' }, + { "scan", no_argument, NULL, 's' }, + { "scan-deep", no_argument, NULL, 'd' }, {NULL, 0, NULL, 0}, /* Last element must be all 0s */ }; + /* Default options */ + iv_opts.verbose = 0; + int option_index; int c; while (1) { - c = getopt_long(argc, argv, "vh::", options, &option_index); + c = getopt_long(argc, argv, "vh::V::sd", options, &option_index); if (c == -1) break; @@ -67,7 +222,17 @@ int main(int argc, char **argv) { break; case 'V': - + iv_opts.verbose = 1; + printf(" ===== %s [%s] =====\n", IV_PROGRAM_NAME, IV_VERSION); + break; + + case 's': + iv_opts.scan = 1; + break; + + case 'd': + iv_opts.scan = 1; + iv_opts.deep = 1; break; case '?': @@ -81,36 +246,60 @@ int main(int argc, char **argv) { if (optind == argc) { // TODO Read from stdin - int c = fgetc(stdin); - if (feof(stdin)) { - usage(EXIT_FAILURE); + struct pollfd ps = { 0, POLLIN, 0 }; + + int rv = poll(&ps, 1, 0); + if (rv == 0) { + if (iv_opts.verbose) { + exit(1); + } else { + usage(EXIT_FAILURE); + } } else { - fputc(c, stdin); Format_PNG png = png_new(); - png->fin = stdin; - handle_png(png); + rv = png_open(png, NULL); + if (rv != 0) { + png_close(png); + exit(EXIT_FAILURE); + } + if (iv_opts.scan) { + handle_png(png); + } + png_close(png); } } // TODO Read files + for (i = optind; i < argc; i++) { + n = strlen(argv[i]); + if (n > len) len = n; + } + + n = argc - optind; for (i = optind; i < argc; i++) { Format_PNG png = png_new(); + png_chunk_t *chunk; if (access(argv[i], R_OK) == -1) { // TODO Error message for bad file - printf("File does not exist: %s\n", argv[i]); + printf("Could not open file %s\n", argv[i]); continue; } - printf("Opening %s... ", argv[i]); + if (n != 1) { + printf("%s %.*s\n", argv[i], (int) (len - strlen(argv[i]) + 7), "--------------------------------------"); + } + rv = png_open(png, argv[i]); if (rv != 0) { - printf("error (%d)\n", rv); png_close(png); continue; } - printf("success!\n"); - handle_png(png); + if (iv_opts.scan) { + handle_png(png); + } png_close(png); } + + exit(EXIT_SUCCESS); } \ No newline at end of file diff --git a/makefile b/makefile index e8b63f8..75a8758 100644 --- a/makefile +++ b/makefile @@ -1,5 +1,5 @@ CC=gcc -CFLAGS=-g +CFLAGS= DEPS = formats/png.h OBJ = main.o formats/png.o diff --git a/meta.h b/meta.h new file mode 100644 index 0000000..e053c69 --- /dev/null +++ b/meta.h @@ -0,0 +1,9 @@ +#ifndef __IV_META_H +#define __IV_META_H +#include "utils/types.h" + +typedef struct meta { + char key[80]; + IVValue val; +} meta_t; +#endif \ No newline at end of file diff --git a/utils/iv_opts.h b/utils/iv_opts.h new file mode 100644 index 0000000..ebe0d45 --- /dev/null +++ b/utils/iv_opts.h @@ -0,0 +1,5 @@ +struct global_opts { + int verbose; + int scan; + char deep; +} iv_opts; \ No newline at end of file diff --git a/utils/logging.c b/utils/logging.c new file mode 100644 index 0000000..e69de29 diff --git a/utils/logging.h b/utils/logging.h new file mode 100644 index 0000000..e69de29 diff --git a/utils/types.c b/utils/types.c new file mode 100644 index 0000000..bf49720 --- /dev/null +++ b/utils/types.c @@ -0,0 +1,175 @@ +#include +#include "types.h" + +IVValue new_iv() { + return (IVValue) malloc(sizeof(union ivvalue_t)); +} + +IVValue new_iv_str(char *str) { + IVValue iv = new_iv(); + iv->str = str; + return iv; +} + +IVValue new_iv_v(void *v) { + IVValue iv = new_iv(); + iv->v = v; + return iv; +} + +IVValue new_iv_c(char c) { + IVValue iv = new_iv(); + iv->c = c; + return iv; +} + +IVValue new_iv_ca(char ca[8]) { + IVValue iv = new_iv(); + int i; + for (i = 0; i < 8; i++) + iv->ca[i] = ca[i]; + return iv; +} + +IVValue new_iv_uc(unsigned char uc) { + IVValue iv = new_iv(); + iv->uc = uc; + return iv; +} + +IVValue new_iv_uca(unsigned char uca[8]) { + IVValue iv = new_iv(); + int i; + for (i = 0; i < 8; i++) + iv->uca[i] = uca[i]; + return iv; +} + +IVValue new_iv_s(short s) { + IVValue iv = new_iv(); + iv->s = s; + return iv; +} + +IVValue new_iv_sa(short sa[4]) { + IVValue iv = new_iv(); + int i; + for (i = 0; i < 4; i++) + iv->sa[i] = sa[i]; + return iv; +} + +IVValue new_iv_us(unsigned short us) { + IVValue iv = new_iv(); + iv->us = us; + return iv; +} + +IVValue new_iv_usa(unsigned short usa[4]) { + IVValue iv = new_iv(); + int i; + for (i = 0; i < 4; i++) + iv->usa[i] = usa[i]; + return iv; +} + +IVValue new_iv_i(int i) { + IVValue iv = new_iv(); + iv->i = i; + return iv; +} + +IVValue new_iv_ia(int ia[2]) { + IVValue iv = new_iv(); + iv->ia[0] = ia[0]; + iv->ia[1] = ia[1]; + return iv; +} + +IVValue new_iv_ui(unsigned int ui) { + IVValue iv = new_iv(); + iv->ui = ui; + return iv; +} + +IVValue new_iv_uia(unsigned int uia[2]) { + IVValue iv = new_iv(); + iv->uia[0] = uia[0]; + iv->uia[1] = uia[1]; + return iv; +} + +IVValue new_iv_l(long long l) { + IVValue iv = new_iv(); + iv->l = l; + return iv; +} + +IVValue new_iv_la(long long *la) { + IVValue iv = new_iv(); + iv->la = la; + return iv; +} + +IVValue new_iv_ul(unsigned long long ul) { + IVValue iv = new_iv(); + iv->ul = ul; + return iv; +} + +IVValue new_iv_ula(unsigned long long *ula) { + IVValue iv = new_iv(); + iv->ula = ula; + return iv; +} + +IVValue new_iv_f(float f) { + IVValue iv = new_iv(); + iv->f = f; + return iv; +} + +IVValue new_iv_fa(float fa[2]) { + IVValue iv = new_iv(); + iv->fa[0] = fa[0]; + iv->fa[1] = fa[1]; + return iv; +} + +IVValue new_iv_d(double d) { + IVValue iv = new_iv(); + iv->d = d; + return iv; +} + +IVValue new_iv_da(double *da) { + IVValue iv = new_iv(); + iv->da = da; + return iv; +} + +char *iv_str(IVValue iv) { + return iv->str; +} + +void *iv_v(IVValue iv) { return iv->v; } +char iv_c(IVValue iv) { return iv->c; } +char *iv_ca(IVValue iv) { return iv->ca; } +unsigned char iv_uc(IVValue iv) { return iv->uc; } +unsigned char *iv_uca(IVValue iv) { return iv->uca; } +short iv_s(IVValue iv) { return iv->s; } +short *iv_sa(IVValue iv) { return iv->sa; } +unsigned short iv_us(IVValue iv) { return iv->us; } +unsigned short *iv_usa(IVValue iv) { return iv->usa; } +int iv_i(IVValue iv) { return iv->i; } +int *iv_ia(IVValue iv) { return iv->ia; } +unsigned int iv_ui(IVValue iv) { return iv->ui; } +unsigned int *iv_uia(IVValue iv) { return iv->uia; } +long long iv_l(IVValue iv) { return iv->l; } +long long *iv_la(IVValue iv) { return iv->la; } +unsigned long long iv_ul(IVValue iv) { return iv->ul; } +unsigned long long *iv_ula(IVValue iv) { return iv->ula; } +float iv_f(IVValue iv) { return iv->f; } +float *iv_fa(IVValue iv) { return iv->fa; } +double iv_d(IVValue iv) { return iv->d; } +double *iv_da(IVValue iv) { return iv->da; } \ No newline at end of file diff --git a/utils/types.h b/utils/types.h new file mode 100644 index 0000000..f256808 --- /dev/null +++ b/utils/types.h @@ -0,0 +1,90 @@ +#ifndef __TYPES_IV +#define __TYPES_IV + +#define VT_STRING 1 +#define VT_CHAR 2 +#define VT_SHORT 3 +#define VT_INT 4 +#define VT_FLOAT 5 +#define VT_LONG 6 +#define VT_DOUBLE 7 +#define VT_VOID 8 + +typedef union ivvalue_t { + char *str; + void *v; + + char c; + char ca[8]; + unsigned char uc; + unsigned char uca[8]; + + short s; + short sa[4]; + unsigned short us; + unsigned short usa[4]; + + int i; + int ia[2]; + unsigned int ui; + unsigned int uia[2]; + + long long l; + long long *la; + unsigned long long ul; + unsigned long long *ula; + + float f; + float fa[2]; + + double d; + double *da; +} *IVValue; + +IVValue new_iv_str(char *); +IVValue new_iv_v(void *); +IVValue new_iv_c(char); +IVValue new_iv_ca(char [8]); +IVValue new_iv_uc(unsigned char); +IVValue new_iv_uca(unsigned char [8]); +IVValue new_iv_s(short); +IVValue new_iv_sa(short [4]); +IVValue new_iv_us(unsigned short); +IVValue new_iv_usa(unsigned short [4]); +IVValue new_iv_i(int); +IVValue new_iv_ia(int [2]); +IVValue new_iv_ui(unsigned int); +IVValue new_iv_uia(unsigned int [2]); +IVValue new_iv_l(long long); +IVValue new_iv_la(long long *); +IVValue new_iv_ul(unsigned long long); +IVValue new_iv_ula(unsigned long long *); +IVValue new_iv_f(float); +IVValue new_iv_fa(float [2]); +IVValue new_iv_d(double); +IVValue new_iv_da(double *); + +char *iv_str(IVValue); +void *iv_v(IVValue); +char iv_c(IVValue); +char *iv_ca(IVValue); +unsigned char iv_uc(IVValue); +unsigned char *iv_uca(IVValue); +short iv_s(IVValue); +short *iv_sa(IVValue); +unsigned short iv_us(IVValue); +unsigned short *iv_usa(IVValue); +int iv_i(IVValue); +int *iv_ia(IVValue); +unsigned int iv_ui(IVValue); +unsigned int *iv_uia(IVValue); +long long iv_l(IVValue); +long long *iv_la(IVValue); +unsigned long long iv_ul(IVValue); +unsigned long long *iv_ula(IVValue); +float iv_f(IVValue); +float *iv_fa(IVValue); +double iv_d(IVValue); +double *iv_da(IVValue); + +#endif \ No newline at end of file diff --git a/utils/version.h b/utils/version.h index caa700e..345c5d2 100644 --- a/utils/version.h +++ b/utils/version.h @@ -10,7 +10,7 @@ #endif /* Version */ -#define IV_VERSION "v0.0.0" -#define IV_UPDATED "2020-05-05" +#define IV_VERSION "v0.1.7" +#define IV_UPDATED "2020-06-03" #endif \ No newline at end of file