asn1c_misc.c 4.73 KB
#include "asn1c_internal.h"
#include <asn1fix_export.h>

#ifndef	DEFFILEMODE	/* Normally in <sys/stat.h> */
#define	DEFFILEMODE	(S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH)
#endif

/*
 * Construct identifier from multiple parts.
 * Convert unsafe characters to underscores.
 */
char *
asn1c_make_identifier(int unsafe_only_spaces, char *arg1, ...) {
	static char *storage;
	static int storage_size;
	int nodelimiter = 0;
	va_list ap;
	char *str;
	int size;
	char *p;

	if(arg1 == NULL)
		return NULL;

	/*
	 * Estimate the necessary storage size
	 */
	size = strlen(arg1);
	va_start(ap, arg1);
	while((str = va_arg(ap, char *)))
		size += 1 + strlen(str);
	va_end(ap);

	/*
	 * Make sure we have this amount of storage.
	 */
	if(storage_size <= size) {
		if(storage) free(storage);
		storage = malloc(size + 1);
		if(storage) {
			storage_size = size;
		} else {
			storage_size = 0;
			return NULL;
		}
	}

	/*
	 * Fill-in the storage.
	 */
	va_start(ap, arg1);
	str = arg1;
	p = storage;
	for(str = arg1; str; str = va_arg(ap, char *)) {
		int subst_made = 0;

		if(str[0] == ' ' && str[1] == '\0') {
			*p++ = ' ';
			nodelimiter = 1;	/* No delimiter */
			continue;
		}

		if(str != arg1 && !nodelimiter)
			*p++ = '_';	/* Delimiter between tokens */
		nodelimiter = 0;

		for(; *str; str++) {
			if(isalnum(*str)) {
				*p++ = *str;
				subst_made = 0;
			} else if(!subst_made++) {
				if(unsafe_only_spaces && !isspace(*str)) {
					*p ++ = *str;
				} else {
					*p++ = '_';
				}
			}
		}
	}
	va_end(ap);
	*p = '\0';

	assert((p - storage) <= storage_size);

	return storage;
}

FILE *
asn1c_open_file(arg_t *arg, const char *name, const char *ext) {
	int created = 1;
	struct stat sb;
	char *fname;
	int len;
	FILE *fp;
	int fd;

	/*
	 * Compute filenames.
	 */
	len = strlen(name) + strlen(ext) + 1;
	fname = alloca(len);
	snprintf(fname, len, "%s%s", name, ext);

	/*
	 * Create files.
	 */
	fd = open(fname, O_CREAT | O_EXCL | O_WRONLY, DEFFILEMODE);
	if(fd == -1 && errno == EEXIST) {
		fd = open(fname, O_WRONLY, DEFFILEMODE);
		created = 0;
	}
	if(fd == -1) {
		perror(fname);
		return NULL;
	}

	/*
	 * Check sanity.
	 */
	if(fstat(fd, &sb) || !S_ISREG(sb.st_mode)) {
		fprintf(stderr, "%s: Not a regular file\n", fname);
		if(created) unlink(fname);
		close(fd);
		return NULL;
	}

	(void)ftruncate(fd, 0);

	/*
	 * Convert file descriptor into file pointer.
	 */
	fp = fdopen(fd, "w");
	if(fp == NULL) {
		if(created) unlink(fname);
		close(fd);
	}
	return fp;
}

char *
asn1c_type_name(arg_t *arg, asn1p_expr_t *expr, enum tnfmt _format) {
	char *typename;

	switch(expr->expr_type) {
	case A1TC_REFERENCE:
		typename = expr->reference->components[
			expr->reference->comp_count-1].name;
		if(typename[0] == '&') {
			arg_t tmp = *arg;

			/*
			 * This is a reference to a type defined in a class.
			 * Resolve it and use instead.
			 */
			tmp.expr = asn1f_class_access_ex(arg->asn, arg->mod,
				arg->expr, expr->reference, &tmp.mod);
			if(tmp.expr) return NULL;

			return asn1c_type_name(&tmp, tmp.expr, _format);
		} else if(_format == TNF_RSAFE) {
			/*
			 * The recursion-safe format is requested.
			 * The problem here is that only constructed types
			 * might be referenced with "struct".
			 * Change RSAFE to CTYPE if the terminal type
			 * is primitive.
			 */
			asn1p_expr_t *terminal;
			terminal = asn1f_find_terminal_type_ex(
				arg->asn, arg->mod, arg->expr, NULL);
			if(terminal
			&& (terminal->expr_type
				& (ASN_BASIC_MASK | ASN_STRING_MASK)))
				_format = TNF_CTYPE;
		}
		break;
	case ASN_CONSTR_SEQUENCE_OF:
	case ASN_CONSTR_SET_OF:
		if(expr->Identifier) {
			typename = expr->Identifier;
		} else {
			asn1p_expr_t *child;
			child = TQ_FIRST(&(expr->members));
			typename = asn1c_type_name(arg, child, _format);
			if(typename)
				return typename;
			_format = TNF_SAFE;
			typename = child->Identifier;
		}
		break;
	case ASN_BASIC_INTEGER:
	case ASN_BASIC_ENUMERATED:
		if((arg->flags & A1C_USE_NATIVE_INTEGERS)) {
			switch(_format) {
			case TNF_CTYPE:
			case TNF_RSAFE:
				return "int";
			default:
				if(expr->expr_type == ASN_BASIC_INTEGER)
					return "NativeInteger";
				else
					return "NativeEnumerated";
			}
		}
		/* Fall through */
	default:
		if(expr->expr_type & (ASN_BASIC_MASK | ASN_STRING_MASK)) {
			if(_format == TNF_RSAFE)
				_format = TNF_CTYPE;
			typename = ASN_EXPR_TYPE2STR(expr->expr_type);
		} else {
			_format = TNF_SAFE;
			typename = expr->Identifier;
		}
	}

	switch(_format) {
	case TNF_UNMODIFIED:
	case TNF_INCLUDE:
		return asn1c_make_identifier(1, typename, 0);
	case TNF_SAFE:
		return asn1c_make_identifier(0, typename, 0);
	case TNF_CTYPE:
		return asn1c_make_identifier(0, typename, "t", 0);
	case TNF_RSAFE:
		return asn1c_make_identifier(0, "struct", " ", typename, 0);
	}

	assert("!unreachable");
	return typename;
}