<commit_before>#ifndef PARAMPARSER_H_
#define PARAMPARSER_H_

#define PCBC_PP_MAX_ARGS 10

typedef struct {
    char name[16];
    zval **ptr;
    zval *val;
} pcbc_pp_state_arg;

typedef struct {
    pcbc_pp_state_arg args[PCBC_PP_MAX_ARGS];
    int arg_req;
    int arg_opt;
    int arg_named;
    int cur_idx;
    zval *zids;
    zval tmpzid;
    HashPosition hash_pos;
} pcbc_pp_state;

// assumes first parameter in the spec is the ids (`id|`).
int pcbc_pp_begin(int param_count TSRMLS_DC, pcbc_pp_state *state, const char *spec, ...) {
    zval **args[PCBC_PP_MAX_ARGS];
    char arg_name[16];
    const char *spec_iter = spec;
    char *arg_iter = arg_name;
    int arg_type = 0;
    int arg_num = 0;
    zval *znamed;
    int arg_unnamed;
    int ii;
    va_list vl;
    va_start(vl,spec);

    if (_zend_get_parameters_array_ex(param_count, args TSRMLS_CC) != SUCCESS) {
        return FAILURE;
    }

    state->zids = *args[0];
    state->cur_idx = 0;
    state->arg_req = 0;
    state->arg_opt = 0;
    state->arg_named = 0;

    do {
        if (*spec_iter == 0 || *spec_iter == ',' || *spec_iter == '|') {
            if (arg_iter != arg_name) {
                pcbc_pp_state_arg *arg = &state->args[arg_num];
                *arg_iter = 0;
                
                memcpy(arg->name, arg_name, 16);
                arg->ptr = va_arg(vl, zval**);

                if (arg_num > 0 && arg_num < param_count) {
                    arg->val = *args[arg_num];
                } else {
                    arg->val = NULL;
                }

                if (arg_type == 0) {
                    state->arg_req++;
                } else if (arg_type == 1) {
                    state->arg_opt++;
                } else if (arg_type == 2) {
                    state->arg_named++;
                }

                arg_num++;
            }

            if (*spec_iter == '|') {
                if (arg_type < 2) {
                    arg_type++;
                }
            }
            if (*spec_iter == 0) {
                break;
            }
            arg_iter = arg_name;
        } else {
            *arg_iter++ = *spec_iter;
        }

        spec_iter++;
    } while(1);

    if (param_count < state->arg_req) {
        php_printf("Less than required number of args\n");
        return FAILURE;
    }

    arg_unnamed = state->arg_req + state->arg_opt;
    if (param_count > arg_unnamed) {
        znamed = *args[arg_unnamed];
    } else {
        znamed = NULL;
    }

    for (ii = 0; ii < state->arg_named; ++ii) {
        int aii = arg_unnamed + ii;
        pcbc_pp_state_arg *arg = &state->args[aii];

        if (znamed) {
            HashTable *htoptions = Z_ARRVAL_P(znamed);
            zval **zvalue;

            if (zend_hash_find(htoptions, arg->name, strlen(arg->name)+1,
                               (void**)&zvalue) == SUCCESS) {
                arg->val = *zvalue;<commit_msg>PCBC-321: Prevent paramparser from segfaulting with non-array options argument.<commit_after>#ifndef PARAMPARSER_H_
#define PARAMPARSER_H_

#define PCBC_PP_MAX_ARGS 10

typedef struct {
    char name[16];
    zval **ptr;
    zval *val;
} pcbc_pp_state_arg;

typedef struct {
    pcbc_pp_state_arg args[PCBC_PP_MAX_ARGS];
    int arg_req;
    int arg_opt;
    int arg_named;
    int cur_idx;
    zval *zids;
    zval tmpzid;
    HashPosition hash_pos;
} pcbc_pp_state;

// assumes first parameter in the spec is the ids (`id|`).
int pcbc_pp_begin(int param_count TSRMLS_DC, pcbc_pp_state *state, const char *spec, ...) {
    zval **args[PCBC_PP_MAX_ARGS];
    char arg_name[16];
    const char *spec_iter = spec;
    char *arg_iter = arg_name;
    int arg_type = 0;
    int arg_num = 0;
    zval *znamed;
    int arg_unnamed;
    int ii;
    va_list vl;
    va_start(vl,spec);

    if (_zend_get_parameters_array_ex(param_count, args TSRMLS_CC) != SUCCESS) {
        return FAILURE;
    }

    state->zids = *args[0];
    state->cur_idx = 0;
    state->arg_req = 0;
    state->arg_opt = 0;
    state->arg_named = 0;

    do {
        if (*spec_iter == 0 || *spec_iter == ',' || *spec_iter == '|') {
            if (arg_iter != arg_name) {
                pcbc_pp_state_arg *arg = &state->args[arg_num];
                *arg_iter = 0;
                
                memcpy(arg->name, arg_name, 16);
                arg->ptr = va_arg(vl, zval**);

                if (arg_num > 0 && arg_num < param_count) {
                    arg->val = *args[arg_num];
                } else {
                    arg->val = NULL;
                }

                if (arg_type == 0) {
                    state->arg_req++;
                } else if (arg_type == 1) {
                    state->arg_opt++;
                } else if (arg_type == 2) {
                    state->arg_named++;
                }

                arg_num++;
            }

            if (*spec_iter == '|') {
                if (arg_type < 2) {
                    arg_type++;
                }
            }
            if (*spec_iter == 0) {
                break;
            }
            arg_iter = arg_name;
        } else {
            *arg_iter++ = *spec_iter;
        }

        spec_iter++;
    } while(1);

    if (param_count < state->arg_req) {
        php_printf("Less than required number of args.\n");
        return FAILURE;
    }

    arg_unnamed = state->arg_req + state->arg_opt;
    if (param_count > arg_unnamed) {
        znamed = *args[arg_unnamed];

        // Ensure that it is an options array!
        if (Z_TYPE_P(znamed) != IS_ARRAY) {
            php_printf("Options argument must be an associative array.\n");
            return FAILURE;
        }
    } else {
        znamed = NULL;
    }

    for (ii = 0; ii < state->arg_named; ++ii) {
        int aii = arg_unnamed + ii;
        pcbc_pp_state_arg *arg = &state->args[aii];

        if (znamed) {
            HashTable *htoptions = Z_ARRVAL_P(znamed);
            zval **zvalue;

            if (zend_hash_find(htoptions, arg->name, strlen(arg->name)+1,
                               (void**)&zvalue) == SUCCESS) {
                arg->val = *zvalue;
{'fraction_non_alphanumeric': 0.12204274759340838, 'fraction_numerical': 0.008973731440691793, 'mean_word_length': 1.570230607966457, 'pattern_counts': {'":': 0, '<': 11, '<?xml version=': 0, '>': 49, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3163706', 'n_tokens_mistral': 2183, 'n_tokens_neox': 2043, 'n_words': 444}