text: #include <stdio.h>
  #include <stdlib.h>
  #include <string.h>
  #include <errno.h>
  #include <assert.h>
  
  #include "asn1parser.h"
  
  static asn1p_expr_t *asn1p_expr_clone_impl(asn1p_expr_t *expr, int skip_extensions, asn1p_expr_t *(*)(asn1p_expr_t *, void *), void *);
  static asn1p_value_t *value_resolver(asn1p_value_t *, void *arg);
  
  /*
   * Construct a new empty types collection.
   */
  asn1p_expr_t *
  asn1p_expr_new(int _lineno, asn1p_module_t *mod) {
  	asn1p_expr_t *expr;
  
  	expr = calloc(1, sizeof *expr);
  	if(expr) {
  		TQ_INIT(&(expr->members));
  		expr->spec_index = -1;
  		expr->module = mod;
  		expr->_lineno = _lineno;
  	}
  
  	return expr;
  }
  
  asn1p_expr_t *
  asn1p_expr_clone(asn1p_expr_t *expr, int skip_extensions) {
  	return asn1p_expr_clone_impl(expr, skip_extensions, 0, 0);
  }
  
  asn1p_expr_t *
  asn1p_expr_clone_with_resolver(asn1p_expr_t *expr, asn1p_expr_t *(*r)(asn1p_expr_t *, void *), void *rarg) {
  	return asn1p_expr_clone_impl(expr, 0, r, rarg);
  }
  
  static asn1p_expr_t *
  asn1p_expr_clone_impl(asn1p_expr_t *expr, int skip_extensions, asn1p_expr_t *(*r)(asn1p_expr_t *, void *), void *rarg) {
  	asn1p_value_t *(*vr)(asn1p_value_t *, void *) = 0;
  	asn1p_expr_t *clone = 0;
  	asn1p_expr_t *tcmemb;	/* Child of tc */
  	int hit_ext = 0;
  
  #define	CLCOPY(field)	do { clone->field = expr->field; } while(0)
  #define	CLCLONE(field, func)	do { if(expr->field) {			\
  			clone->field = func(expr->field);		\
  			if(clone->field == NULL) {			\
  				asn1p_expr_free(clone);			\
  				return NULL;				\
  			}						\
  		} } while(0)
  #define	CLVRCLONE(field, func)	do { if(expr->field) {			\
  			clone->field = func(expr->field, vr, rarg);	\
  			if(clone->field == NULL) {			\
  				asn1p_expr_free(clone);			\
  				return NULL;				\
  			}						\
  		} } while(0)
  
  	if(r) {
  		vr = value_resolver;
  		clone = r(expr, rarg);
  		if(clone) {
  			/* Merge constraints */
  			if(expr->constraints) {
  				asn1p_constraint_t *tmpct = asn1p_constraint_clone_with_resolver(expr->constraints, vr, rarg);
  				if(clone->constraints) {
  					if(asn1p_constraint_insert(clone->constraints, tmpct)) {
  						asn1p_constraint_free(tmpct);
  						asn1p_expr_free(clone);
  						return NULL;
  					}
  				} else {
  					clone->constraints = tmpct;
  				}
  				assert(expr->combined_constraints == 0);
  			}
  			/* Merge defaults */
  			CLCOPY(marker.flags);
  			CLVRCLONE(marker.default_value,
  				asn1p_value_clone_with_resolver);
  			if(clone->tag.tag_class == TC_NOCLASS) {
  				CLCOPY(tag);
  			} else if(expr->tag.tag_class != TC_NOCLASS) {
  				fprintf(stderr, "asn1c does not support "
  					"nested tagging in parameterization, "
  					"necessary at line %d\n",
  					expr->_lineno);
  				asn1p_expr_free(clone);
  				return NULL;
  			}
  			return clone;
  		} else if(errno != ESRCH) {
  			return NULL;	/* Hard error */
  		}
  	}
  	if(!clone) clone = asn1p_expr_new(expr->_lineno, expr->module);
  	if(!clone) return NULL;
  
  	/*
  	 * Copy simple fields.
  	 */
  	CLCOPY(meta_type);
  	CLCOPY(expr_type);
  	CLCOPY(tag);
  	CLCOPY(marker.flags);		/* OPTIONAL/DEFAULT */
  	CLCOPY(_mark);
  
  	clone->data = 0;	/* Do not clone this */
  	clone->data_free = 0;	/* Do not clone this */
  
  	/*
  	 * Clone complex fields.
  	 */
  	CLCLONE(Identifier, strdup);
  	CLCLONE(reference, asn1p_ref_clone);
  	CLVRCLONE(constraints, asn1p_constraint_clone_with_resolver);
  	CLVRCLONE(combined_constraints, asn1p_constraint_clone_with_resolver);
  	CLCLONE(lhs_params, asn1p_paramlist_clone);
  	CLVRCLONE(value, asn1p_value_clone_with_resolver);
  	CLVRCLONE(marker.default_value, asn1p_value_clone_with_resolver);
  	CLCLONE(with_syntax, asn1p_wsyntx_clone);
  
  	/*
  	 * Copy all the children of this expr.
  	 */
  	TQ_FOR(tcmemb, &(expr->members), next) {
  		asn1p_expr_t *cmemb;
  
  		if(skip_extensions
  		&& tcmemb->expr_type == A1TC_EXTENSIBLE) {
  			hit_ext++; /* Even if hit_ext wraps around, we're OK. */
  			continue;
  		}
  		if(hit_ext == 1) continue;	/* Skip between ...'s */
  
  		cmemb = asn1p_expr_clone_impl(tcmemb, skip_extensions, r, rarg);
  		if(cmemb == NULL) {
  			asn1p_expr_free(clone);
  			return NULL;
  		}
  		asn1p_expr_add(clone, cmemb);
  	}
  
  	return clone;
  }
  
  
  static asn1p_value_t *
  value_resolver(asn1p_value_t *value, void *rarg) {
  	asn1p_value_t *cval;
  	asn1p_expr_t *tmpexpr;
  	asn1p_expr_t *target;
  	asn1p_ref_t *ref;
  	struct {
  		asn1p_expr_t *(*expr_resolve)(asn1p_expr_t *, void *arg);
  	} *varg = rarg;
  
  	if(!value || value->type != ATV_REFERENCED) {
  		errno = ESRCH;
  		return NULL;
  	}
  
  	ref = value->value.reference;
  	tmpexpr = asn1p_expr_new(ref->_lineno, 0);
  	tmpexpr->meta_type = AMT_TYPEREF;
  	tmpexpr->expr_type = A1TC_REFERENCE;
  	tmpexpr->reference = ref;
  	target = varg->expr_resolve(tmpexpr, rarg);
  	tmpexpr->reference = 0;
  	asn1p_expr_free(tmpexpr);
  
  	if(!target)
  		return NULL;	/* errno's are compatible */
  
  	if(target->meta_type == AMT_VALUE) {
  		if(!target->value) {
  			fprintf(stderr,
  			"FATAL: Parameterization did not resolve "
  			"value reference at line %d\n", ref->_lineno);
  			asn1p_expr_free(target);
  			errno = EPERM;
  			return NULL;
  		}
  		cval = asn1p_value_clone(target->value);
  	} else if(target->meta_type == AMT_VALUESET) {
  		if(!target->constraints) {
  			fprintf(stderr,
  			"FATAL: Parameterization did not resolve "
  			"value set reference at line %d\n", ref->_lineno);
  			asn1p_expr_free(target);
  			errno = EPERM;
  			return NULL;
  		}
  		cval = asn1p_value_fromconstr(target->constraints, 1);
  	} else {
  		errno = EPERM;
  		cval = NULL;
  	}
  
  	asn1p_expr_free(target);
  	return cval;
  }
  
  /*
   * Add expression as a member of another.
   */
  void
  asn1p_expr_add(asn1p_expr_t *to, asn1p_expr_t *what) {
  	TQ_ADD(&(to->members), what, next);
  	what->parent_expr = to;
  }
  
  /*
   * Add inner expressions as members of another.
   */
  void
  asn1p_expr_add_many(asn1p_expr_t *to, asn1p_expr_t *from_what) {
  	asn1p_expr_t *expr;
  	TQ_FOR(expr, &(from_what->members), next) {
          expr->parent_expr = to;
      }
  	TQ_CONCAT(&(to->members), &(from_what->members), next);
  }
  
  
  /*
   * Destruct the types collection structure.
   */
  void
  asn1p_expr_free(asn1p_expr_t *expr) {
  	if(expr) {
  		asn1p_expr_t *tm;
  
  		/* Remove all children */
  		while((tm = TQ_REMOVE(&(expr->members), next))) {
  			if(tm->parent_expr != expr)
  				printf("<%s:%p !-> %s:%p>\n",
  					tm->Identifier, tm->parent_expr,
  					expr->Identifier, expr);
  			assert(tm->parent_expr == expr);
  			asn1p_expr_free(tm);
  		}
  
  		free(expr->Identifier);
  		if(expr->reference)
  			asn1p_ref_free(expr->reference);
  		if(expr->constraints)
  			asn1p_constraint_free(expr->constraints);
  		if(expr->combined_constraints)
  			asn1p_constraint_free(expr->combined_constraints);
  		if(expr->lhs_params)
  			asn1p_paramlist_free(expr->lhs_params);
  		if(expr->value)
  			asn1p_value_free(expr->value);
  		if(expr->marker.default_value)
  			asn1p_value_free(expr->marker.default_value);
  		if(expr->with_syntax)
  			asn1p_wsyntx_free(expr->with_syntax);
  
  		if(expr->data && expr->data_free)
  			expr->data_free(expr->data);
  
  		memset(expr, 0, sizeof(*expr));
  		free(expr);
  	}
  }
  
  
  char *asn1p_tag2string(struct asn1p_type_tag_s *tag, char *buf) {
  	static char buf_stat[TAG2STRING_BUFFER_SIZE];
  	char *start;
  	char *end;
  
  	if(!buf) buf = buf_stat;
  	start = buf;
  	end = buf + TAG2STRING_BUFFER_SIZE;
  
  	if(tag->tag_class == TC_NOCLASS) {
  		*buf = 0;
  		return buf;
  	}
  
  	strcpy(buf, "[");
  	switch(tag->tag_class) {
  	case TC_NOCLASS:
  		assert(tag->tag_class != TC_NOCLASS);
  		break;
  	case TC_UNIVERSAL:	strcat(buf, "UNIVERSAL ");	break;
  	case TC_PRIVATE:	strcat(buf, "PRIVATE ");	break;
  	case TC_APPLICATION:	strcat(buf, "APPLICATION ");	break;
  	case TC_CONTEXT_SPECIFIC:
  		break;
  	}
  	buf += snprintf(buf + strlen(buf), end - buf,
  		"%" PRIdASN "]", tag->tag_value);
  	assert((unsigned int)(buf - end) > sizeof(" IMPLICIT "));
  
  	switch(tag->tag_mode) {
  	case TM_DEFAULT: break;
  	case TM_IMPLICIT: strcat(buf, " IMPLICIT"); break;
  	case TM_EXPLICIT: strcat(buf, " EXPLICIT"); break;
  	}
  
  	return start;
  }
  