Table of Contents
examples/ex_tiff.c
Functions
| Name | |
|---|---|
| void | test_sub_ifd(int be) Run the checks, or list a file given as the argument. |
| void | test_order(int be) Parse a built buffer and check every value. |
| void | test_broken(void ) Broken buffers are refused, never read out of bounds. |
| void | put_entry(unsigned char * e, uint16_t tag, uint16_t type, uint32_t count, uint32_t value, int be) Write one directory entry. |
| void | put32(unsigned char * p, uint32_t v, int be) Write a 32 bit value in a byte order. |
| void | put16(unsigned char * p, uint16_t v, int be) Write a 16 bit value in a byte order. |
| int | main(int argc, char ** argv) |
| int | list_file(const char * path) List the tags of a file's first directory. |
| void | check(const char * what, int ok) Count and print a failed condition. |
| size_t | build(unsigned char * buf, int be) Build a DCP shaped buffer: a name, a short, three signed rationals, two floats, and one entry pointing past the end. |
Attributes
| Name | |
|---|---|
| int | failures failures counted by check() |
Functions Documentation
function test_sub_ifd
static void test_sub_ifd(
int be
)
Run the checks, or list a file given as the argument.
Parameters:
- argc argument count
- argv arguments
- be non-zero for big endian
Return: 0 on success
-V LEVEL sets the log level, as for every example: run_tests.sh passes it, and before this it was taken for the name of a file to list.
A directory pointing to an EXIF sub-directory, read through n_tiff_parse_ifd(): the shape of a camera file's SubSecTimeOriginal.
function test_order
static void test_order(
int be
)
Parse a built buffer and check every value.
Parameters:
- be non-zero for big endian
function test_broken
static void test_broken(
void
)
Broken buffers are refused, never read out of bounds.
function put_entry
static void put_entry(
unsigned char * e,
uint16_t tag,
uint16_t type,
uint32_t count,
uint32_t value,
int be
)
Write one directory entry.
Parameters:
- e where
- tag tag
- type field type
- count value count
- value the value itself, or the offset of the values
- be non-zero for big endian
function put32
static void put32(
unsigned char * p,
uint32_t v,
int be
)
Write a 32 bit value in a byte order.
Parameters:
- p where
- v the value
- be non-zero for big endian
function put16
static void put16(
unsigned char * p,
uint16_t v,
int be
)
Write a 16 bit value in a byte order.
Parameters:
- p where
- v the value
- be non-zero for big endian
function main
int main(
int argc,
char ** argv
)
function list_file
static int list_file(
const char * path
)
List the tags of a file's first directory.
Parameters:
- path the file
Return: 0, or 1 when it cannot be read or parsed
function check
static void check(
const char * what,
int ok
)
Count and print a failed condition.
Parameters:
- what what was checked
- ok the condition
function build
static size_t build(
unsigned char * buf,
int be
)
Build a DCP shaped buffer: a name, a short, three signed rationals, two floats, and one entry pointing past the end.
Parameters:
- buf receives it, 256 bytes
- be non-zero for big endian
Return: its length
Attributes Documentation
variable failures
static int failures = 0;
failures counted by check()
Source code
/*
* Nilorea Library
* Copyright (C) 2005-2026 Castagnier Mickael
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
* implied. See the License for the specific language governing
* permissions and limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "nilorea/n_common.h"
#include "nilorea/n_files.h"
#include "nilorea/n_log.h"
#include "nilorea/n_tiff.h"
#include <math.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
static int failures = 0;
static void check(const char* what, int ok) {
if (!ok) {
failures++;
fprintf(stderr, "FAIL %s\n", what);
}
}
static void put16(unsigned char* p, uint16_t v, int be) {
p[be ? 0 : 1] = (unsigned char)(v >> 8);
p[be ? 1 : 0] = (unsigned char)(v & 0xff);
}
static void put32(unsigned char* p, uint32_t v, int be) {
int i = 0;
for (i = 0; i < 4; i++)
p[be ? i : 3 - i] = (unsigned char)(v >> (24 - 8 * i));
}
static void put_entry(unsigned char* e, uint16_t tag, uint16_t type, uint32_t count, uint32_t value, int be) {
put16(e, tag, be);
put16(e + 2, type, be);
put32(e + 4, count, be);
put32(e + 8, value, be);
}
static size_t build(unsigned char* buf, int be) {
const float floats[2] = {1.5f, -0.25f};
int i = 0;
memset(buf, 0, 256);
buf[0] = buf[1] = (unsigned char)(be ? 'M' : 'I');
put16(buf + 2, N_TIFF_MAGIC_DCP, be);
put32(buf + 4, 8, be);
put16(buf + 8, 5, be);
/* five entries of 12 bytes from 10, values from 80 */
put_entry(buf + 10, 50708, 2, 12, 80, be); /* ASCII, at 80 */
put_entry(buf + 22, 50778, 3, 1, 0, be); /* SHORT, in the entry */
put16(buf + 22 + 8, 21, be);
put_entry(buf + 34, 50721, 10, 3, 96, be); /* SRATIONAL x3, at 96 */
put_entry(buf + 46, 50938, 11, 2, 120, be); /* FLOAT x2, at 120 */
put_entry(buf + 58, 50939, 11, 64, 200, be); /* past the end */
memcpy(buf + 80, "Test Camera", 12);
put32(buf + 96, (uint32_t)8309, be);
put32(buf + 100, 10000, be);
put32(buf + 104, (uint32_t)-1786, be);
put32(buf + 108, 10000, be);
put32(buf + 112, 5, be);
put32(buf + 116, 0, be);
for (i = 0; i < 2; i++) {
uint32_t bits = 0;
memcpy(&bits, &floats[i], sizeof(bits));
put32(buf + 120 + 4 * i, bits, be);
}
return 256;
}
static void test_order(int be) {
unsigned char buf[256];
size_t len = build(buf, be);
N_TIFF* tiff = n_tiff_parse(buf, len);
double v[4] = {0, 0, 0, 0};
char text[64];
check("the buffer parses", tiff != NULL);
if (!tiff)
return;
check("the byte order is read", tiff->big_endian == be);
check("the magic is reported", tiff->magic == N_TIFF_MAGIC_DCP);
check("the entry past the end is skipped", tiff->count == 4 && !n_tiff_find(tiff, 50939));
check("ASCII reads", n_tiff_get_ascii(tiff, 50708, text, sizeof(text)) == 0 &&
strcmp(text, "Test Camera") == 0);
check("ASCII is cut to the room given",
n_tiff_get_ascii(tiff, 50708, text, 5) == 0 && strcmp(text, "Test") == 0);
check("a short in the entry reads", n_tiff_get_doubles(tiff, 50778, v, 4) == 1 && v[0] == 21.0);
check("signed rationals read", n_tiff_get_doubles(tiff, 50721, v, 4) == 3 &&
fabs(v[0] - 0.8309) < 1e-9 && fabs(v[1] + 0.1786) < 1e-9 &&
v[2] == 0.0);
check("floats read", n_tiff_get_doubles(tiff, 50938, v, 4) == 2 && v[0] == 1.5 && v[1] == -0.25);
check("a count is reported", n_tiff_count(tiff, 50721) == 3 && n_tiff_count(tiff, 1) == 0);
check("ASCII is not a number", n_tiff_get_doubles(tiff, 50708, v, 4) == 0);
check("a number is not ASCII", n_tiff_get_ascii(tiff, 50721, text, sizeof(text)) == -1 &&
text[0] == '\0');
n_tiff_free(&tiff);
check("free clears the pointer", tiff == NULL);
}
static void test_broken(void) {
unsigned char buf[256];
size_t len = build(buf, 0);
check("a short buffer is refused", n_tiff_parse(buf, 6) == NULL);
buf[0] = 'X';
check("a bad byte order is refused", n_tiff_parse(buf, len) == NULL);
build(buf, 0);
put32(buf + 4, 250, 0);
check("a directory past the end is refused", n_tiff_parse(buf, len) == NULL);
build(buf, 0);
put16(buf + 8, 60, 0);
check("a directory longer than the buffer is refused", n_tiff_parse(buf, len) == NULL);
}
static int list_file(const char* path) {
unsigned char* data = NULL;
size_t len = 0;
N_TIFF* tiff = NULL;
size_t i = 0;
if (n_file_read_bounded(path, 64 * 1024 * 1024, &data, &len) != TRUE || !data) {
fprintf(stderr, "cannot read %s\n", path);
return 1;
}
tiff = n_tiff_parse(data, len);
if (!tiff) {
fprintf(stderr, "%s is not TIFF structured\n", path);
free(data);
return 1;
}
printf("%s: %s endian, magic 0x%04x, %zu entries\n", path, tiff->big_endian ? "big" : "little",
tiff->magic, tiff->count);
for (i = 0; i < tiff->count; i++)
printf(" tag %5u type %2u count %u\n", tiff->entries[i].tag, tiff->entries[i].type,
tiff->entries[i].count);
n_tiff_free(&tiff);
free(data);
return 0;
}
static void test_sub_ifd(int be) {
unsigned char buf[64];
N_TIFF* tiff = NULL;
N_TIFF* exif = NULL;
double offset = 0.0;
char text[8];
memset(buf, 0, sizeof(buf));
buf[0] = buf[1] = (unsigned char)(be ? 'M' : 'I');
put16(buf + 2, N_TIFF_MAGIC, be);
put32(buf + 4, 8, be);
/* IFD0: one entry, the EXIF pointer, a LONG holding 26. */
put16(buf + 8, 1, be);
put_entry(buf + 10, 0x8769, 4, 1, 26, be);
/* At 26: one entry, SubSecTimeOriginal, "45" and its terminator, which
* fit in the entry itself. */
put16(buf + 26, 1, be);
put_entry(buf + 28, 0x9291, 2, 3, 0, be);
memcpy(buf + 28 + 8, "45", 3);
tiff = n_tiff_parse(buf, sizeof(buf));
check("sub-directory: IFD0 parses", tiff != NULL);
check("sub-directory: the pointer reads", tiff && n_tiff_get_doubles(tiff, 0x8769, &offset, 1) == 1 &&
offset == 26.0);
check("sub-directory: its tag is not in IFD0", tiff && n_tiff_find(tiff, 0x9291) == NULL);
exif = n_tiff_parse_ifd(buf, sizeof(buf), (size_t)offset);
check("sub-directory: it parses at the offset", exif != NULL);
check("sub-directory: its ASCII reads", exif && n_tiff_get_ascii(exif, 0x9291, text, sizeof(text)) == 0 &&
strcmp(text, "45") == 0);
check("sub-directory: it takes the header's byte order", exif && exif->big_endian == be);
n_tiff_free(&exif);
n_tiff_free(&tiff);
check("sub-directory: an offset past the end is refused", n_tiff_parse_ifd(buf, sizeof(buf), 63) == NULL);
check("sub-directory: an offset inside the header is refused", n_tiff_parse_ifd(buf, sizeof(buf), 4) == NULL);
/* A count that runs past the end. */
put16(buf + 26, 9, be);
check("sub-directory: a directory longer than the buffer is refused",
n_tiff_parse_ifd(buf, sizeof(buf), 26) == NULL);
}
int main(int argc, char** argv) {
int opt = 0, log_level = LOG_ERR;
while ((opt = getopt(argc, argv, "V:")) != -1) {
if (opt != 'V') {
fprintf(stderr, "Usage: %s [-V LOG_LEVEL] [file]\n", argv[0]);
return 2;
}
log_level = atoi(optarg);
}
set_log_level(log_level > 0 ? log_level : LOG_ERR);
if (optind < argc)
return list_file(argv[optind]);
test_order(0);
test_order(1);
test_sub_ifd(0);
test_sub_ifd(1);
test_broken();
printf("%s: %d failure(s)\n", failures ? "FAILED" : "ok", failures);
return failures ? 1 : 0;
}
Modules
- AABB3D
- ACCEPT_POOL
- ALLEGRO4
- ALLEGRO5
- ANIM_LIB
- ASTAR
- ATLAS_LIB
- AVRO
- CLOCK_SYNC
- COMMONS
- CONFIGFILE
- CYPHER_BASE64
- CYPHER_VIGENERE
- DEAD_RECKONING
- ENUMS
- EXCEPTIONS
- GAME_ENV
- GIT
- GUI
- HASH_TABLE
- ISOMETRIC_ENGINE
- LIST
- LOG
- LOGNODUP
- LZ4
- NETWORKING
- NETWORK_MSG
- N_COOKIES
- N_DIFF
- N_DIGEST
- N_DISK_CACHE
- N_DNS
- N_ECC
- N_ENTROPY
- N_FILES
- N_FLUIDS
- N_GUI_COLOR
- N_GUI_COLORWHEEL
- N_GUI_CURVE
- N_GUI_GRID
- N_GUI_POPUP
- N_HEX
- N_HTTP3
- N_HTTP_COOKIE
- N_HTTP_FORM
- N_HTTP_MIME
- N_HTTP_REQ
- N_HTTP_RESP
- N_HTTP_ROUTER
- N_HTTP_SERVER
- N_ICC
- N_JSON
- N_KAFKA
- N_LINALG
- N_MODULE
- N_OPENSSL_COMPAT
- N_PRETTY
- N_PTH
- N_QUERY
- N_RANDOM
- N_SHA256
- N_STR
- N_TIFF
- N_TIME
- N_USER
- N_X509
- N_ZIP
- PARTICLES
- PCRE
- PHYSICS
- SIGNALS
- STACK
- THREADS
- TRAJECTORY
- TREE
- ZLIB