text: #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 */
  	FTT_CONSTR_TYPE /* Find the type of the given expression having constraint */ 
  };
  
  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;
  }
  
  static asn1p_expr_t *
  asn1f_lookup_in_module(asn1p_module_t *mod, const char *name) {
      asn1p_expr_t *expr = genhash_get(mod->members_hash, name);
      if(!expr) {
          asn1p_expr_t *memb;
          TQ_FOR(memb, &mod->members, next) {
              if(memb->expr_type == ASN_BASIC_ENUMERATED) {
                  asn1p_expr_t *v = asn1f_lookup_child(memb, name);
                  if(v) return v;
              }
          }
      }
  
      return expr;
  }
  
  asn1p_module_t *
  asn1f_lookup_in_imports(arg_t *arg, asn1p_module_t *mod, const char *name) {
  	asn1p_xports_t *xp;
  
  	/*
  	 * Search in which exactly module this name is defined.
  	 */
  	TQ_FOR(xp, &(mod->imports), xp_next) {
          asn1p_module_t *fromModule =
              asn1f_lookup_module(arg, xp->fromModuleName, NULL);
          asn1p_expr_t *tc = (asn1p_expr_t *)0;
  
          TQ_FOR(tc, &(xp->xp_members), next) {
              if(strcmp(name, tc->Identifier) == 0)
  				break;
  
  			if(!fromModule)
  				continue;
  
              asn1p_expr_t *v =
                  asn1f_lookup_in_module(fromModule, name);
              if(v) 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, const 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_expr_t *rhs_pspecs, const asn1p_ref_t *ref, int recursion_depth) {
      asn1_namespace_t *initial_namespace = arg->ns;
  	asn1p_ref_t tmp_ref;
  	asn1p_module_t *imports_from;
  	char *modulename;
  	char *identifier;
  
      if(ref->module && arg->mod != ref->module) {
          return WITH_MODULE(
              ref->module,
              asn1f_lookup_symbol_impl(arg, rhs_pspecs, ref, recursion_depth));
      }
  
  	/*
  	 * 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("Lookup (%s) in %s for line %d",
  		asn1f_printable_reference(ref),
  		asn1_namespace_string(initial_namespace),
  		ref->_lineno);
  
  	if(recursion_depth++ > 30 /* Arbitrary constant */) {
          FATAL(
              "Excessive circular referencing detected in namespace %s for %s at "
              "line %d",
              asn1_namespace_string(initial_namespace),
              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, rhs_pspecs, ref);
  		
  		return extract;
  	} else {
  		DEBUG("\tToo many components: %d", ref->comp_count);
  		errno = EINVAL;
  		return NULL;
  	}
  
      if(modulename) {
          /*
           * The modulename is specified inside this reference.
           * To avoid recursion, reformat the reference
           * as it were local to that module.
           */
          tmp_ref = *ref;
          tmp_ref.components++; /* Hide the first element */
          tmp_ref.comp_count--;
          assert(tmp_ref.comp_count > 0);
          ref = &tmp_ref;
      }
  
      asn1_namespace_t *my_namespace = initial_namespace;
  
  #define DISPOSE_OF_MY_NAMESPACE()               \
      do {                                        \
          int tmp_errno = errno;                  \
          if(my_namespace != initial_namespace) { \
              asn1_namespace_free(my_namespace);  \
              my_namespace = NULL;                \
          }                                       \
          errno = tmp_errno;                      \
      } while(0)
  
      /*
       * If module name is specified explicitly
       * OR the current module's IMPORTS clause contains the identifier,
       * switch namespace to 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;
          }
  
          DISPOSE_OF_MY_NAMESPACE();
          my_namespace = asn1_namespace_new_from_module(imports_from, 1);
          DEBUG("Lookup (%s) in %s for line %d", asn1f_printable_reference(ref),
                asn1_namespace_string(my_namespace), ref->_lineno);
      }
  
      size_t namespace_switches = 0;
  
      /*
       * Search through all layers of the namespace.
       */
      for(ssize_t ns_item = my_namespace->elements_count - 1; ns_item >= 0;
          ns_item--) {
          struct asn1_namespace_element_s *ns_el =
              &my_namespace->elements[ns_item];
  
          switch(ns_el->selector) {
          case NAM_SYMBOL:
              if(modulename) {
                  /*
                   * Trying to match a fully specified "Module.Symbol"
                   * against the "Symbol" parameter. Doesn't match.
                   */
                  continue;
              }
              if(strcmp(ns_el->u.symbol.identifier, identifier) != 0) {
                  continue;
              } else {
                  DEBUG("Lookup (%s) in %s for line %d => found as parameter",
                        asn1f_printable_reference(ref),
                        asn1_namespace_string(my_namespace), ref->_lineno);
                  DISPOSE_OF_MY_NAMESPACE();
                  return ns_el->u.symbol.resolution;
              }
          case NAM_SPACE: {
              asn1p_expr_t *ref_tc; /* Referenced tc */
              /*
               * Do a direct symbol search in the given module.
               */
              ref_tc = asn1f_lookup_in_module(ns_el->u.space.module, identifier);
              if(ref_tc) {
                  /* It is acceptable that we don't use input parameters */
                  if(rhs_pspecs && !ref_tc->lhs_params) {
                      WARNING(
                          "Parameterized type %s expected "
                          "for %s at line %d",
                          ref_tc->Identifier, asn1f_printable_reference(ref),
                          ref->_lineno);
                  }
                  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;
                      DISPOSE_OF_MY_NAMESPACE();
                      return NULL;
                  }
                  if(rhs_pspecs && ref_tc->lhs_params) {
                      /* Specialize the target */
                      ref_tc =
                          asn1f_parameterization_fork(arg, ref_tc, rhs_pspecs);
                  }
  
                  DISPOSE_OF_MY_NAMESPACE();
                  return ref_tc;
              }
          }
  
      /*
              if(!expr && !(arg->expr->_mark & TM_BROKEN)
                 && !(imports_from->_tags & MT_STANDARD_MODULE)) {
                  arg->expr->_mark |= TM_BROKEN;
                  if(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);
                  }
              }
          */
  
              /* Search inside the IMPORTS section of the given module */
              imports_from =
                  asn1f_lookup_in_imports(arg, ns_el->u.space.module, identifier);
              if(imports_from) {
  
                  if(namespace_switches++ > 10 /* Arbitrary constant */) {
                      FATAL(
                          "Excessive circular referencing detected in namespace "
                          "%s for %s at "
                          "line %d",
                          asn1_namespace_string(arg->ns),
                          asn1f_printable_reference(ref), ref->_lineno);
                      errno = ETOOMANYREFS;
                      DISPOSE_OF_MY_NAMESPACE();
                      return NULL;
                  }
  
                  /* Switch namespace */
                  DISPOSE_OF_MY_NAMESPACE();
                  my_namespace = asn1_namespace_new_from_module(imports_from, 1);
                  DEBUG("Lookup (%s) in %s for line %d",
                        asn1f_printable_reference(ref),
                        asn1_namespace_string(my_namespace), ref->_lineno);
                  ns_item = my_namespace->elements_count;
  
                  continue;
              } else if(errno != ESRCH) {
                  /*
                   * Return only if the name was not found.
                   * If module was not found or more serious error
                   * encountered, just return preserving the errno.
                   */
                  DISPOSE_OF_MY_NAMESPACE();
                  return NULL;
              }
  
              if(modulename) {
                  FATAL(
                      "Module %s referred by %s in module %s "
                      "does not contain the requested symbol",
                      ns_el->u.space.module->ModuleName,
                      asn1f_printable_reference(ref), modulename);
                  break;
              }
  
              /* Search failed in the given namespace */
              continue;
          }
      }
  
      DEBUG(
          "Namespace %s does not contain \"%s\" "
          "mentioned at line %d: %s",
          asn1_namespace_string(my_namespace), identifier, ref->_lineno,
          strerror(errno));
  
      DISPOSE_OF_MY_NAMESPACE();
  
      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_expr_t *rhs_pspecs,
                      asn1p_ref_t *ref) {
      asn1p_expr_t *expr = asn1f_lookup_symbol_impl(arg, rhs_pspecs, ref, 0);
      if (ref) ref->ref_expr = expr;
      return expr;
  }
  
  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);
  }
  
  asn1p_expr_t *
  asn1f_find_ancestor_type_with_PER_constraint(arg_t *arg, asn1p_expr_t *expr) {
  	return asn1f_find_terminal_thing(arg, expr, FTT_CONSTR_TYPE);
  }
  
  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;
  
      if(arg->mod != expr->module) {
          return WITH_MODULE(expr->module,
                             asn1f_find_terminal_thing(arg, expr, what));
      }
  
      switch(what) {
      case FTT_TYPE:
      case FTT_CONSTR_TYPE:
          /* Expression may be a terminal type itself */
          if(expr->expr_type != A1TC_REFERENCE) {
              return expr;
          }
          ref = expr->reference;
          break;
      case FTT_VALUE:
  
  	DEBUG("%s(%s->%s) meta %d for line %d",
  		"VALUE", expr->Identifier, asn1f_printable_reference(ref),
  		expr->meta_type, expr->_lineno);
  
          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
             && WITH_MODULE(val_type_tc->module,
                            asn1f_look_value_in_type(arg, val_type_tc, expr))) {
              return expr;
          }
  		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->rhs_pspecs, ref);
      if(tc == NULL) {
  		/*
  	 	 * Lookup inside the ref's module and its IMPORTS section.
  	 	 */
          tc = WITH_MODULE(ref->module,
                           asn1f_lookup_symbol(arg, 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;
  
      if((what == FTT_CONSTR_TYPE) && (tc->constraints)) {
          return tc;
      }
  
      tc->_mark |= TM_RECURSION;
      expr = WITH_MODULE(tc->module, 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->xp_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;
  }
  