"checkit_tiff" is an incredibly fast conformance checker for baseline TIFFs (with various extensions), see http://andreas-romeyke.de
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1380 lines
49 KiB

/* rule based checks if given TIFF is a specific baseline TIFF
*
* author: Andreas Romeyke, 2015-2017
* licensed under conditions of libtiff
* (see http://libtiff.maptools.org/misc.html)
*
*/
/* #define YY_DEBUG */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include "config_parser.h"
#include "check_helper.h"
#include "msg_parser.h"
#include <pcre.h>
#define YY_CTX_LOCAL
/*
#define RULE_DEBUG 1
#define DEBUG 1
#define EXE_DEBUG 1
#define YY_DEBUG 1
*/
/* global vars */
parser_state_t parser_state;
/* redefined YY_INPUT to read from stream */
#define YY_INPUT(yyctx, buf, result, max_size) \
{ \
int yyc= fgetc(parser_state.stream); \
(result)= (EOF == yyc) ? 0 : (*(buf)= yyc, 1); \
yyprintf((stderr, "<%c>", yyc)); \
}
/* prints a plan (list) of functions */
/*
void print_plan () {
printf("print plan:\n");
executionentry_t * this_exe_p = plan.start;
while (NULL != this_exe_p) {
printf("next action is: %s\n", this_exe_p->name);
this_exe_p = this_exe_p->next;
}
}
*/
#define CHECKUNDERFLOW(SPACE_P, NAME) \
if (((SPACE_P)->NAME ## _stackp) >=MAXRESULT) {\
fprintf(stderr, "stackoverflow in " #SPACE_P "->" #NAME "_stack at lineno=%i, only %u results could be stored, increase MAXRESULT\n", parser_state.lineno, MAXRESULT);\
exit(EXIT_FAILURE);\
}
// endmacro CHECKUNDERFLOW
#define CHECKOVERFLOW(SPACE_P, NAME) \
if (((SPACE_P)->NAME ## _stackp) >=MAXRESULT) {\
fprintf(stderr, "stackoverflow in " #SPACE_P "->" #NAME "_stack at lineno=%i, only %u results could be stored, increase MAXRESULT\n", parser_state.lineno, MAXRESULT);\
exit(EXIT_FAILURE);\
}
// endmacro CHECKOVERFLOW
#define PUSH(SPACE_P, NAME, RES) \
(SPACE_P)->NAME ## _stack[((SPACE_P)->NAME ## _stackp)++]=RES;\
CHECKOVERFLOW(SPACE_P, NAME)
//endmacro PUSH
#define POP(SPACE_P, NAME, RES) \
CHECKUNDERFLOW(SPACE_P, NAME) \
(RES)=(SPACE_P)->NAME ## _stack[--((SPACE_P)->NAME ## _stackp)];
//endmacro POP
#define PRINT(SPACE_P, TYPESIG, NAME) \
CHECKOVERFLOW(SPACE_P, NAME) \
CHECKUNDERFLOW(SPACE_P, NAME) \
for (unsigned int j=0; j< (SPACE_P)->NAME ## _stackp; j++)\
printf(" " #SPACE_P "->" #NAME "-stack value[ %i ] --> " #TYPESIG "\n", j, (SPACE_P)->NAME ## _stack[j]);\
//endmacro PRINT
#define PRINTV(SPACE_P, VSTRINGARRAY, NAME) \
CHECKOVERFLOW(SPACE_P, NAME) \
CHECKUNDERFLOW(SPACE_P, NAME) \
for (unsigned int j=0; j< (SPACE_P)->NAME ## _stackp; j++)\
printf(" " #SPACE_P "->" #NAME "-stack value[ %i ] --> %s\n", j, (VSTRINGARRAY)[(SPACE_P)->NAME ## _stack[j]]);\
//endmacro PRINTV
static void result_push(full_res_t r) { PUSH(&parser_state, result, r); }
static void result_printstack() {
printf("=== BEGIN result_printstack\n");
for (int i = 0; i < parser_state.result_stackp; i++) {
full_res_t full_result = parser_state.result_stack[i];
printf("i=%i lineno=%i tag=%hu func=%s (%i) returncode=%s (%i)\n", i,
full_result.lineno,
full_result.tag,
get_parser_function_description(full_result.function),
full_result.function,
get_parser_error_description(full_result.returncode),
full_result.returncode
);
}
printf("=== END result_printstack\n");
}
static void i_printstack () { PRINT(&parser_state, "%u", i); }
static void i_push (unsigned int i) { PUSH(&parser_state, i, i);}
static unsigned int i_pop () { unsigned i; POP(&parser_state, i, i); return i; }
static void v_printstack () {
const char * vname[] = {
"range",
"logical_or",
"any",
"only",
"regex",
"ntupel",
};
PRINTV(&parser_state, vname, val);
}
static void v_push ( values_t v) { PUSH(&parser_state, val, v);}
static values_t v_pop () { values_t v; POP(&parser_state, val, v); return v; }
static void r_printstack () { PRINT(&parser_state, "%s", regex); }
static void r_push (char * r) { PUSH(&parser_state, regex, r); }
static char * r_pop () { char * v; POP(&parser_state, regex, v); return v; }
static void exe_push (internal_entry_t i) { PUSH(&parser_state, exe, i);}
static internal_entry_t exe_pop () { internal_entry_t e; POP(&parser_state, exe, e); return e; }
void exe_printstack_human_readable () {
CHECKOVERFLOW(&parser_state, exe);
CHECKUNDERFLOW(&parser_state, exe);
printf("\n/* the rules are in stack order, the top comes first */\n\n");
for (int j=parser_state.exe_stackp-1; j>=0; j--) {
printf("/* lineno=%03i */ ", parser_state.exe_stack[j].lineno );
if (parser_state.exe_stack[j].is_precondition) { printf ("PRECOND: "); }
else { printf ("EXEC: "); }
printf("%s tag=%i", get_parser_function_name(parser_state.exe_stack[j].function), parser_state.exe_stack[j].tag);
/* print i_stack */
if ( parser_state.exe_stack[j].i_stackp >0 && parser_state.exe_stack[j].i_stackp<=MAXSTACKDEPTH) {
printf (" top i stack=(%u)", parser_state.exe_stack[j].i_stack[ parser_state.exe_stack[j].i_stackp-1]);
}
/* print regex_stack */
if ( parser_state.exe_stack[j].regex_stackp >0 && parser_state.exe_stack[j].regex_stackp<=MAXSTACKDEPTH) {
for (int i=0; i < parser_state.exe_stack[j].regex_stackp; i++) {
printf(" regex_stack[%i]=%s", i, parser_state.exe_stack[j].regex_stack[i]);
}
}
if (!parser_state.exe_stack[j].is_precondition) { printf ("\n"); }
printf("\n");
}
}
/* help function to print exe stack */
static void exe_printstack () {
CHECKOVERFLOW(&parser_state, exe);
CHECKUNDERFLOW(&parser_state, exe);
printf("=== BEGIN exe_printstack\n");
for (int j=0; j< parser_state.exe_stackp; j++) {
printf(" exe-stack value[ %i ] --> {\n\tlineno=%i\n\tis_precondition=%s (%i)\n\ttag=%hu\n\tfunction_used=%s (%i)\n",
j,
parser_state.exe_stack[j].lineno,
(parser_state.exe_stack[j].is_precondition == true?"true":"false"),
parser_state.exe_stack[j].is_precondition,
parser_state.exe_stack[j].tag,
get_parser_function_description(parser_state.exe_stack[j].function),
parser_state.exe_stack[j].function
);
/* print i_stack */
if ( parser_state.exe_stack[j].i_stackp >=0 && parser_state.exe_stack[j].i_stackp<=MAXSTACKDEPTH) {
for (int i=0; i < parser_state.exe_stack[j].i_stackp; i++) {
printf("\ti_stack[%i]=%u\n", i, parser_state.exe_stack[j].i_stack[i]);
}
}
/* print regex_stack */
if ( parser_state.exe_stack[j].regex_stackp >=0 && parser_state.exe_stack[j].regex_stackp<=MAXSTACKDEPTH) {
for (int i=0; i < parser_state.exe_stack[j].regex_stackp; i++) {
printf("\tregex_stack[%i]=%s\n", i, parser_state.exe_stack[j].regex_stack[i]);
}
}
printf("}\n");
}
printf("=== END exe_printstack\n");
}
/* reduce results */
static void reduce_results() {
#ifdef DEBUG
printf("---------------------------------------------------------------------\n");
printf("begin REDUCE\n");
printf("---------------------------------------------------------------------\n");
#endif
/* go forward and eliminate all valid rule results */
full_res_t * tmp = NULL;
tmp = malloc(sizeof(full_res_t) * MAXRESULT);
if (NULL == tmp) {
fprintf(stderr, "Could not allocate memory\n");
exit(EXIT_FAILURE);
}
int tmpc=0;
for (int i = 0; i < parser_state.result_stackp; i++) {
full_res_t full_result = parser_state.result_stack[i];
#ifdef DEBUG
printf("reduce i=%i tmpc=%i lineno=%i tag=%i func=%s returncode=%i\n", i, tmpc, full_result.lineno, full_result.tag, get_parser_function_description(full_result.function), full_result.returncode );
if (full_result.found_value != NULL) { printf("\tvalue='%s'\n", full_result.found_value);}
#endif
if (full_result.returncode == parser_logical_combine_open) {
/* check all values until parser_logical_close found */
bool_t one_result_is_ok = false;
for (int j = i+1; j < parser_state.result_stackp; j++) {
full_res_t l_full_result = parser_state.result_stack[j];
if (l_full_result.returncode == parser_logical_combine_close) {
if (one_result_is_ok == true) {
full_result.returncode = is_valid;
/* free all combined values */
/* iterate and free all "ignored" entries */
for (int k=i+1; k<j; k++) {
if (NULL != parser_state.result_stack[k].found_value) {
free( parser_state.result_stack[k].found_value);
parser_state.result_stack[k].found_value = NULL;
}
}
i=j;
#ifdef DEBUG
printf("\tend combine i=%i j=%i\n", i, j);
#endif
}
break;
} else { /* combine results via OR */
#ifdef DEBUG
printf("\tcombine result i=%i j=%i, resultcode=%i resultmsg=%s\n", i, j, l_full_result.returncode, get_parser_error_description(l_full_result.returncode ));
#endif
if (l_full_result.returncode == is_valid) {
one_result_is_ok = true;
}
}
}
}
/* push to tmp */
tmp[tmpc++]=full_result;
} /* end for loop */
/* copy back */
for (int i = 0; i <= tmpc; i++) {
parser_state.result_stack[i]= tmp[i];
}
free(tmp);
tmp=NULL;
/* copy size */
parser_state.result_stackp=tmpc;
#ifdef DEBUG
printf("----------------------\n");
#endif
}
/* stack function for parser */
static void exe_i_push (internal_entry_t * ep, unsigned int i) {
PUSH( ep, i, i)
}
static unsigned int exe_i_pop(internal_entry_t * ep) {
unsigned int i; POP( ep, i, i); return i;
}
/* stack function for parser */
static internal_entry_t * exe_regex_push (internal_entry_t * ep, const char * s) {
#ifdef EXE_DEBUG
printf("-------------------\n");
printf("\tpush REGEX='%s'\n", s);
printf("-------------------\n");
#endif
PUSH( ep, regex, strdup(s))
#ifdef EXE_DEBUG
printf("\ton top='%s'\n", ep->regex_stack[ (ep->regex_stackp)-1 ]);
#endif
return ep;
}
static char * exe_regex_pop(internal_entry_t * ep) {
char * s;
POP( ep, regex, s);
#ifdef EXE_DEBUG
printf("-------------------\n");
printf("\tpop REGEX='%s'\n", s);
printf("-------------------\n");
printf("\ton top='%s'\n", ep->regex_stack[ (ep->regex_stackp) ]);
#endif
return s;
}
static char * __ch_malloc(char * msg) {
assert( msg == NULL);
msg = calloc(VALUESTRLEN, sizeof(char));
if (NULL == msg) {
fprintf(stderr, "Could not allocate memory, code=%s, line=%i\n", __FILE__, __LINE__); exit(could_not_allocate_memory);
}
return msg;
}
static ret_t call_exec_function(ctiff_t * ctif, ret_t * retp, internal_entry_t * exep) {
ret_t ret = *retp;
internal_entry_t exe = *exep;
char * expected_value=NULL;
switch (exe.function) {
case fc_true: { ret.returncode=is_valid; break;}
case fc_false: { ret.returncode=is_error; break; }
case fc_tag_has_some_of_these_values: { unsigned int count = exe_i_pop(&exe);
unsigned int values[count];
expected_value = __ch_malloc(expected_value);
char * strp = expected_value;
for (unsigned int j=0; j<count; j++) values[j]=exe_i_pop(&exe);
int all_printed = 0;
int printed = snprintf(strp, VALUESTRLEN, "count=%u, values ", count);
strp+=printed;
all_printed+=printed;
for (unsigned int j=0; j<count; j++) {
/* reduce VALUESTRLEN with n*printed */
printed = snprintf(strp, (size_t) (VALUESTRLEN - all_printed), " [%u]=%u", j, values[j]);
strp+=printed;
all_printed+=printed;
}
ret = check_tag_has_some_of_these_values(ctif, exe.tag, count, values);
break;
}
case fc_tag_has_valuelist: { unsigned int count = exe_i_pop(&exe);
unsigned int values[count];
expected_value = __ch_malloc(expected_value);
char * strp = expected_value;
for (unsigned int j=0; j<count; j++) values[j]=exe_i_pop(&exe);
int all_printed = 0;
int printed = snprintf(expected_value, VALUESTRLEN, "count=%u, values", count);
strp+=printed;
all_printed+=printed;
for (unsigned int j=0; j<count; j++) {
/* reduce VALUESTRLEN with n*printed */
printed = snprintf(strp, VALUESTRLEN, " [%u]=%u",j, values[j]);
strp+=printed;
all_printed+=printed;
}
ret = check_tag_has_valuelist(ctif, exe.tag, count, values);
break;
}
case fc_tag_has_value_in_range: { unsigned int a = exe_i_pop(&exe);
unsigned int b = exe_i_pop(&exe);
expected_value = __ch_malloc(expected_value);
snprintf(expected_value, VALUESTRLEN, "%u -- %u", a, b);
ret = check_tag_has_value_in_range(ctif, exe.tag, a, b);
break;
}
case fc_tag_has_value: { unsigned int a = exe_i_pop(&exe);
expected_value = __ch_malloc(expected_value);
snprintf(expected_value, VALUESTRLEN, "%u", a);
ret = check_tag_has_value(ctif, exe.tag, a);
break;
}
case fc_tag_has_value_quiet: { unsigned int a = exe_i_pop(&exe);
expected_value = __ch_malloc(expected_value);
snprintf(expected_value, VALUESTRLEN, "%u", a);
ret = check_tag_has_value_quiet(ctif, exe.tag, a);
break;
}
case fc_tag: { ret = check_tag(ctif, exe.tag); break;}
case fc_tag_quiet: { ret = check_tag_quiet(ctif, exe.tag); break;}
case fc_notag: {
expected_value = __ch_malloc(expected_value);
strncpy(expected_value, "nothing", VALUESTRLEN);
ret = check_notag(ctif, exe.tag);
break;
}
case fc_tag_has_valid_type: { ret = check_tag_has_valid_type(ctif, exe.tag); break;}
case fc_datetime: { ret = check_datetime(ctif); break;}
case fc_icc: { ret = check_icc(ctif); break;}
case fc_has_only_one_ifd: { ret = check_has_only_one_ifd(ctif); break;}
case fc_tagorder: { ret = check_tagorder(ctif); break;}
case fc_tag_has_valid_asciivalue: { ret = check_tag_has_valid_asciivalue(ctif, exe.tag); break;}
case fc_tag_has_value_matching_regex: { char * regex_string = exe_regex_pop( &exe);
expected_value = __ch_malloc(expected_value);
snprintf(expected_value, VALUESTRLEN, "regex=%s", regex_string);
ret = check_tag_has_value_matching_regex(ctif, exe.tag, regex_string);
free( regex_string );
break;
}
case fc_all_offsets_are_word_aligned: { ret = check_all_offsets_are_word_aligned(ctif); break;}
case fc_all_offsets_are_used_once_only: { ret = check_all_offsets_are_used_once_only(ctif); break;}
case fc_all_IFDs_are_word_aligned: { ret = check_all_IFDs_are_word_aligned(ctif); break;}
case fc_internal_logic_combine_open: { ret.returncode = parser_logical_combine_open ; break; }
case fc_internal_logic_combine_close: { ret.returncode = parser_logical_combine_close ; break; }
case fc_all_offsets_are_not_zero: { ret = check_all_offsets_are_greater_zero(ctif); break;}
case fc_all_geotiff_tags_have_same_count_of_values: {
ret = check_all_geotiff_tags(ctif); break;
}
case fc_check_cardinality: { ret = check_cardinality_of_some_tags_are_equal(ctif); break;}
default: {
GET_EMPTY_RET(res)
res.value_found = __ch_malloc(res.value_found);
fprintf(stderr, "lineno=%i, stack entry tag %i", parser_state.lineno, exe.tag);
exit(parser_error_wrong_function_found_in_parser_state_exe_stack);
;
}
}
assert( ret.returncode != should_not_occur );
assert( ret.returncode != calling_error_count_size);
*retp = ret;
return ret;
}
/* ececute fc directly */
static ret_t execute_function_and_set_results_for_next_call (ctiff_t * ctif, internal_entry_t * exep, ret_t * retp, int * precondition_result_p, function_t * precondition_function_p ) {
assert(NULL != ctif);
assert(NULL != exep);
assert(NULL != retp);
internal_entry_t exe = *exep;
ret_t ret = *retp;
ret = call_exec_function(ctif, &ret, &exe );
#ifdef EXE_DEBUG
printf("ret.returncode=%i, exe.is_precondition=%i is_valid=%i", ret.returncode, exe.is_precondition, is_valid);
if (exe.function == fc_tag_has_valid_type) {
printf("SSS tag %i has valid type: %i==%i (%s)\n", exe.tag, ret.returncode, is_valid, ret.value_found);
}
#endif
if (false == exe.is_precondition) { /* no precondition */
if (exe.tag >= MINTAGS) { /* exe.tag is 16bit, therefore true: && exe.tag <= (MAXTAGS-1)) */
parser_state.called_tags[exe.tag]=true; /* mark tag that it has a rule */
}
}
/* combine results */
if (
( true == exe.is_precondition ) &&
(
! (
exe.function == fc_tag_has_valid_type &&
(
ret.returncode == tagerror_unexpected_type_found ||
ret.returncode == tag_does_not_exist
)
)
)
) { /* precondition, next run depends on this result */
*precondition_result_p = ret.returncode;
if (NULL != ret.value_found) {
free(ret.value_found);
ret.value_found = NULL;
}
//if (NULL != expected_value) {
// free(expected_value);
// expected_value = NULL;
//}
} else { /* no precondition function */
/* if true, execute function */
full_res_t full;
full.tag = exe.tag;
full.function=exe.function;
full.lineno=exe.lineno;
full.returncode=ret.returncode;
full.expected_value=NULL;
full.found_value=NULL;
/*
if (NULL != expected_value) {
if (ret.returncode != is_valid) {
full.expected_value = expected_value;
} else {
free (expected_value);
expected_value = NULL;
}
}
*/
if (ret.returncode != is_valid) {
full.found_value = ret.value_found;
} else { free( ret.value_found ); ret.value_found=NULL; }
result_push( full );
*precondition_result_p=is_valid;
*precondition_function_p = exe.function;
}
#ifdef EXE_DEBUG
printf(" precondition_result=%i\n", *precondition_result_p);
#endif
return ret;
}
static ret_t clean_logical_or_if_precondition_failed (internal_entry_t * exep, ret_t * retp) {
assert(NULL != exep);
assert(NULL != retp);
internal_entry_t exe = *exep;
ret_t ret = *retp;
#ifdef EXE_DEBUG
printf ( "\t\t\t\t\t\t\t####################################\n" );
printf ( "exe function: %s\n", get_parser_function_description ( exe.function ) );
#endif
if ( exe.function == fc_internal_logic_combine_open ) { /* clean logical or if precondition fails */
do {
internal_entry_t l_exe = exe_pop();
#ifdef EXE_DEBUG
printf ( "~~~ eliminating Logicals because PRE fails: %s\n", get_parser_function_description ( l_exe.function ) );
#endif
if ( l_exe.function == fc_internal_logic_combine_close ) break;
} while ( parser_state.exe_stackp > 0 );
#ifdef EXE_DEBUG
printf ( "\t\t\t\t\t\t\t# ende\n" );
#endif
}
ret.returncode = is_valid;
return ret;
}
/* executes a plan (list) of functions, checks if predicate-function calls are
* needed, too. The plan is a global variable.
*
* HINT: the order of stack is reversed, that means:
* first, push an execute-function
* second, push the corresponding precondition
*
* Example: "123; optional; only("1")"
* push fc_tag_has_value 123 <--- test if tag 123 has only value 1, but only if precondition succeed
* push fc_tag_quiet 123 <--- precondition, checks if tag exists
*
* @param tif pointer to TIFF structure
* @return return-code is 0 if all called functions are succeed
*/
void execute_plan (ctiff_t * ctif) {
/* iterate other function-stack */
int precondition_result=is_valid;
bool_t last_run_was_a_precondition=false;
function_t precondition_function=fc_true;
for (unsigned int i = MINTAGS; i<MAXTAGS; i++) { /* mark all tags as "unprocessed" */
parser_state.called_tags[i]=false;
}
#ifdef EXE_DEBUG
printf("------------------------------------\n");
//i_printstack();
//r_printstack();
exe_printstack();
#endif
do { /* parser_state.exe_stackp > 0 */
GET_EMPTY_RET(ret)
internal_entry_t exe = exe_pop();
bool_t should_we_go_in_fc_call=
( /* Precondition was valid */
(true == last_run_was_a_precondition) &&
(is_valid == precondition_result)
) ||
( /* Precondition was valid, but has a type warning */
(true == last_run_was_a_precondition) &&
(tagwarn_type_of_unknown_tag_could_not_be_checked == precondition_result)
) ||
( /* last was a normal function */
false == last_run_was_a_precondition
)
;
#ifdef EXE_DEBUG
//i_printstack();
//exe_printstack();
printf("last run was a precondition? %s\n", last_run_was_a_precondition?"true":"false");
printf("last run was a precondition? (2) %s\n", (true==last_run_was_a_precondition)?"true":"false");
printf(".. precondition_result=%s\n", get_parser_error_description(precondition_result));
printf(".. precondition_function=%s\n", get_parser_function_description(precondition_function));
printf("should we go in fc call? %s\n", ( /* last was successful precondition */
should_we_go_in_fc_call)?"true":"false");
printf("parsing function %s (%i) (linecode=%i)\n", get_parser_function_description( exe.function), exe.function, exe.lineno);
#endif
if ( should_we_go_in_fc_call ) { /* if true, execute function */
ret = execute_function_and_set_results_for_next_call(ctif, &exe, &ret, &precondition_result, &precondition_function );
} else { // if precondition fails, return precondition if they have unexpected value!
if (last_run_was_a_precondition) {
ret = clean_logical_or_if_precondition_failed(&exe, &ret);
}
}
last_run_was_a_precondition=exe.is_precondition;
assert( ret.returncode != should_not_occur );
#ifdef EXE_DEBUG
if (ret.returncode == is_valid) {
printf("tmpresult = %s {returncode=%i, tag=%i}\n\nexe is precondition=%s\n", get_parser_error_description(ret.returncode), ret.returncode, exe.tag, exe.is_precondition==true?"true":"false");;
}else{
printf("tmpresult = %s {returncode=%i, tag=%i}\n\nexe is precondition=%s\n", get_parser_error_description(ret.returncode), ret.returncode, exe.tag, exe.is_precondition==true?"true":"false");;
}
printf("==========\n");
#endif
} while (parser_state.exe_stackp > 0);
#ifdef EXE_DEBUG
printf("all processed\n");
#endif
#ifndef NOTAGCHECK
for (unsigned int i = MINTAGS; i< MAXTAGS; i++) {
if (false==parser_state.called_tags[i]) {
ret_t res = check_notag(ctif, (tag_t) i);
if (res.returncode == tag_exist) {
#ifdef EXE_DEBUG
printf("checking %u -> res=%s (%i)\n", i, get_parser_error_description(res.returncode), res.returncode);
#endif
full_res_t full;
full.tag = (tag_t) i;
full.function=fc_notag;
full.lineno=-1;
full.returncode=res.returncode;
full.expected_value=NULL;
full.found_value=NULL;
result_push( full );
}
free( res.value_found );
}
}
#endif /* NOTAGCHECK */
}
/* function to clean an execution plan */
void clean_plan () {
}
/* helper function for parser */
static tag_t settag( tag_t tag) { parser_state.tag=tag; return tag; }
static tag_t settagref( tag_t tag) { parser_state.tagref=tag; return tag; }
/* helper function for parser */
static tag_t gettag( ) { return parser_state.tag;}
static int incrlineno() {
parser_state.lineno++;
#ifdef DEBUG
printf("##lineno=%i\n", parser_state.lineno);
#endif
return parser_state.lineno;
}
/* helper function for parser */
static int getlineno() { return parser_state.lineno;}
/*
int rule_tagorder_in_dsl( int tag ) {
int prevtag = gettag();
#ifdef DEBUG
printf ("tag '%i' found (prev tag was: '%i')\n", tag, prevtag);
#endif
if (prevtag > tag) {
fprintf(stderr, "tag '%i' should be greater than previous tag '%i'\n", tag, prevtag);
exit( -1 );
}
}
*/
/* helper function for parser */
static void commentline() {
#ifdef DEBUG
printf("commentline, %i\n", parser_state.lineno);
#endif
}
/* helper function for parser */
/*
void rule_should_not_occure(char* s) {
#ifdef DEBUG
printf("no parser rule matched after line %i (prev tag was %u): '%s'\n", getlineno(), gettag(), s);
#endif
}
*/
/* helper function for parser */
static void set_mandatory() {
#ifdef DEBUG
printf("tag '%u' is mandatory\n", gettag());
#endif
parser_state.req=mandatory;
}
/* helper function for parser */
static void set_optional() {
#ifdef DEBUG
printf("tag '%u' is optional\n", gettag());
#endif
parser_state.req=optional;
parser_state.any_reference=any_ref;
parser_state.tagref=gettag();
}
/* helper function for parser */
static void set_ifdepends() {
#ifdef DEBUG
printf("tag '%u' is set if depends\n", gettag());
#endif
parser_state.req=ifdepends;
}
/* helper function for parser */
static void set_optdepends() {
#ifdef DEBUG
printf("tag '%u' is set optional depends\n", gettag());
#endif
parser_state.req=optdepends;
}
/* helper function for parser */
static void regex_push( char * regex_string) {
pcre *re;
int erroffset;
const char * errorcode;
re = pcre_compile(
regex_string, /* the pattern */
0, /* default options */
&errorcode, /* for error code */
&erroffset, /* for error offset */
NULL); /* no compile context */
if (NULL != re) {
#ifdef DEBUG
printf("regex found: '%s' in line %i\n", regex_string, parser_state.lineno);
#endif
pcre_free(re);
r_push(regex_string);
} else {
fprintf(stderr, "regex compile error: %s at offset: %i in line %i\n", errorcode, erroffset, parser_state.lineno);
exit(EXIT_FAILURE);
}
}
/* helper function for parser */
static void reset_valuelist() {
parser_state.valuelist = 0;
}
/* helper function for parser */
static void incr_values () {
parser_state.valuelist++;
}
/* prepare functions for preconditions */
static internal_entry_t prepare_internal_entry() {
internal_entry_t p;
p.i_stackp=0;
p.regex_stackp=0;
p.lineno=getlineno();
p.is_precondition=true;
p.tag=parser_state.tagref;
p.function=fc_tag_quiet;
switch (parser_state.any_reference) {
case no_ref:
case any_ref:
p.function=fc_tag_quiet;
break;
case only_ref:
p.function=fc_tag_has_value_quiet;
exe_i_push(&p, i_pop() );
break;
case range_ref:
p.function=fc_tag_has_value_in_range;
exe_i_push(&p, i_pop() );
exe_i_push(&p, i_pop() );
break;
case ntupel_ref:
{
unsigned int c = i_pop();
for (unsigned int i = 0; i<c; i++) {
exe_i_push(&p, i_pop() );
}
exe_i_push(&p, c);
p.function=fc_tag_has_valuelist;
break;
}
case regex_ref:
p.function=fc_tag_has_value_matching_regex;
exe_regex_push(&p, r_pop());
break;
}
return p;
}
static void build_functional_structure__range(internal_entry_t * e_p) {
#ifdef RULE_DEBUG
printf("range found\n");
#endif
exe_i_push(e_p, i_pop() );
exe_i_push(e_p, i_pop() );
e_p->function=fc_tag_has_value_in_range;
}
static void build_functional_structure__ntupel(internal_entry_t * e_p) {
#ifdef RULE_DEBUG
printf("ntupel found\n");
#endif
unsigned int c = i_pop();
for (unsigned int i = 0; i<c; i++) {
exe_i_push(e_p, i_pop() );
}
exe_i_push(e_p, c);
e_p->function=fc_tag_has_valuelist;
}
static void build_functional_structure__only(internal_entry_t * e_p) {
#ifdef RULE_DEBUG
printf("only found\n");
#endif
exe_i_push(e_p, i_pop() );
e_p->function=fc_tag_has_value;
}
static void build_functional_structure__any(internal_entry_t * e_p) {
#ifdef RULE_DEBUG
printf("any found\n");
#endif
e_p->function=fc_tag;
}
static void build_functional_structure__regex(internal_entry_t * e_p) {
#ifdef RULE_DEBUG
printf("regex found\n");
#endif
exe_regex_push(e_p, r_pop());
e_p->function=fc_tag_has_value_matching_regex;
}
static void build_functional_structure__iccprofile(internal_entry_t * e_p) {
#ifdef RULE_DEBUG
printf("iccprofile found\n");
#endif
e_p->function=fc_icc;
}
static void build_functional_structure__datetime(internal_entry_t * e_p) {
#ifdef RULE_DEBUG
printf("datetime found\n");
#endif
e_p->function=fc_datetime;
}
static void build_functional_structure__printable_ascii(internal_entry_t * e_p) {
#ifdef RULE_DEBUG
printf("printable_ascii found\n");
#endif
exe_regex_push(e_p, "^[[:print:]]*$");
e_p->function=fc_tag_has_value_matching_regex;
}
/* builds an entry structure holding function and their values */
static void build_functional_structure(internal_entry_t * e_p, values_t val) {
switch (val) {
case range:
build_functional_structure__range(e_p);
break;
case ntupel:
build_functional_structure__ntupel(e_p);
break;
case only:
build_functional_structure__only(e_p);
break;
case any:
build_functional_structure__any(e_p);
break;
case regex:
build_functional_structure__regex(e_p);
break;
case iccprofile:
build_functional_structure__iccprofile(e_p);
break;
case datetime:
build_functional_structure__datetime(e_p);
break;
case printable_ascii:
build_functional_structure__printable_ascii(e_p);
break;
/* TODO:
case sbit: {
#ifdef RULE_DEBUG
printf("sbit found\n");
#endif
exe_regex_push(e_p, r_pop());
e_p->function=fc_tag_has_value;
break;
}
*/
default:
fprintf(stderr, "unknown val %i, should not occur\n", val);
exit(EXIT_FAILURE);
}
}
/* adds preconditions to stack, first comes the real function, than the preconditions! */
static void evaluate_req_and_push_exe(requirements_t req, internal_entry_t e) {
assert( e.function != fc_dummy);
#ifdef RULE_DEBUG
printf("eval e=%s\n", get_parser_function_description(e.function));
#endif
if (e.function == fc_internal_logic_combine_close) {
#ifdef RULE_DEBUG
printf("\n### eval req logical close found\n");
#endif
e.function = fc_internal_logic_combine_close;
exe_push(e); // TODO: exe_push(e); /* <-- logical close */
// TODO: exe_push(e); /* <-- real rule(s) */
for (int i = 0; i< parser_state.logical_elements; i++) {
values_t val = v_pop();
e.function = fc_dummy;
build_functional_structure(&e, val); /* <-- this builds the tiff test */
#ifdef RULE_DEBUG
printf("###### i=%i e=%s\n", i, get_parser_function_description(e.function));
#endif
exe_push(e);
};
e.function=fc_internal_logic_combine_open;
}
exe_push(e);
switch (req) {
case ifdepends:
case optional:
{
internal_entry_t p = prepare_internal_entry();
if (parser_state.mode & mode_enable_type_checks) {
internal_entry_t z = e;
z.is_precondition = true;
z.function = fc_tag_has_valid_type;
exe_push(z);
}
exe_push(p);
break;
}
case mandatory:
{
if (parser_state.mode & mode_enable_type_checks) {
internal_entry_t z = e;
z.is_precondition = true;
z.function = fc_tag_has_valid_type;
exe_push(z);
}
break;
}
case optdepends:
{
internal_entry_t pp;
pp.i_stackp = 0;
pp.regex_stackp = 0;
pp.val_stackp=0;
pp.lineno = getlineno();
pp.is_precondition = true;
pp.tag = e.tag;
pp.function = fc_tag_quiet;
internal_entry_t p = prepare_internal_entry();
if (parser_state.mode & mode_enable_type_checks) {
internal_entry_t z = e;
z.is_precondition = true;
z.function = fc_tag_has_valid_type;
exe_push(z);
}
exe_push(p);
exe_push(pp);
break;
}
default:
{
fprintf(stderr, "unknown parserstate.req (%i), should not occur\n", parser_state.req);
exit(EXIT_FAILURE);
}
}
}
static void set_rule_logical_open() {
#ifdef RULE_DEBUG
printf("rule_add_logical_config\n");
#endif
/*
internal_entry_t e;
e.tag = parser_state.tag;
e.i_stackp=0;
e.regex_stackp=0;
e.lineno=getlineno();
e.is_precondition=false;
e.function=fc_internal_logic_combine_open;
//exe_push(e);
*/
parser_state.within_logical_or=true;
//evaluate_req_and_push_exe( parser_state.req, e);
//rule_addtag_config();
}
static void incr_logical_elements() {
#ifdef RULE_DEBUG
printf("incr logical elements to %i\n", parser_state.logical_elements+1);
#endif
parser_state.logical_elements++;
}
/* helper function for parser */
static void set_rule_logical_close() {
#ifdef RULE_DEBUG
printf("reset_logical_list\n");
#endif
internal_entry_t e;
e.tag = parser_state.tag;
e.i_stackp=0;
e.regex_stackp=0;
e.val_stackp = 0;
e.lineno=getlineno();
e.is_precondition=false;
e.function=fc_internal_logic_combine_close;
//exe_push(e);
evaluate_req_and_push_exe( parser_state.req, e);
parser_state.within_logical_or=false;
parser_state.logical_elements=0;
}
/* this adds the config of a tagline to execution plan
* HINT: order of calling arguments from stacks is IMPORTANT! */
static void rule_addtag_config() {
if( parser_state.val_stackp > 0) {
#ifdef RULE_DEBUG
printf("rule_addtag_config\n");
#endif
internal_entry_t e;
e.i_stackp=0;
e.regex_stackp=0;
e.lineno=getlineno();
e.is_precondition=false; /* no precondition as default */
e.tag = parser_state.tag;
#ifdef RULE_DEBUG
printf( "try to match tagline at line %i\n", e.lineno);
#endif
/*
typedef struct internal_entry_s {
int lineno;
int count_of_arguments;
int is_precondition;
tag_t tag;
logic_t is_combined;
function_t function_used;
} internal_entry_t;
*/
/* HINT: order of evaluating last val and last req is IMPORTANT! */
#ifdef RULE_DEBUG
v_printstack();
i_printstack();
r_printstack();
#endif
/* stack should be organized as:
* [ruleA]
* [preconditionA] <- optional
* [prepreconditionA] <- optional
* [ruleB]
* [preconditionB] <- optional
* [prepreconditionB] <- optional
*/
do {
values_t val = v_pop();
#ifdef RULE_DEBUG
printf("was ist noch auf stacks?\n");
v_printstack();
i_printstack();
r_printstack();
printf("auf exe stack ist nun:\n");
exe_printstack();
printf("-----\n");
#endif
e.function = fc_dummy;
build_functional_structure(&e, val); /* <-- this builds the tiff test */
/* set predicate if and only if lastreq = depends */
/* HINT: order of evaluating last val and last req is IMPORTANT! */
/* HINT: order of calling arguments from stacks is IMPORTANT! */
evaluate_req_and_push_exe( parser_state.req, e); /* <-- this sets the predicates */
} while ( parser_state.val_stackp > 0);
reset_valuelist();
parser_state.any_reference = 0;
#ifdef RULE_DEBUG
printf("ENDE rule_addtag\n");
exe_printstack();
printf("\n\n\n");
#endif
}
}
/* set_mode */
static void set_mode(modes_t mode) {
#ifdef RULE_DEBUG
printf("Mode=%i at line=%i (needs to be implemented)\n", mode, parser_state.lineno );
#endif
internal_entry_t e;
e.i_stackp=0;
e.regex_stackp=0;
e.lineno=getlineno();
e.is_precondition=false; /* no precondition as default */
e.tag = parser_state.tag;
switch (mode) {
case mode_enable_offset_checks: {
e.function = fc_all_offsets_are_word_aligned;
exe_push(e);
e.function = fc_all_offsets_are_used_once_only;
exe_push(e);
e.function = fc_all_offsets_are_not_zero;
exe_push(e);
break;
}
case mode_enable_ifd_checks: {
e.function = fc_all_IFDs_are_word_aligned;
exe_push(e);
e.function = fc_tagorder;
exe_push(e);
e.function = fc_check_cardinality;
exe_push(e);
break;
}
case mode_baseline: {
e.function = fc_has_only_one_ifd;
exe_push(e);
break;
}
case mode_enable_deep_geotiff_checks: {
e.function = fc_all_geotiff_tags_have_same_count_of_values;
exe_push(e);
break;
}
case mode_enable_type_checks: { /* nothing, because we must enable it only in rule_addtag_config */
}
}
parser_state.mode |= mode;
}
/* reset the parser state */
void reset_parser_state() {
parser_state.any_reference=0;
parser_state.exe_stackp=0;
parser_state.includedepth=0;
parser_state.i_stackp=0;
parser_state.lineno=1;
parser_state.regex_stackp=0;
parser_state.req=0;
parser_state.result_stackp=0;
parser_state.tag=0;
parser_state.tagref=0;
parser_state.val_stackp=0;
parser_state.valuelist=0;
parser_state.mode=0;
parser_state.within_logical_or=false;
parser_state.logical_elements=0;
for (unsigned int i=0; i<MAXTAGS; i++) {
parser_state.called_tags[i]= false;
}
}
/* include the PEG generated parser, see "man peg" for details */
#include "config_dsl.grammar.c" /* yyparse() */
/* set_include */
static void set_include( const char * include_file ) {
if (parser_state.includedepth >= 1) {
fprintf( stderr, "only include depth of %i is supported\n", MAXINCLUDEDEPTH);
exit (EXIT_FAILURE);
}
parser_state.includedepth++;
printf("Include='%s'\n", include_file);
FILE * cfg = fopen(include_file, "r");
if (NULL == cfg) {
fprintf( stderr, "file '%s' could not be opened\n", include_file);
exit (EXIT_FAILURE);
};
clean_plan();
yycontext ctx2;
memset(&ctx2, 0, sizeof(yycontext));
FILE * old_stream = parser_state.stream;
int old_lineno = parser_state.lineno;
printf("At lineno %i, file %s, switching to include file %s\n", old_lineno, "", include_file);
parser_state.lineno=1;
parser_state.stream=cfg;
while (yyparse(&ctx2)) /* repeat until EOF */
;
yyrelease(&ctx2);
parser_state.includedepth--;
printf("End of Include=%s\n", include_file);
parser_state.stream = old_stream;
parser_state.lineno = old_lineno;
fclose(cfg);
}
/* function to parse a config file from STDIN */
void parse_plan () {
reset_parser_state();
/*/
r_printstack();
printf("rp=%i\n", parser_state.regex_stackp);
r_push("foo");
r_printstack();
printf("rp=%i\n", parser_state.regex_stackp);
const char * v = r_pop();
r_printstack();
printf("rp=%i %s\n", parser_state.regex_stackp, v);
fprintf(stderr, "----\n");
r_pop();
*/
yycontext ctx;
memset(&ctx, 0, sizeof(yycontext));
parser_state.stream=stdin;
while (yyparse(&ctx)) /* repeat until EOF */
;
yyrelease(&ctx);
}
/* function to parse a config file from file stream */
void parse_plan_via_stream( FILE * file ) {
reset_parser_state();
yycontext ctx;
memset(&ctx, 0, sizeof(yycontext));
parser_state.stream=file;
while (yyparse(&ctx)) /* repeat until EOF */
;
yyrelease(&ctx);
}
/* function to parse a config file */
void parse_plan_via_file( const char * cfg_file ) {
FILE * cfg = fopen(cfg_file, "r");
if (NULL == cfg) {
fprintf( stderr, "file '%s' could not be opened\n", cfg_file);
exit (EXIT_FAILURE);
};
clean_plan();
parse_plan_via_stream( cfg );
fclose(cfg);
}
/* set parse error
* @param msg describes the details about what was going wrong
* @param yytext gives context of the error
*/
static void set_parse_error(char * msg, char * yytext) {
fprintf(stderr, "%s at line %i (error at '%s')\n", msg, parser_state.lineno, yytext);
exit(EXIT_FAILURE);
}
void clean_plan_results() {
for (int i=parser_state.result_stackp-1; i >= 0; --i) {
if (NULL != parser_state.result_stack[i].found_value) {
free( parser_state.result_stack[i].found_value );
parser_state.result_stack[i].found_value=NULL;
}
if (NULL != parser_state.result_stack[i].expected_value) {
free( parser_state.result_stack[i].expected_value );
parser_state.result_stack[i].expected_value=NULL;
}
}
}
/* prints a plan (list) of functions and their results*/
ret_t print_plan_results(retmsg_t * actual_render) {
GET_EMPTY_RET(res)
#ifdef DEBUG
printf("print plan results:\n");
printf("####################(\n");
for (int i=parser_state.result_stackp-1; i >= 0; --i) {
full_res_t parser_result = parser_state.result_stack[i];
if (0 == parser_result.returncode) {
printf( "i=%i retcode=%i ", i, parser_result.returncode);
} else {
printf( "i=%i retcode=%i ", i, parser_result.returncode);
}
printf( "value-> %s lineno=%i\n", parser_result.found_value, parser_result.lineno);
}
printf(")####################\n");
printf("reduced\n");
#endif
#ifdef EXE_DEBUG
result_printstack();
#endif
reduce_results();
#ifdef EXE_DEBUG
result_printstack();
#endif
int count_of_valid_results = 0;
for (int i=parser_state.result_stackp-1; i >= 0; --i) {
full_res_t parser_result = parser_state.result_stack[i];
#ifdef DEBUG
printf( "i=%i retcode=%s (%i) lineno=%i tag=%i %s\n", i, get_parser_error_description(parser_result.returncode), parser_result.returncode, parser_result.lineno, parser_result.tag, parser_result.found_value);
#endif
returncode_t returncode = parser_result.returncode;
/* fill render pipeline */
uint16 tag = parser_result.tag;
/* fill with errorcodes */
rm_type_t type= rm_default;
switch ( returncode ) {
/* add rm_type */
case is_valid: type = rm_is_valid; count_of_valid_results++; break;
case parser_logicalor_error: type = rm_logicalor_error; break;
case could_not_allocate_memory:
case could_not_print:
case should_not_occur: assert ( 1 );
case code_error_streampointer_empty:
case code_error_filedescriptor_empty:
case code_error_ctif_empty:
case pcre_compile_error:
case tiff_seek_error_header:
case tiff_read_error_header:
case tiff_seek_error_offset:
case tiff_read_error_offset:
type = rm_hard_error; break;
default: type = rm_error; break;
}
__add_to_render_pipeline_via_strncpy(&actual_render, STRING_AND_LEN(""), type);
/* fill with tag infos */
if (tag > 0) {
char tagstr [VALUESTRLEN];
snprintf(tagstr, VALUESTRLEN, "tag %i (%s)", tag, TIFFTagName(tag));
__add_to_render_pipeline_via_strncpy(&actual_render, STRING_AND_LEN( tagstr), rm_tag);
} else {
__add_to_render_pipeline_via_strncpy(&actual_render, STRING_AND_LEN("general"), rm_mode);
}
/* fill with rule infos */
__add_to_render_pipeline_via_strncpy(&actual_render, STRING_AND_LEN( get_parser_function_description(parser_result.function)), rm_rule);
if (parser_result.expected_value != NULL) {
__add_to_render_pipeline_via_strncpy(&actual_render, STRING_AND_LEN( parser_result.expected_value), rm_expected);
} else {
// __add_to_render_pipeline_via_strncpy(&actual_render, "nothing", rm_expected);
}
/* fill with value found */
if (returncode != is_valid) {
__add_to_render_pipeline_via_strncpy(&actual_render, STRING_AND_LEN( get_parser_error_description(returncode)), rm_error_description);
if (parser_result.found_value != NULL) {
__add_to_render_pipeline_via_strncpy(&actual_render, STRING_AND_LEN(parser_result.found_value), rm_value);
} else {
//__add_to_render_pipeline_via_strncpy(&actual_render, "nothing", rm_value);
}
}
/* fill with lineno */
char msg[5];
snprintf(msg, 5, "%i", parser_result.lineno);
__add_to_render_pipeline_via_strncpy(&actual_render, STRING_AND_LEN(msg), rm_lineno);
/* fill with newline */
__add_to_render_pipeline_via_strncpy(&actual_render, STRING_AND_LEN(""), rm_endrule);
if (type == rm_hard_error) { goto harderrors; }
/* printf( "%s", renderer( parser_state.result_stack[i].result)); */
}
harderrors:
{
int errors = (parser_state.result_stackp -count_of_valid_results );
char msg[25];
snprintf(msg, 25, "Found %i errors", errors) ;
if (errors > 0) {
res.returncode = is_error;
__add_to_render_pipeline_via_strncpy(&actual_render, STRING_AND_LEN(msg), rm_error);
__add_to_render_pipeline_via_strncpy(&actual_render, STRING_AND_LEN("No, the given tif is not valid :("), rm_summary_invalid);
} else {
res.returncode = is_valid;
__add_to_render_pipeline_via_strncpy(&actual_render, msg, 24, rm_is_valid);
__add_to_render_pipeline_via_strncpy(&actual_render, STRING_AND_LEN("Yes, the given tif is valid :)"), rm_summary_valid);
}
}
__add_to_render_pipeline_via_strncpy(&actual_render, STRING_AND_LEN(""), rm_endtiff);
clean_plan_results();
return res;
}
/* vim: set tabstop=2 softtabstop=2 shiftwidth=2 smarttab expandtab :*/