/* create a hashtable */
jcov_hash_t *jcov_hash_new(SIZE_T    size,
                           UINTPTR_T (*hash_f)(void *),
                           SIZE_T    (*size_f)(void *),
                           int       (*compare_f)(void *, void *)) {
    jcov_hash_t *table = (jcov_hash_t*)jcov_calloc(sizeof(jcov_hash_t));
    table->n_entries = 0;
    table->size = size;
    table->entries = jcov_calloc(size * sizeof(jcov_bucket_t *));
    table->hash_f = hash_f;
    table->size_f = size_f;
    table->compare_f = compare_f;
    return table;
}
static void parse_options(char *options) {
    char *tmp;
    int (*read_option)(char *, char *);
    char opt_name[MAX_PATH_LEN];
    char opt_val[MAX_PATH_LEN];
    int i;

    if (options == NULL)
        return;

    if ((opt_cmp(options, OPT_HELP)) == 0) {
        jcov_usage();
    }
    
    cur_opt = options;
    read_option = read_option_cmdline;
    
    while (1) {
        if (read_option(opt_name, opt_val) == 0) {
            jcov_close(&opt_file);
            if (read_option == read_option_disk)
                read_option = read_option_cmdline;
            else
                break;
        } else if (opt_cmp(opt_name, OPT_OPTIONS_FILE) == 0) {
            if ((opt_file = fopen(opt_val, "rb")) == NULL) {
                sprintf(opt_name,"cannot open file : %s\n", opt_val);
                jcov_error_stop(opt_name);
            }
            read_option = read_option_disk;
        } else if (opt_cmp(opt_name, OPT_FILE) == 0) {
            jcov_file = (char*)jcov_calloc(strlen(opt_val) + 1);
            strcpy(jcov_file, opt_val);
        } else if (opt_cmp(opt_name, OPT_TYPE) == 0) {
            jcov_data_type = opt_val[0];
            if (jcov_data_type == 'M' || jcov_data_type == 'm')
                jcov_data_type = JCOV_DATA_M;
            else if (jcov_data_type == 'B' || jcov_data_type == 'b')
                jcov_data_type = JCOV_DATA_B;
            else {
                printf("Invalid data type : %c\n", jcov_data_type);
                jcov_usage();
            }
        } else if (opt_cmp(opt_name, OPT_ABSTR_METH) == 0) {
            if (opt_cmp(opt_val, OPT_ON) == 0)
                include_abstracts = 1;
        } else if (opt_cmp(opt_name, OPT_OVERWRITE) == 0) {
            if (opt_cmp(opt_val, OPT_ON) == 0)
                overwrite_jcov_file = 1;
        } else if (opt_cmp(opt_name, OPT_EC) == 0) {
            if (opt_cmp(opt_val, OPT_ON) == 0)
                load_early_classes = 1;
        } else if (opt_cmp(opt_name, OPT_VERBOSITY) == 0) {
            if (strlen(opt_val) > 1) {
                jcov_usage();
            }
            i = opt_val[0] - '0';
            if (i < 0 || i > 9) {
                jcov_usage();
            }
            verbose_mode = i;
        } else if (opt_cmp(opt_name, OPT_INCLUDE) == 0) {
            ADD_FILTER(class_filter, incl);
        } else if (opt_cmp(opt_name, OPT_EXCLUDE) == 0) {
            ADD_FILTER(class_filter, excl);
        } else if (opt_cmp(opt_name, OPT_CALLER_INCL) == 0) {
            ADD_FILTER(caller_filter, incl);
        } else if (opt_cmp(opt_name, OPT_CALLER_EXCL) == 0) {
            ADD_FILTER(caller_filter, excl);
        } else {
            printf("Unrecognized option : %s\n", opt_name);
            jcov_usage();
        }
    }
}