aboutsummaryrefslogtreecommitdiffstats
path: root/libasn1compiler/asn1c_misc.c
blob: 8c5bb628729ec3d2ec034651ad0ae20b1fc3cb03 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
#include "asn1c_internal.h"
#include "asn1c_misc.h"

#include <asn1fix_export.h>

/*
 * Checks that the given string is not a reserved C/C++ keyword.
 * ISO/IEC 9899:1999 (C99), A.1.2
 */
static char *res_kwd[] = {
	"const", "auto", "register", "volatile", "restrict", "extern",
	"static", "inline",
	"signed", "unsigned",
	"void", "char", "short", "int", "long", "float", "double",
	"enum", "union", "struct", "typedef", "class",
	"switch", "case", "default", "break", "continue",
	"if", "else", "do", "for", "while", "goto",
	"sizeof", "return"
};
static int
reserved_keyword(const char *str) {
	int i;
	for(i = 0 ; i < sizeof(res_kwd)/sizeof(res_kwd[0]); i++) {
		if(strcmp(str, res_kwd[i]) == 0)
			return 1;
	}
	return 0;
}

/*
 * Construct identifier from multiple parts.
 * Convert unsafe characters to underscores.
 */
char *
asn1c_make_identifier(enum ami_flags_e flags, char *arg1, ...) {
	static char *storage;
	static int storage_size;
	int nodelimiter = 0;
	va_list ap;
	char *str;
	char *nextstr;
	size_t 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 = nextstr) {
		int subst_made = 0;
		nextstr = va_arg(ap, char *);

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

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

		/*
		 * If it is a single argument, check that it does not clash
		 * with C/C++ language keywords.
		 */
		if((flags & AMI_CHECK_RESERVED)
		&& str == arg1 && !nextstr && reserved_keyword(str)) {
			*p++ = toupper(*str++);
			/* Fall through */
		}

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

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

	return storage;
}

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

	/* Rewind to the topmost parent expression */
	if((top_parent = expr->parent_expr))
		while(top_parent->parent_expr)
			top_parent = top_parent->parent_expr;

	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);
			if(tmp.expr) return NULL;

			tmp.mod = tmp.expr->module;
			return asn1c_type_name(&tmp, tmp.expr, _format);
		}

		if(_format == TNF_RSAFE) {
			asn1p_expr_t *terminal;
			terminal = asn1f_find_terminal_type_ex(arg->asn, expr);
			if(terminal && terminal->expr_type & ASN_CONSTR_MASK) {
				typename = terminal->Identifier;
			}
		}

		if(_format == TNF_CTYPE) {
			/*
			 * If the component references the type itself,
			 * switch to a recursion-safe type naming
			 * ("struct foo" instead of "foo_t").
			 */
			asn1p_expr_t *terminal;
			terminal = asn1f_find_terminal_type_ex(arg->asn, expr);
			if(terminal && terminal == top_parent) {
				_format = TNF_RSAFE;
			}
		}
		break;
#if 0
	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;
#endif
	case ASN_BASIC_INTEGER:
	case ASN_BASIC_ENUMERATED:
	case ASN_BASIC_REAL:
		if((arg->flags & A1C_USE_NATIVE_TYPES)) {
			switch(_format) {
			case TNF_CTYPE:
			case TNF_RSAFE:
				if(expr->expr_type == ASN_BASIC_REAL)
					return "double";
				else
					return "long";
			default: break;
			}
			switch(expr->expr_type) {
			case ASN_BASIC_INTEGER:
				return "NativeInteger";
			case ASN_BASIC_ENUMERATED:
				return "NativeEnumerated";
			case ASN_BASIC_REAL:
				return "NativeReal";
			default: break;
			}
		}
		/* Fall through */
	default:
		if(expr->expr_type
		& (ASN_CONSTR_MASK | ASN_BASIC_MASK | ASN_STRING_MASK)) {
			if(_format == TNF_RSAFE)
				_format = TNF_CTYPE;
			typename = ASN_EXPR_TYPE2STR(expr->expr_type);
		} else {
			_format = TNF_RSAFE;
			typename = expr->Identifier;
		}
	}

	switch(_format) {
	case TNF_UNMODIFIED:
	case TNF_INCLUDE:
		return asn1c_make_identifier(AMI_MASK_ONLY_SPACES, typename, 0);
	case TNF_SAFE:
		return asn1c_make_identifier(0, typename, 0);
	case TNF_CTYPE:	/* C type */
		return asn1c_make_identifier(0, typename, "t", 0);
	case TNF_RSAFE:	/* Recursion-safe type */
		return asn1c_make_identifier(AMI_CHECK_RESERVED,
			"struct", " ", typename, 0);
	}

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