constraint checking code
git-svn-id: https://asn1c.svn.sourceforge.net/svnroot/asn1c/trunk@149 59561ff5-6e30-0410-9f3c-9617f08c8826
diff --git a/libasn1compiler/asn1c_C.c b/libasn1compiler/asn1c_C.c
index 197ce1f..ac13873 100644
--- a/libasn1compiler/asn1c_C.c
+++ b/libasn1compiler/asn1c_C.c
@@ -4,7 +4,8 @@
*/
#include "asn1c_internal.h"
#include "asn1c_C.h"
-#include <asn1fix_export.h> /* exportable stuff from libasn1fix */
+#include "asn1c_constraint.h"
+#include <asn1fix_export.h> /* Stuff exported by libasn1fix */
typedef struct tag2el_s {
struct asn1p_type_tag_s el_tag;
@@ -25,17 +26,6 @@
static int check_if_extensible(asn1p_expr_t *expr);
static int emit_tags_vector(arg_t *arg, asn1p_expr_t *expr, int *tags_impl_skip, int choice_mode);
static int emit_tag2member_map(arg_t *arg, tag2el_t *tag2el, int tag2el_count);
-static int emit_constraint_checking_code(arg_t *arg);
-static int emit_single_constraint_check(arg_t *arg, asn1p_constraint_t *ct, int mode);
-static int emit_alphabet_tables(arg_t *arg, asn1p_constraint_t *ct, int *table);
-static int emit_alphabet_check_cycle(arg_t *arg);
-static int check_constraint_type_presence(asn1p_constraint_t *ct, enum asn1p_constraint_type_e type);
-static asn1p_expr_type_e _find_terminal_type(arg_t *arg);
-static int emit_value_determination_code(arg_t *arg);
-static int emit_size_determination_code(arg_t *arg);
-static long compute_min_size(arg_t *arg);
-static long compute_max_size(arg_t *arg);
-static long compute_xxx_size(arg_t *arg, int _max);
#define C99_MODE (arg->flags & A1C_NO_C99)
#define UNNAMED_UNIONS (arg->flags & A1C_UNNAMED_UNIONS)
@@ -969,28 +959,28 @@
/*
* Constraint checking.
*/
- if(expr->constraints) /* Emit tables with FROM() constraints */
- emit_alphabet_tables(arg, expr->constraints, 0);
+ /* Emit FROM() tables and others */
+ asn1c_emit_constraint_tables(arg, 0);
+
p = MKID(expr->Identifier);
OUT("int\n");
OUT("%s_constraint(asn1_TYPE_descriptor_t *td, const void *sptr,\n", p);
INDENTED(
OUT("\t\tasn_app_consume_bytes_f *app_errlog, void *app_key) {\n");
OUT("\n");
- if(expr->constraints) {
-
- emit_constraint_checking_code(arg);
-
+ if(asn1c_emit_constraint_checking_code(arg) == 1) {
+ if(0) {
OUT("/* Check the constraints of the underlying type */\n");
OUT("return asn1_DEF_%s.check_constraints\n",
asn1c_type_name(arg, expr, TNF_SAFE));
OUT("\t(td, sptr, app_errlog, app_key);\n");
- } else {
+ } else {
OUT("/* Make the underlying type checker permanent */\n");
OUT("td->check_constraints = asn1_DEF_%s.check_constraints;\n",
asn1c_type_name(arg, expr, TNF_SAFE));
OUT("return td->check_constraints\n");
OUT("\t(td, sptr, app_errlog, app_key);\n");
+ }
}
);
OUT("}\n");
@@ -1365,687 +1355,3 @@
return tags_count;
}
-
-static int
-emit_constraint_checking_code(arg_t *arg) {
- asn1p_expr_t *expr = arg->expr;
- asn1p_expr_type_e etype;
- int size_present, value_present;
-
- if(expr->constraints == NULL)
- return 0; /* No constraints defined */
-
- etype = _find_terminal_type(arg);
-
- size_present = check_constraint_type_presence(expr->constraints,
- ACT_CT_SIZE);
- value_present = check_constraint_type_presence(expr->constraints,
- ACT_EL_VALUE);
-
- if(size_present || value_present) {
- OUT("const %s_t *st = sptr;\n", MKID(arg->expr->Identifier));
- if(size_present) {
- OUT("size_t size;\n");
- OUT("size_t min_size __attribute__ ((unused)) = %ld;\n",
- compute_min_size(arg));
- OUT("size_t max_size __attribute__ ((unused)) = %ld;\n",
- compute_max_size(arg));
- }
- if(value_present)
- switch(etype) {
- case ASN_BASIC_INTEGER:
- case ASN_BASIC_ENUMERATED:
- OUT("long value;\n");
- break;
- case ASN_BASIC_BOOLEAN:
- OUT("int value;\n");
- break;
- default:
- break;
- }
- OUT("\n");
- }
-
- OUT("if(!sptr) {\n");
- INDENT(+1);
- OUT("_ASN_ERRLOG(app_errlog, app_key,\n");
- OUT("\t\"%%s: value not given\", td->name);\n");
- OUT("return -1;\n");
- INDENT(-1);
- OUT("}\n");
- OUT("\n");
-
- if(size_present)
- emit_size_determination_code(arg);
- if(value_present)
- emit_value_determination_code(arg);
-
- OUT("\n");
- OUT("if(\n");
- emit_single_constraint_check(arg, expr->constraints, 0);
- OUT(") {\n");
- INDENTED(OUT("/* Constraint check succeeded */\n"));
- OUT("} else {\n");
- INDENT(+1);
- OUT("_ASN_ERRLOG(app_errlog, app_key,\n");
- OUT("\t\"%%s: constraint failed\", td->name);\n");
- OUT("return -1;\n");
- INDENT(-1);
- OUT("}\n");
-
- return 0;
-}
-
-static int
-emit_single_constraint_check(arg_t *arg, asn1p_constraint_t *ct, int mode) {
- char *s_v;
- int el;
-
- assert(arg && ct);
-
- switch(ct->type) {
- case ACT_INVALID:
- assert(ct->type != ACT_INVALID);
- OUT("-1 /* Invalid constraint at line %d */\n", ct->_lineno);
- break;
- case ACT_EL_VALUE:
- OUT("(");
- if(mode == ACT_CT_SIZE) s_v = "size";
- else s_v = "value";
- OUT("%s", s_v);
- if(ct->value->type != ATV_TRUE)
- OUT(" == ");
- switch(ct->value->type) {
- case ATV_INTEGER: OUT("%lld",
- (long long)ct->value->value.v_integer); break;
- case ATV_MIN: OUT("min_%s", s_v); break;
- case ATV_MAX: OUT("max_%s", s_v); break;
- case ATV_FALSE: OUT("0"); break;
- case ATV_TRUE: break;
- default:
- break;
- }
- OUT(")\n");
- break;
- case ACT_EL_RANGE:
- case ACT_EL_LLRANGE:
- case ACT_EL_RLRANGE:
- case ACT_EL_ULRANGE:
- if(mode == ACT_CT_SIZE) {
- s_v = "size";
- } else {
- s_v = "value";
- }
- OUT("((%s", s_v);
- switch(ct->type) {
- case ACT_EL_RANGE:
- case ACT_EL_RLRANGE:
- OUT(" >= "); break;
- case ACT_EL_LLRANGE:
- case ACT_EL_ULRANGE:
- OUT(" > "); break;
- default: break;
- }
- switch(ct->range_start->type) {
- case ATV_INTEGER: OUT("%lld",
- (long long)ct->range_start->value.v_integer); break;
- case ATV_MIN: OUT("min_%s", s_v); break;
- case ATV_MAX: OUT("max_%s", s_v); break;
- case ATV_FALSE: OUT("0"); break;
- case ATV_TRUE: break;
- default:
- break;
- }
- OUT(") && (%s", s_v);
- switch(ct->type) {
- case ACT_EL_RANGE:
- case ACT_EL_LLRANGE:
- OUT(" <= "); break;
- case ACT_EL_RLRANGE:
- case ACT_EL_ULRANGE:
- OUT(" < "); break;
- default: break;
- }
- switch(ct->range_stop->type) {
- case ATV_INTEGER: OUT("%lld",
- (long long)ct->range_stop->value.v_integer); break;
- case ATV_MIN: OUT("min_%s", s_v); break;
- case ATV_MAX: OUT("max_%s", s_v); break;
- case ATV_FALSE: OUT("0"); break;
- case ATV_TRUE: break;
- default:
- break;
- }
- OUT("))\n");
- break;
- case ACT_EL_EXT:
- OUT("0 /* Extensible (...), but not defined herein */\n");
- break;
- case ACT_CT_SIZE:
- if(mode) {
- OUT("0 /* Invalid constraint at line %d */\n",
- ct->_lineno);
- return -1;
- }
- assert(ct->el_count == 1);
- return emit_single_constraint_check(arg,
- ct->elements[0], ACT_CT_SIZE);
- case ACT_CT_FROM:
- if(mode) {
- OUT("0 /* Invalid constraint at line %d */\n",
- ct->_lineno);
- return -1;
- }
- OUT("check_alphabet_%x(sptr)\n", ct);
- break;
- case ACT_CT_WCOMP:
- case ACT_CT_WCOMPS:
- OUT("%d /* Unsupported constraint at line %d */\n",
- ct->type, ct->_lineno);
- return -1;
- break;
- case ACT_CA_SET:
- OUT("(\n");
- INDENT(+1);
- for(el = 0; el < ct->el_count; el++) {
- if(el) OUT("&& ");
- emit_single_constraint_check(arg,
- ct->elements[el], mode);
- }
- INDENT(-1);
- OUT(")\n");
- break;
- case ACT_CA_CSV:
- OUT("(\n");
- INDENT(+1);
- for(el = 0; el < ct->el_count; el++) {
- if(el) OUT("|| ");
- emit_single_constraint_check(arg,
- ct->elements[el], mode);
- }
- INDENT(-1);
- OUT(")\n");
- break;
- case ACT_CA_UNI:
- OUT("(\n");
- INDENT(+1);
- for(el = 0; el < ct->el_count; el++) {
- if(el) OUT("|| ");
- emit_single_constraint_check(arg,
- ct->elements[el], mode);
- }
- INDENT(-1);
- OUT(")\n");
- break;
- case ACT_CA_INT:
- OUT("(\n");
- INDENT(+1);
- for(el = 0; el < ct->el_count; el++) {
- if(el) OUT("&& ");
- emit_single_constraint_check(arg,
- ct->elements[el], mode);
- }
- INDENT(-1);
- OUT(")\n");
- break;
- case ACT_CA_CRC:
- WARNING("Unsupported component relation constraint at line %d",
- ct->_lineno);
- OUT("%d /* Unsupported component relation constraint "
- "at line %d */\n",
- ct->type, ct->_lineno);
- return -1;
- case ACT_CA_EXC:
- WARNING("Unsupported EXCEPT constraint at line %d",
- ct->_lineno);
- OUT("%d /* Unsupported EXCEPT constraint at line %d */\n",
- ct->type, ct->_lineno);
- return -1;
- }
-
- return 0;
-}
-
-static int
-check_constraint_type_presence(asn1p_constraint_t *ct, enum asn1p_constraint_type_e type) {
- int el;
-
- if(ct == NULL) return 0;
-
- if(ct->type == type) return 1;
-
- if(type == ACT_EL_VALUE) {
- if(ct->type >= ACT_CT_SIZE
- && ct->type <= ACT_CT_WCOMPS)
- /* Values defined further
- * are not really value's values */
- return 0;
- if(ct->type > ACT_EL_VALUE && ct->type < ACT_CT_SIZE)
- return 1; /* Also values */
- }
-
- for(el = 0; el < ct->el_count; el++) {
- if(check_constraint_type_presence(ct->elements[el], type))
- return 1;
- }
-
- return 0;
-}
-
-static int
-emit_alphabet_tables(arg_t *arg, asn1p_constraint_t *ct, int *table) {
- int ch = 0;
- int ch_start = 0;
- int ch_stop = 0;
- int el = 0;
-
- assert(arg && ct);
-
- switch(ct->type) {
- case ACT_INVALID:
- break;
- case ACT_EL_VALUE:
- if(!table) break;
-
- switch(ct->value->type) {
- case ATV_INTEGER:
- if(ct->value->value.v_integer < 0
- || ct->value->value.v_integer > 255) {
- OUT("\n");
- OUT("#error Value %lld out of range "
- "for alphabet character at line %d\n",
- (long long)ct->value->value.v_integer,
- ct->_lineno);
- break;
- } else {
- ch = ct->value->value.v_integer;
- table[ch] = 1;
- }
- break;
- case ATV_STRING:
- for(ch = 0; ch < ct->value->value.string.size; ch++)
- table[ct->value->value.string.buf[ch]] = 1;
- break;
- default:
- OUT("\n");
- WARNING("Invalid alphabet character specification "
- "at line %d", ct->_lineno);
- OUT("#error Invalid alphabet character specification "
- "at line %d\n", ct->_lineno);
- break;
- }
- break;
- case ACT_EL_RANGE:
- case ACT_EL_LLRANGE:
- case ACT_EL_RLRANGE:
- case ACT_EL_ULRANGE:
- if(!table) break;
-
- ch_start = 0;
- ch_stop = 255;
-
- switch(ct->range_start->type) {
- case ATV_INTEGER:
- ch_start = ct->range_start->value.v_integer; break;
- case ATV_MIN: ch_start = 0; break;
- case ATV_MAX: ch_start = 255; break;
- case ATV_STRING:
- if(ct->range_start->value.string.size == 1) {
- ch_start = ct->range_start->value.string.buf[0];
- break;
- }
- /* Fall through */
- default:
- OUT("\n");
- FATAL("Invalid alphabet range constraint "
- "at line %d\n", ct->_lineno);
- OUT("#error Invalid alphabet range constraint "
- "at line %d\n", ct->_lineno);
- return -1;
- }
-
- switch(ct->range_stop->type) {
- case ATV_INTEGER:
- ch_stop = ct->range_stop->value.v_integer; break;
- case ATV_MIN: ch_stop = 0; break;
- case ATV_MAX: ch_stop = 255; break;
- case ATV_STRING:
- if(ct->range_stop->value.string.size == 1) {
- ch_stop = ct->range_stop->value.string.buf[0];
- break;
- }
- /* Fall through */
- default:
- OUT("\n");
- FATAL("Invalid alphabet range constraint "
- "at line %d\n", ct->_lineno);
- OUT("#error Invalid alphabet range constraint "
- "at line %d\n", ct->_lineno);
- break;
- }
-
- switch(ct->type) {
- case ACT_EL_RANGE: break;
- case ACT_EL_RLRANGE: ch_stop--; break;
- case ACT_EL_LLRANGE: ch_start++; break;
- case ACT_EL_ULRANGE: ch_start++; ch_stop--; break;
- default: break;
- }
-
- if(ch_start > ch_stop) {
- WARNING("Empty character range "
- "alphabet constraint at line %d", ct->_lineno);
- OUT_NOINDENT("#warning Empty character range "
- "alphabet constraint at line %d\n", ct->_lineno);
- break;
- }
-
- for(ch = ch_start; ch <= ch_stop; ch++) {
- if(ch < 0 || ch > 255) continue;
- table[ch] = 1;
- }
-
- break;
- case ACT_EL_EXT:
- break;
- case ACT_CT_SIZE:
- break;
- case ACT_CT_FROM:
- if(table) {
- OUT("#error Nested FROM in subtype constraints\n");
- return -1;
- } else {
- table = alloca(256 * sizeof(table[0]));
- memset(table, 0, 256 * sizeof(table[0]));
-
- for(el = 0; el < ct->el_count; el++) {
- emit_alphabet_tables(arg, ct->elements[el],
- table);
- }
- OUT("static int alphabet_table_%x[256] = {\n", ct);
- for(ch = 0; ch < 256; ch++) {
- OUT("%d,", table[ch]?1:0);
- if(!((ch+1) % 16)) {
- if(ch) {
- int c;
- OUT("\t/* ");
- for(c = ch - 16; c < ch; c++) {
- if(table[c]) {
- if(c > 0x20
- && c < 0x80)
- OUT("%c", c);
- else
- OUT(".", c);
- } else {
- OUT(" ");
- }
- }
- OUT(" */");
- }
- OUT("\n");
- }
- }
- OUT("};\n");
- OUT("static int check_alphabet_%x(const void *sptr) {\n", ct);
- INDENT(+1);
- OUT("int *table = alphabet_table_%x;\n", ct);
- emit_alphabet_check_cycle(arg);
- OUT("return 1;\n");
- INDENT(-1);
- OUT("};\n");
- }
- break;
- case ACT_CT_WCOMP:
- case ACT_CT_WCOMPS:
- break;
- case ACT_CA_CRC:
- break;
- case ACT_CA_SET:
- case ACT_CA_CSV:
- case ACT_CA_UNI:
- for(el = 0; el < ct->el_count; el++)
- emit_alphabet_tables(arg, ct->elements[el], table);
- break;
- case ACT_CA_INT:
- if(table) {
- int table2[256];
-
- assert(ct->el_count >= 1);
- emit_alphabet_tables(arg, ct->elements[0], table);
- for(el = 1; el < ct->el_count; el++) {
- memset(table2, 0, sizeof(table2));
- emit_alphabet_tables(arg,
- ct->elements[el], table2);
- /* Intersection */
- for(ch = 0; ch < 256; ch++) {
- if(table2[ch] == 0)
- table[ch] = 0;
- }
- }
- } else {
- for(el = 0; el < ct->el_count; el++)
- emit_alphabet_tables(arg, ct->elements[el], 0);
- }
-
- break;
- case ACT_CA_EXC:
- OUT("EXC\n");
- if(table) {
- int table2[256];
-
- assert(ct->el_count >= 1);
- emit_alphabet_tables(arg, ct->elements[0], table);
- for(el = 1; el < ct->el_count; el++) {
- memset(table2, 0, sizeof(table2));
- emit_alphabet_tables(arg,
- ct->elements[el], table2);
- /* Exclusion */
- for(ch = 0; ch < 256; ch++) {
- if(table2[ch])
- table[ch] = 0;
- }
- }
- } else {
- for(el = 0; el < ct->el_count; el++)
- emit_alphabet_tables(arg, ct->elements[el], 0);
- }
- break;
- }
-
- return 0;
-}
-
-static int
-emit_alphabet_check_cycle(arg_t *arg) {
- asn1p_expr_type_e etype;
-
- etype = _find_terminal_type(arg);
- if(!(etype & ASN_STRING_MASK)
- && !(etype == ASN_BASIC_OCTET_STRING)) {
- OUT("#error Cannot apply FROM constraint to ASN.1 type %s\n",
- ASN_EXPR_TYPE2STR(etype));
- return -1;
- }
-
- OUT("/* The underlying type is %s */\n",
- ASN_EXPR_TYPE2STR(etype));
- OUT("const %s_t *st = sptr;\n", MKID(arg->expr->Identifier));
-
- switch(etype) {
- case ASN_STRING_UTF8String:
- OUT("uint8_t *ch = st->buf;\n");
- OUT("uint8_t *end = ch + st->size;\n");
- OUT("\n");
- OUT("for(; ch < end; ch++) {\n");
- INDENT(+1);
- OUT("if(*ch >= 0x80 || !table[*ch]) return 0;\n");
- INDENT(-1);
- OUT("}\n");
- break;
- case ASN_STRING_UniversalString:
- OUT("uint32_t *ch = st->buf;\n");
- OUT("uint32_t *end = ch + st->size;\n");
- OUT("\n");
- OUT("for(; ch < end; ch++) {\n");
- INDENT(+1);
- OUT("uint32_t wc = (((uint8_t *)ch)[0] << 24)\n");
- OUT("\t\t| (((uint8_t *)ch)[1] << 16)\n");
- OUT("\t\t| (((uint8_t *)ch)[2] << 8)\n");
- OUT("\t\t| ((uint8_t *)ch)[3]\n");
- OUT("if(wc > 255 || !table[wc]) return 0;\n");
- INDENT(-1);
- OUT("}\n");
- OUT("if(ch != end) return 0; /* (size%4)! */\n");
- break;
- case ASN_STRING_BMPString:
- OUT("uint16_t *ch = st->buf;\n");
- OUT("uint16_t *end = ch + st->size;\n");
- OUT("\n");
- OUT("for(; ch < end; ch++) {\n");
- INDENT(+1);
- OUT("uint16_t wc = (((uint8_t *)ch)[0] << 8)\n");
- OUT("\t\t| ((uint8_t *)ch)[1];\n");
- OUT("if(wc > 255 || !table[wc]) return 0;\n");
- INDENT(-1);
- OUT("}\n");
- OUT("if(ch != end) return 0; /* (size%2)! */\n");
- break;
- case ASN_BASIC_OCTET_STRING:
- default:
- OUT("uint8_t *ch = st->buf;\n");
- OUT("uint8_t *end = ch + st->size;\n");
- OUT("\n");
- OUT("for(; ch < end; ch++) {\n");
- INDENT(+1);
- OUT("if(!table[*ch]) return 0;\n");
- INDENT(-1);
- OUT("}\n");
- break;
- }
-
- return 0;
-}
-
-static int
-emit_size_determination_code(arg_t *arg) {
- asn1p_expr_type_e etype = _find_terminal_type(arg);
-
- switch(etype) {
- case ASN_BASIC_BIT_STRING:
- OUT("if(st->size > 0) {\n");
- OUT("\t/* Size in bits */\n");
- OUT("\tsize = (st->size - 1) - (st->buf[0] & 0x7);\n");
- OUT("} else {\n");
- OUT("\tsize = 0;\n");
- OUT("}\n");
- break;
- case ASN_STRING_UniversalString:
- OUT("size = st->size >> 2;\t/* 4 byte per character */\n");
- break;
- case ASN_STRING_BMPString:
- OUT("size = st->size >> 1;\t/* 2 byte per character */\n");
- break;
- case ASN_STRING_UTF8String:
- OUT("size = UTF8String_length(st, td->name, app_errlog, app_key);\n");
- OUT("if(size == (size_t)-1) return -1;\n");
- break;
- default:
- if((etype & ASN_STRING_MASK)
- || etype == ASN_BASIC_OCTET_STRING) {
- OUT("size = st->size;\n");
- break;
- } else {
- const char *type_name = ASN_EXPR_TYPE2STR(etype);
- if(!type_name) type_name = arg->expr->Identifier;
- WARNING("SIZE constraint is not defined for %s",
- type_name);
- OUT_NOINDENT("#warning SIZE constraint "
- "not defined for %s!\n", type_name);
- OUT("size = st->size;\n");
- }
- return -1;
- }
-
- return 0;
-}
-
-static int
-emit_value_determination_code(arg_t *arg) {
- asn1p_expr_type_e etype = _find_terminal_type(arg);
-
- switch(etype) {
- case ASN_BASIC_INTEGER:
- case ASN_BASIC_ENUMERATED:
- if(arg->flags & A1C_USE_NATIVE_INTEGERS) {
- OUT("value = *(int *)st;\n");
- } else {
- OUT("if(asn1_INTEGER2long(st, &value)) {\n");
- INDENT(+1);
- OUT("_ASN_ERRLOG(app_errlog, app_key,\n");
- OUT("\t\"%%s: value too large\", td->name);\n");
- OUT("return -1;\n");
- INDENT(-1);
- OUT("}\n");
- }
- break;
- case ASN_BASIC_BOOLEAN:
- OUT("value = st->value;\n");
- break;
- default:
- WARNING("Value cannot be determined "
- "for constraint check for %s at line %d\n",
- arg->expr->Identifier, arg->expr->_lineno);
- OUT("#error Value cannot be determined for %s at %d\n",
- arg->expr->Identifier, arg->expr->_lineno);
- break;
- }
-
- return 0;
-}
-
-static long compute_min_size(arg_t *arg) { return compute_xxx_size(arg, 0); }
-static long compute_max_size(arg_t *arg) { return compute_xxx_size(arg, 1); }
-
-static long compute_xxx_size(arg_t *arg, int _max) {
- asn1p_expr_type_e etype;
- long basic_max = 0x7fffffff;
- long basic_min = 0x80000000;
- long svalue = 0;
-
- etype = _find_terminal_type(arg);
- switch(etype) {
- case ASN_BASIC_BIT_STRING:
- svalue = _max?basic_max/8:0;
- break;
- case ASN_STRING_UTF8String:
- svalue = _max?basic_max/6:0;
- break;
- case ASN_STRING_UniversalString:
- svalue = _max?basic_max/4:0;
- break;
- case ASN_STRING_BMPString:
- svalue = _max?basic_max/2:0;
- break;
- case ASN_BASIC_OCTET_STRING:
- svalue = _max?basic_max:0;
- break;
- default:
- if((etype & ASN_STRING_MASK)) {
- svalue = _max?basic_max:0;
- break;
- }
- svalue = _max?basic_max:basic_min;
- break;
- }
-
- return svalue;
-}
-
-static asn1p_expr_type_e
-_find_terminal_type(arg_t *arg) {
- asn1p_expr_t *expr;
- expr = asn1f_find_terminal_type_ex(arg->asn, arg->mod, arg->expr, NULL);
- if(expr) return expr->expr_type;
- return A1TC_INVALID;
-}
-