#include "asn1fix_internal.h" enum ftt_what { FTT_TYPE, /* Find the type of the given expression */ FTT_VALUE, /* Find the value of the given expression */ }; static asn1p_expr_t *asn1f_find_terminal_thing(arg_t *arg, asn1p_expr_t *expr, enum ftt_what); static int asn1f_compatible_with_exports(arg_t *arg, asn1p_module_t *mod, const char *name); /* * Lookup a child by its name. */ asn1p_expr_t * asn1f_lookup_child(asn1p_expr_t *tc, const char *name) { asn1p_expr_t *child_tc; TQ_FOR(child_tc, &(tc->members), next) { if(child_tc->Identifier && strcmp(child_tc->Identifier, name) == 0) { return child_tc; } } errno = ESRCH; return NULL; } asn1p_module_t * asn1f_lookup_in_imports(arg_t *arg, asn1p_module_t *mod, const char *name) { asn1p_xports_t *xp; asn1p_expr_t *tc; /* * Search in which exactly module this name is defined. */ TQ_FOR(xp, &(mod->imports), xp_next) { TQ_FOR(tc, &(xp->members), next) { if(strcmp(name, tc->Identifier) == 0) break; } if(tc) break; } if(xp == NULL) { errno = ESRCH; return NULL; } /* * Okay, right now we have a module name and, hopefully, an OID. * Search the arg->asn for the specified module. */ mod = asn1f_lookup_module(arg, xp->fromModuleName, xp->identifier.oid); if(mod == NULL) { /* Conditional debug */ if(!(arg->expr->_mark & TM_BROKEN)) { arg->expr->_mark |= TM_BROKEN; FATAL("Cannot find external module \"%s\" " "mentioned for " "\"%s\" at line %d. " "Obtain this module and instruct compiler to process it too.", xp->fromModuleName, name, arg->expr->_lineno); } /* ENOENT/ETOOMANYREFS */ return NULL; } /* * Check that the EXPORTS section of this module contains * the symbol we care about, or it is EXPORTS ALL. */ if(asn1f_compatible_with_exports(arg, mod, name)) { errno = EPERM; return NULL; } return mod; } asn1p_module_t * asn1f_lookup_module(arg_t *arg, const char *module_name, asn1p_oid_t *oid) { asn1p_module_t *mod; assert(module_name); /* * If OID is given, the module_name is unused. * If OID is not given, the module_name may mean * either the real module's name, or the symbol which is the * result of renaming. Check this first. */ if(oid == 0) { asn1p_xports_t *xp; /* * Check inside the IMPORTS section for possible renaming. * Renaming practically means that a module_name is mentioned * somewhere in the IMPORTS section AND OID is given. */ TQ_FOR(xp, &(arg->mod->imports), xp_next) { if(strcmp(module_name, xp->fromModuleName)) continue; if(oid) { FATAL("Ambiguous reference: " "%s " "matches several modules", module_name); errno = ETOOMANYREFS; return NULL; } /* * Yes, there is a renaming. * Make lookup use OID instead. */ oid = xp->identifier.oid; } } /* * Perform lookup using OID or module_name. */ TQ_FOR(mod, &(arg->asn->modules), mod_next) { if(oid) { if(mod->module_oid) { if(asn1p_oid_compare(oid, mod->module_oid)) { continue; } else { /* Match! Even if name doesn't. */ return mod; } } else { /* Not match, even if name is the same. */ continue; } } if(strcmp(module_name, mod->ModuleName) == 0) return mod; } DEBUG("\tModule \"%s\" not found", module_name); errno = ENOENT; return NULL; } static asn1p_expr_t * asn1f_lookup_symbol_impl(arg_t *arg, asn1p_module_t *mod, asn1p_expr_t *rhs_pspecs, asn1p_ref_t *ref, int recursion_depth) { asn1p_expr_t *ref_tc; /* Referenced tc */ asn1p_module_t *imports_from; char *modulename; char *identifier; /* * First of all, a reference to a symbol may be specified * using several possible forms: * a) simple identifier * v INTEGER ::= value * b) external reference * v INTEGER ::= Module1.value * c) class-related stuff (the most complex stuff) * v ::= <[A-Z][A-Z0-9a-z-]*>.&<[A-Z0-9a-z-]+>. * All other forms are not implemented at this moment. */ DEBUG("(%s) in %s for line %d", asn1f_printable_reference(ref), mod->ModuleName, ref->_lineno); if(recursion_depth++ > 30 /* Arbitrary constant */) { FATAL("Excessive circular referencing detected in module %s for %s at line %d", mod->ModuleName, asn1f_printable_reference(ref), ref->_lineno); errno = ETOOMANYREFS; return NULL; } if(ref->comp_count == 1) { modulename = NULL; identifier = ref->components[0].name; } else if(ref->comp_count == 2 && ref->components[1].name[0] != '&') { modulename = ref->components[0].name; identifier = ref->components[1].name; } else if(ref->comp_count > 1 && isupper(ref->components[0].name[0]) && ref->components[1].name[0] == '&') { asn1p_expr_t *extract; /* * This is a reference to a CLASS-related stuff. * Employ a separate function for that. */ extract = asn1f_class_access(arg, mod, rhs_pspecs, ref); return extract; } else { DEBUG("\tToo many components: %d", ref->comp_count); errno = EINVAL; return NULL; } /* * If module name is specified explicitly * OR the current module's IMPORTS clause contains the identifier, * fetch that module. */ if(modulename) { imports_from = asn1f_lookup_module(arg, modulename, 0); if(imports_from == NULL) { FATAL("Module \"%s\" " "mentioned at line %d is not found", modulename, ref->_lineno); return NULL; } /* * Check that the EXPORTS section of this module contains * the symbol we care about, or it is EXPORTS ALL. */ if(asn1f_compatible_with_exports(arg,imports_from,identifier)) { errno = EPERM; return NULL; } } else { /* Search inside the IMPORTS section of the current module */ imports_from = asn1f_lookup_in_imports(arg, mod, identifier); if(imports_from == NULL && errno != ESRCH) { /* * Return only of the name was not found. * If module was not found or more serious error * encountered, just return preserving the errno. */ return NULL; } } /* * The symbol is being imported from another module. */ importing: if(imports_from) { asn1p_ref_t tmpref = *ref; asn1p_expr_t *expr; if(modulename) { /* * The modulename is specified inside this reference. * To avoid recursion, reformat the reference * as it were local to that module. */ tmpref.components++; /* Hide the first element */ tmpref.comp_count--; assert(tmpref.comp_count > 0); } expr = asn1f_lookup_symbol_impl(arg, imports_from, rhs_pspecs, &tmpref, recursion_depth); if(!expr && !(arg->expr->_mark & TM_BROKEN) && !(imports_from->_tags & MT_STANDARD_MODULE)) { arg->expr->_mark |= TM_BROKEN; if(modulename) { FATAL("Module %s referred by %s in module %s " "does not contain the requested symbol", imports_from->ModuleName, asn1f_printable_reference(ref), mod->ModuleName); } else { FATAL("Module %s referred in IMPORTS section " "for %s of module %s does not contain " "the requested symbol", imports_from->ModuleName, asn1f_printable_reference(ref), mod->ModuleName); } } return expr; } /* * Now we know where to search for a value: in the current module. */ TQ_FOR(ref_tc, &(mod->members), next) { if(ref_tc->Identifier) if(strcmp(ref_tc->Identifier, identifier) == 0) break; } if(ref_tc) { if(rhs_pspecs && !ref_tc->lhs_params) { FATAL("Parameterized type %s expected " "for %s at line %d", ref_tc->Identifier, asn1f_printable_reference(ref), ref->_lineno); errno = EPERM; return NULL; } if(!rhs_pspecs && ref_tc->lhs_params) { FATAL("Type %s expects specialization " "from %s at line %d", ref_tc->Identifier, asn1f_printable_reference(ref), ref->_lineno); errno = EPERM; return NULL; } if(rhs_pspecs && ref_tc->lhs_params) { /* Specialize the target */ ref_tc = asn1f_parameterization_fork(arg, ref_tc, rhs_pspecs); } return ref_tc; } /* * Not found in the current module. * Search in our default standard module. */ { /* Search inside standard module */ static asn1p_oid_t *uioc_oid; if(!uioc_oid) { asn1p_oid_arc_t arcs[] = { { 1, "iso" }, { 3, "org" }, { 6, "dod" }, { 1, "internet" }, { 4, "private" }, { 1, "enterprise" }, { 9363, "spelio" }, { 1, "software" }, { 5, "asn1c" }, { 3, "standard-modules" }, { 0, "auto-imported" }, { 1, 0 } }; uioc_oid = asn1p_oid_construct(arcs, sizeof(arcs)/sizeof(arcs[0])); } if(!imports_from && (!mod->module_oid || asn1p_oid_compare(mod->module_oid, uioc_oid))) { imports_from = asn1f_lookup_module(arg, "ASN1C-UsefulInformationObjectClasses", uioc_oid); if(imports_from) goto importing; } } DEBUG("Module \"%s\" does not contain \"%s\" " "mentioned at line %d: %s", mod->ModuleName, identifier, ref->_lineno, strerror(errno)); if(asn1f_check_known_external_type(identifier) == 0) { errno = EEXIST; /* Exists somewhere */ } else { errno = ESRCH; } return NULL; } asn1p_expr_t * asn1f_lookup_symbol(arg_t *arg, asn1p_module_t *mod, asn1p_expr_t *rhs_pspecs, asn1p_ref_t *ref) { return asn1f_lookup_symbol_impl(arg, mod, rhs_pspecs, ref, 0); } asn1p_expr_t * asn1f_find_terminal_type(arg_t *arg, asn1p_expr_t *expr) { return asn1f_find_terminal_thing(arg, expr, FTT_TYPE); } asn1p_expr_t * asn1f_find_terminal_value(arg_t *arg, asn1p_expr_t *expr) { return asn1f_find_terminal_thing(arg, expr, FTT_VALUE); } static asn1p_expr_t * asn1f_find_terminal_thing(arg_t *arg, asn1p_expr_t *expr, enum ftt_what what) { asn1p_ref_t *ref = 0; asn1p_expr_t *tc; switch(what) { case FTT_TYPE: /* Expression may be a terminal type itself */ if(expr->expr_type != A1TC_REFERENCE) return expr; ref = expr->reference; break; case FTT_VALUE: assert(expr->meta_type == AMT_VALUE); assert(expr->value); /* Expression may be a terminal type itself */ if(expr->value->type != ATV_REFERENCED) return expr; ref = expr->value->value.reference; break; } DEBUG("%s(%s->%s) for line %d", (what == FTT_VALUE)?"VALUE":"TYPE", expr->Identifier, asn1f_printable_reference(ref), expr->_lineno); assert(ref); /* * Lookup inside the type itself (ENUMERATED, INTEGER, etc). */ if(what == FTT_VALUE) { asn1p_expr_t *val_type_tc; val_type_tc = asn1f_find_terminal_type(arg, expr); if(val_type_tc && asn1f_look_value_in_type(arg, val_type_tc, expr)) return NULL; if(expr->value->type != ATV_REFERENCED) { return expr; } assert(ref == expr->value->value.reference); ref = expr->value->value.reference; } /* * Lookup inside the default module and its IMPORTS section. */ tc = asn1f_lookup_symbol(arg, expr->module, expr->rhs_pspecs, ref); if(tc == NULL) { DEBUG("\tSymbol \"%s\" not found: %s", asn1f_printable_reference(ref), strerror(errno)); return NULL; } /* * Recursive loops detection. */ if(tc->_mark & TM_RECURSION) { DEBUG("Recursion loop detected for %s at line %d", asn1f_printable_reference(ref), ref->_lineno); errno = EPERM; return NULL; } tc->_type_referenced = 1; tc->_mark |= TM_RECURSION; WITH_MODULE(tc->module, expr = asn1f_find_terminal_thing(arg, tc, what)); tc->_mark &= ~TM_RECURSION; return expr; } /* * Make sure that the specified name is present or otherwise does * not contradict with the EXPORTS clause of the specified module. */ static int asn1f_compatible_with_exports(arg_t *arg, asn1p_module_t *mod, const char *name) { asn1p_xports_t *exports; asn1p_expr_t *item; assert(mod); assert(name); exports = TQ_FIRST(&(mod->exports)); if(exports == NULL) { /* No EXPORTS section or EXPORTS ALL; */ return 0; } TQ_FOR(item, &(exports->members), next) { if(strcmp(item->Identifier, name) == 0) return 0; } /* Conditional debug */ if(!(arg->expr->_mark & TM_BROKEN)) { arg->expr->_mark |= TM_BROKEN; FATAL("EXPORTS section of module %s in %s " "does not mention %s at line %d", mod->ModuleName, mod->source_file_name, name, arg->expr->_lineno); } errno = ESRCH; return -1; }