/* 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; 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(const char *name, bool noerror) { struct typedefs *this; for (this = types; this != NULL; this = this->next) { if (strcmp(this->name, name) == 0) return this; } if (!noerror) mmfatal(PARSE_ERROR, "unrecognized data type name \"%s\"", name); return NULL; } 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; } }