/* 'checkit_tiff' is a conformance checker for baseline TIFFs
 *
 * author: Andreas Romeyke, 2015-2022
 * licensed under conditions of libtiff
 * (see http://libtiff.maptools.org/misc.html)
 *
 */

#include "check.h"
#include "check_helper.h"
#include "ctstring.h"
#define PCRE2_CODE_UNIT_WIDTH 8
#define OVECCOUNT 30    /* should be a multiple of 3 */
#define MAX_HEXDUMP_SIZE 8192 /* 8192 */

#include <pcre2.h>
#include "simple_string.h"

/*
#define DEBUG
*/

static ret_t regex_no_match(ret_t *ret, char *val, uint32 count, int match_offset) {
    switch(match_offset) {
        case PCRE2_ERROR_NOMATCH: {
//                                 printf("PCRE2, no match! regex=%s\n", regex_string);
            char hexdump[MAX_HEXDUMP_SIZE];
            sprint_string_as_hexdump(hexdump, MAX_HEXDUMP_SIZE, val, count);
            (*ret) = set_value_found_ret_formatted(ret, "%s", hexdump);
            free(val);
            (*ret).returncode = tagerror_pcre_nomatch;
            break;
        }
            /*
                   Handle other special cases if you like
                   */
    }
    return (*ret);
}

static ret_t regex_match(ret_t *ret) {
    (*ret).returncode=is_valid;
    return (*ret);
}


static int match_given_regex(const char *val, uint32 count, pcre2_code_8 *re) {
    pcre2_match_data *ovector = pcre2_match_data_create(OVECCOUNT, NULL);
    /*  PCRE_NOTEMPTY: An empty string is not a valid match */
    int match_offset = pcre2_match(
            re,               /* compiled regex */
            (PCRE2_SPTR) val, /* subject string */
            ((int) count-1),  /* length of subject */
            0,                /* start offset */
            PCRE2_NOTEMPTY,   /* default options */
            ovector,          /* the match block */
            NULL
            );
#ifdef DEBUG
    printf("tag with count=%u and value='%s' -> match_offset=%d\n", count, val, match_offset);
#endif
    pcre2_code_free(re );
    return match_offset;
}

static ret_t regex_not_compileable(const char *regex_string, ret_t *ret, char *val, size_t erroffset, int errorcode) {
    PCRE2_UCHAR errormsg[51];
    pcre2_get_error_message(errorcode, errormsg, sizeof(errormsg));
    //printf("PCRE2, error=%s!\n", errormsg);
    (*ret) = set_value_found_ret_formatted_with_returncode(ret, pcre_compile_error, "regex '%s' compile error: %s at offset: %li\n", regex_string,
                                           errormsg, (int) erroffset);
    free(val);
    return (*ret);
}

ret_t check_tag_has_value_matching_regex(ctiff_t * ctif, tag_t tag, const char * regex_string) {
    GET_EMPTY_RET(ret);
    TIFP_CHECK(ctif, ret);
    EXIST_TAG(ctif,tag,ret);
    TIFFDataType datatype = TIFFGetRawTagType(ctif, ctif->ifd0, tag);
    if (datatype == TIFF_ASCII) {
        char *val = NULL;
        uint32 count = 0;
        ret = TIFFGetFieldASCII(ctif, ctif->ifd0, tag, &val, &count);
        if (ret.returncode != is_valid) { return ret; }
        if (0 < count) { /* there exists a tag */
            char hexdump[MAX_HEXDUMP_SIZE];
            sprint_string_as_hexdump(hexdump, MAX_HEXDUMP_SIZE, val, count);
            if (val[count - 1] != '\0') {
                ret = set_value_found_ret_formatted_with_returncode(&ret, tagerror_no_zero_as_end_of_string_in_asciivalue, "'%c' (at position %u: %s)", val[count - 1], count - 1, hexdump);
                return ret;
            }
            unsigned int r = count_multiple_zero_bytes(val, count);
            if (r != 0) {
                ret = set_value_found_ret_formatted_with_returncode(&ret, tagerror_multiple_zeros_in_asciivalue, "%s (\\0 at position %u in %u-len)", hexdump, r, count);
                return ret;
            }
            PCRE2_SIZE erroffset;
            int errorcode;
            pcre2_code *re = pcre2_compile(
                    (PCRE2_SPTR) regex_string,           /* the pattern */
                    PCRE2_ZERO_TERMINATED,  /* the pattern length in code units */
                    0,                      /* default options */
                    &errorcode,             /* for error code */
                    &erroffset,             /* for error offset */
                    NULL
            );                  /* no compile context */
            if (NULL != re) {
                int match_offset = match_given_regex(val, count, re);
                if (match_offset >= 0) {
                    return regex_match(&ret);
                } else {
                    return regex_no_match(&ret, val, count, match_offset);
                }
            } else {
                return regex_not_compileable(regex_string, &ret, val, erroffset, errorcode);
            }
        } else {
            ret.returncode = tagerror_expected_count_iszero;
            free(val);
            return ret;
        }
    } else {
        ret = set_value_found_ret_with_returncode(&ret, const_str(TIFFTypeName(datatype)), tagerror_unexpected_type_found);
        return ret;
    }
    return ret;
}



/* vim: set tabstop=2 softtabstop=2 shiftwidth=2 smarttab expandtab :*/
