diff options
Diffstat (limited to 'src/interfaces/ecpg/preproc/variable.c')
-rw-r--r-- | src/interfaces/ecpg/preproc/variable.c | 625 |
1 files changed, 625 insertions, 0 deletions
diff --git a/src/interfaces/ecpg/preproc/variable.c b/src/interfaces/ecpg/preproc/variable.c new file mode 100644 index 0000000..887d479 --- /dev/null +++ b/src/interfaces/ecpg/preproc/variable.c @@ -0,0 +1,625 @@ +/* src/interfaces/ecpg/preproc/variable.c */ + +#include "postgres_fe.h" + +#include "preproc_extern.h" + +static struct variable *allvariables = NULL; + +struct variable * +new_variable(const char *name, struct ECPGtype *type, int brace_level) +{ + struct variable *p = (struct variable *) mm_alloc(sizeof(struct variable)); + + p->name = mm_strdup(name); + p->type = type; + p->brace_level = brace_level; + + p->next = allvariables; + allvariables = p; + + return p; +} + +static struct variable * +find_struct_member(char *name, char *str, struct ECPGstruct_member *members, int brace_level) +{ + char *next = strpbrk(++str, ".-["), + *end, + c = '\0'; + + if (next != NULL) + { + c = *next; + *next = '\0'; + } + + for (; members; members = members->next) + { + if (strcmp(members->name, str) == 0) + { + if (next == NULL) + { + /* found the end */ + switch (members->type->type) + { + case ECPGt_array: + return new_variable(name, ECPGmake_array_type(ECPGmake_simple_type(members->type->u.element->type, members->type->u.element->size, members->type->u.element->counter), members->type->size), brace_level); + case ECPGt_struct: + case ECPGt_union: + return new_variable(name, ECPGmake_struct_type(members->type->u.members, members->type->type, members->type->type_name, members->type->struct_sizeof), brace_level); + default: + return new_variable(name, ECPGmake_simple_type(members->type->type, members->type->size, members->type->counter), brace_level); + } + } + else + { + *next = c; + if (c == '[') + { + int count; + + /* + * We don't care about what's inside the array braces so + * just eat up the character + */ + for (count = 1, end = next + 1; count; end++) + { + switch (*end) + { + case '[': + count++; + break; + case ']': + count--; + break; + default: + break; + } + } + } + else + end = next; + + switch (*end) + { + case '\0': /* found the end, but this time it has to be + * an array element */ + if (members->type->type != ECPGt_array) + mmfatal(PARSE_ERROR, "incorrectly formed variable \"%s\"", name); + + switch (members->type->u.element->type) + { + case ECPGt_array: + return new_variable(name, ECPGmake_array_type(ECPGmake_simple_type(members->type->u.element->u.element->type, members->type->u.element->u.element->size, members->type->u.element->u.element->counter), members->type->u.element->size), brace_level); + case ECPGt_struct: + case ECPGt_union: + return new_variable(name, ECPGmake_struct_type(members->type->u.element->u.members, members->type->u.element->type, members->type->u.element->type_name, members->type->u.element->struct_sizeof), brace_level); + default: + return new_variable(name, ECPGmake_simple_type(members->type->u.element->type, members->type->u.element->size, members->type->u.element->counter), brace_level); + } + break; + case '-': + if (members->type->type == ECPGt_array) + return find_struct_member(name, ++end, members->type->u.element->u.members, brace_level); + else + return find_struct_member(name, ++end, members->type->u.members, brace_level); + break; + break; + case '.': + if (members->type->type == ECPGt_array) + return find_struct_member(name, end, members->type->u.element->u.members, brace_level); + else + return find_struct_member(name, end, members->type->u.members, brace_level); + break; + default: + mmfatal(PARSE_ERROR, "incorrectly formed variable \"%s\"", name); + break; + } + } + } + } + + return NULL; +} + +static struct variable * +find_struct(char *name, char *next, char *end) +{ + struct variable *p; + char c = *next; + + /* first get the mother structure entry */ + *next = '\0'; + p = find_variable(name); + + if (c == '-') + { + if (p->type->type != ECPGt_array) + mmfatal(PARSE_ERROR, "variable \"%s\" is not a pointer", name); + + if (p->type->u.element->type != ECPGt_struct && p->type->u.element->type != ECPGt_union) + mmfatal(PARSE_ERROR, "variable \"%s\" is not a pointer to a structure or a union", name); + + /* restore the name, we will need it later */ + *next = c; + + return find_struct_member(name, ++end, p->type->u.element->u.members, p->brace_level); + } + else + { + if (next == end) + { + if (p->type->type != ECPGt_struct && p->type->type != ECPGt_union) + mmfatal(PARSE_ERROR, "variable \"%s\" is neither a structure nor a union", name); + + /* restore the name, we will need it later */ + *next = c; + + return find_struct_member(name, end, p->type->u.members, p->brace_level); + } + else + { + if (p->type->type != ECPGt_array) + mmfatal(PARSE_ERROR, "variable \"%s\" is not an array", name); + + if (p->type->u.element->type != ECPGt_struct && p->type->u.element->type != ECPGt_union) + mmfatal(PARSE_ERROR, "variable \"%s\" is not a pointer to a structure or a union", name); + + /* restore the name, we will need it later */ + *next = c; + + return find_struct_member(name, end, p->type->u.element->u.members, p->brace_level); + } + } +} + +static struct variable * +find_simple(char *name) +{ + struct variable *p; + + for (p = allvariables; p; p = p->next) + { + if (strcmp(p->name, name) == 0) + return p; + } + + return NULL; +} + +/* Note that this function will end the program in case of an unknown */ +/* variable */ +struct variable * +find_variable(char *name) +{ + char *next, + *end; + struct variable *p; + int count; + + next = strpbrk(name, ".[-"); + if (next) + { + if (*next == '[') + { + /* + * We don't care about what's inside the array braces so just eat + * up the characters + */ + for (count = 1, end = next + 1; count; end++) + { + switch (*end) + { + case '[': + count++; + break; + case ']': + count--; + break; + default: + break; + } + } + if (*end == '.') + p = find_struct(name, next, end); + else + { + char c = *next; + + *next = '\0'; + p = find_simple(name); + if (p == NULL) + mmfatal(PARSE_ERROR, "variable \"%s\" is not declared", name); + + *next = c; + switch (p->type->u.element->type) + { + case ECPGt_array: + return new_variable(name, ECPGmake_array_type(ECPGmake_simple_type(p->type->u.element->u.element->type, p->type->u.element->u.element->size, p->type->u.element->u.element->counter), p->type->u.element->size), p->brace_level); + case ECPGt_struct: + case ECPGt_union: + return new_variable(name, ECPGmake_struct_type(p->type->u.element->u.members, p->type->u.element->type, p->type->u.element->type_name, p->type->u.element->struct_sizeof), p->brace_level); + default: + return new_variable(name, ECPGmake_simple_type(p->type->u.element->type, p->type->u.element->size, p->type->u.element->counter), p->brace_level); + } + } + } + else + p = find_struct(name, next, next); + } + else + p = find_simple(name); + + if (p == NULL) + mmfatal(PARSE_ERROR, "variable \"%s\" is not declared", name); + + return p; +} + +void +remove_typedefs(int brace_level) +{ + struct typedefs *p, + *prev; + + for (p = prev = types; p;) + { + if (p->brace_level >= brace_level) + { + /* remove it */ + if (p == types) + prev = types = p->next; + else + prev->next = p->next; + + if (p->type->type_enum == ECPGt_struct || p->type->type_enum == ECPGt_union) + free(p->struct_member_list); + free(p->type); + free(p->name); + free(p); + if (prev == types) + p = types; + else + p = prev ? prev->next : NULL; + } + else + { + prev = p; + p = prev->next; + } + } +} + +void +remove_variables(int brace_level) +{ + struct variable *p, + *prev; + + for (p = prev = allvariables; p;) + { + if (p->brace_level >= brace_level) + { + /* is it still referenced by a cursor? */ + struct cursor *ptr; + + for (ptr = cur; ptr != NULL; ptr = ptr->next) + { + struct arguments *varptr, + *prevvar; + + for (varptr = prevvar = ptr->argsinsert; varptr != NULL; varptr = varptr->next) + { + if (p == varptr->variable) + { + /* remove from list */ + if (varptr == ptr->argsinsert) + ptr->argsinsert = varptr->next; + else + prevvar->next = varptr->next; + } + } + for (varptr = prevvar = ptr->argsresult; varptr != NULL; varptr = varptr->next) + { + if (p == varptr->variable) + { + /* remove from list */ + if (varptr == ptr->argsresult) + ptr->argsresult = varptr->next; + else + prevvar->next = varptr->next; + } + } + } + + /* remove it */ + if (p == allvariables) + prev = allvariables = p->next; + else + prev->next = p->next; + + ECPGfree_type(p->type); + free(p->name); + free(p); + if (prev == allvariables) + p = allvariables; + else + p = prev ? prev->next : NULL; + } + else + { + prev = p; + p = prev->next; + } + } +} + + +/* + * Here are the variables that need to be handled on every request. + * These are of two kinds: input and output. + * I will make two lists for them. + */ + +struct arguments *argsinsert = NULL; +struct arguments *argsresult = NULL; + +void +reset_variables(void) +{ + argsinsert = NULL; + argsresult = NULL; +} + +/* Insert a new variable into our request list. + * Note: The list is dumped from the end, + * so we have to add new entries at the beginning */ +void +add_variable_to_head(struct arguments **list, struct variable *var, struct variable *ind) +{ + struct arguments *p = (struct arguments *) mm_alloc(sizeof(struct arguments)); + + p->variable = var; + p->indicator = ind; + p->next = *list; + *list = p; +} + +/* Append a new variable to our request list. */ +void +add_variable_to_tail(struct arguments **list, struct variable *var, struct variable *ind) +{ + struct arguments *p, + *new = (struct arguments *) mm_alloc(sizeof(struct arguments)); + + for (p = *list; p && p->next; p = p->next); + + new->variable = var; + new->indicator = ind; + new->next = NULL; + + if (p) + p->next = new; + else + *list = new; +} + +void +remove_variable_from_list(struct arguments **list, struct variable *var) +{ + struct arguments *p, + *prev = NULL; + bool found = false; + + for (p = *list; p; p = p->next) + { + if (p->variable == var) + { + found = true; + break; + } + prev = p; + } + if (found) + { + if (prev) + prev->next = p->next; + else + *list = p->next; + } +} + +/* Dump out a list of all the variable on this list. + This is a recursive function that works from the end of the list and + deletes the list as we go on. + */ +void +dump_variables(struct arguments *list, int mode) +{ + char *str_zero; + + if (list == NULL) + return; + + str_zero = mm_strdup("0"); + + /* + * The list is build up from the beginning so lets first dump the end of + * the list: + */ + + dump_variables(list->next, mode); + + /* Then the current element and its indicator */ + ECPGdump_a_type(base_yyout, list->variable->name, list->variable->type, list->variable->brace_level, + list->indicator->name, list->indicator->type, list->indicator->brace_level, + NULL, NULL, str_zero, NULL, NULL); + + /* Then release the list element. */ + if (mode != 0) + free(list); + + free(str_zero); +} + +void +check_indicator(struct ECPGtype *var) +{ + /* make sure this is a valid indicator variable */ + switch (var->type) + { + struct ECPGstruct_member *p; + + case ECPGt_short: + case ECPGt_int: + case ECPGt_long: + case ECPGt_long_long: + case ECPGt_unsigned_short: + case ECPGt_unsigned_int: + case ECPGt_unsigned_long: + case ECPGt_unsigned_long_long: + break; + + case ECPGt_struct: + case ECPGt_union: + for (p = var->u.members; p; p = p->next) + check_indicator(p->type); + break; + + case ECPGt_array: + check_indicator(var->u.element); + break; + default: + mmerror(PARSE_ERROR, ET_ERROR, "indicator variable must have an integer type"); + break; + } +} + +struct typedefs * +get_typedef(char *name) +{ + struct typedefs *this; + + for (this = types; this && strcmp(this->name, name) != 0; this = this->next); + if (!this) + mmfatal(PARSE_ERROR, "unrecognized data type name \"%s\"", name); + + return this; +} + +void +adjust_array(enum ECPGttype type_enum, char **dimension, char **length, char *type_dimension, char *type_index, int pointer_len, bool type_definition) +{ + if (atoi(type_index) >= 0) + { + if (atoi(*length) >= 0) + mmfatal(PARSE_ERROR, "multidimensional arrays are not supported"); + + *length = type_index; + } + + if (atoi(type_dimension) >= 0) + { + if (atoi(*dimension) >= 0 && atoi(*length) >= 0) + mmfatal(PARSE_ERROR, "multidimensional arrays are not supported"); + + if (atoi(*dimension) >= 0) + *length = *dimension; + + *dimension = type_dimension; + } + + if (pointer_len > 2) + mmfatal(PARSE_ERROR, ngettext("multilevel pointers (more than 2 levels) are not supported; found %d level", + "multilevel pointers (more than 2 levels) are not supported; found %d levels", pointer_len), + pointer_len); + + if (pointer_len > 1 && type_enum != ECPGt_char && type_enum != ECPGt_unsigned_char && type_enum != ECPGt_string) + mmfatal(PARSE_ERROR, "pointer to pointer is not supported for this data type"); + + if (pointer_len > 1 && (atoi(*length) >= 0 || atoi(*dimension) >= 0)) + mmfatal(PARSE_ERROR, "multidimensional arrays are not supported"); + + if (atoi(*length) >= 0 && atoi(*dimension) >= 0 && pointer_len) + mmfatal(PARSE_ERROR, "multidimensional arrays are not supported"); + + switch (type_enum) + { + case ECPGt_struct: + case ECPGt_union: + /* pointer has to get dimension 0 */ + if (pointer_len) + { + *length = *dimension; + *dimension = mm_strdup("0"); + } + + if (atoi(*length) >= 0) + mmfatal(PARSE_ERROR, "multidimensional arrays for structures are not supported"); + + break; + case ECPGt_varchar: + case ECPGt_bytea: + /* pointer has to get dimension 0 */ + if (pointer_len) + *dimension = mm_strdup("0"); + + /* one index is the string length */ + if (atoi(*length) < 0) + { + *length = *dimension; + *dimension = mm_strdup("-1"); + } + + break; + case ECPGt_char: + case ECPGt_unsigned_char: + case ECPGt_string: + /* char ** */ + if (pointer_len == 2) + { + *length = *dimension = mm_strdup("0"); + break; + } + + /* pointer has to get length 0 */ + if (pointer_len == 1) + *length = mm_strdup("0"); + + /* one index is the string length */ + if (atoi(*length) < 0) + { + /* + * make sure we return length = -1 for arrays without given + * bounds + */ + if (atoi(*dimension) < 0 && !type_definition) + + /* + * do not change this for typedefs since it will be + * changed later on when the variable is defined + */ + *length = mm_strdup("1"); + else if (strcmp(*dimension, "0") == 0) + *length = mm_strdup("-1"); + else + *length = *dimension; + + *dimension = mm_strdup("-1"); + } + break; + default: + /* a pointer has dimension = 0 */ + if (pointer_len) + { + *length = *dimension; + *dimension = mm_strdup("0"); + } + + if (atoi(*length) >= 0) + mmfatal(PARSE_ERROR, "multidimensional arrays for simple data types are not supported"); + + break; + } +} |