forked from Qortal/Brooklyn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1611 lines
35 KiB
1611 lines
35 KiB
// SPDX-License-Identifier: GPL-2.0-or-later |
|
/* Simplified ASN.1 notation parser |
|
* |
|
* Copyright (C) 2012 Red Hat, Inc. All Rights Reserved. |
|
* Written by David Howells ([email protected]) |
|
*/ |
|
|
|
#include <stdarg.h> |
|
#include <stdio.h> |
|
#include <stdlib.h> |
|
#include <stdint.h> |
|
#include <stdbool.h> |
|
#include <string.h> |
|
#include <ctype.h> |
|
#include <unistd.h> |
|
#include <fcntl.h> |
|
#include <sys/stat.h> |
|
#include <linux/asn1_ber_bytecode.h> |
|
|
|
enum token_type { |
|
DIRECTIVE_ABSENT, |
|
DIRECTIVE_ALL, |
|
DIRECTIVE_ANY, |
|
DIRECTIVE_APPLICATION, |
|
DIRECTIVE_AUTOMATIC, |
|
DIRECTIVE_BEGIN, |
|
DIRECTIVE_BIT, |
|
DIRECTIVE_BMPString, |
|
DIRECTIVE_BOOLEAN, |
|
DIRECTIVE_BY, |
|
DIRECTIVE_CHARACTER, |
|
DIRECTIVE_CHOICE, |
|
DIRECTIVE_CLASS, |
|
DIRECTIVE_COMPONENT, |
|
DIRECTIVE_COMPONENTS, |
|
DIRECTIVE_CONSTRAINED, |
|
DIRECTIVE_CONTAINING, |
|
DIRECTIVE_DEFAULT, |
|
DIRECTIVE_DEFINED, |
|
DIRECTIVE_DEFINITIONS, |
|
DIRECTIVE_EMBEDDED, |
|
DIRECTIVE_ENCODED, |
|
DIRECTIVE_ENCODING_CONTROL, |
|
DIRECTIVE_END, |
|
DIRECTIVE_ENUMERATED, |
|
DIRECTIVE_EXCEPT, |
|
DIRECTIVE_EXPLICIT, |
|
DIRECTIVE_EXPORTS, |
|
DIRECTIVE_EXTENSIBILITY, |
|
DIRECTIVE_EXTERNAL, |
|
DIRECTIVE_FALSE, |
|
DIRECTIVE_FROM, |
|
DIRECTIVE_GeneralString, |
|
DIRECTIVE_GeneralizedTime, |
|
DIRECTIVE_GraphicString, |
|
DIRECTIVE_IA5String, |
|
DIRECTIVE_IDENTIFIER, |
|
DIRECTIVE_IMPLICIT, |
|
DIRECTIVE_IMPLIED, |
|
DIRECTIVE_IMPORTS, |
|
DIRECTIVE_INCLUDES, |
|
DIRECTIVE_INSTANCE, |
|
DIRECTIVE_INSTRUCTIONS, |
|
DIRECTIVE_INTEGER, |
|
DIRECTIVE_INTERSECTION, |
|
DIRECTIVE_ISO646String, |
|
DIRECTIVE_MAX, |
|
DIRECTIVE_MIN, |
|
DIRECTIVE_MINUS_INFINITY, |
|
DIRECTIVE_NULL, |
|
DIRECTIVE_NumericString, |
|
DIRECTIVE_OBJECT, |
|
DIRECTIVE_OCTET, |
|
DIRECTIVE_OF, |
|
DIRECTIVE_OPTIONAL, |
|
DIRECTIVE_ObjectDescriptor, |
|
DIRECTIVE_PATTERN, |
|
DIRECTIVE_PDV, |
|
DIRECTIVE_PLUS_INFINITY, |
|
DIRECTIVE_PRESENT, |
|
DIRECTIVE_PRIVATE, |
|
DIRECTIVE_PrintableString, |
|
DIRECTIVE_REAL, |
|
DIRECTIVE_RELATIVE_OID, |
|
DIRECTIVE_SEQUENCE, |
|
DIRECTIVE_SET, |
|
DIRECTIVE_SIZE, |
|
DIRECTIVE_STRING, |
|
DIRECTIVE_SYNTAX, |
|
DIRECTIVE_T61String, |
|
DIRECTIVE_TAGS, |
|
DIRECTIVE_TRUE, |
|
DIRECTIVE_TeletexString, |
|
DIRECTIVE_UNION, |
|
DIRECTIVE_UNIQUE, |
|
DIRECTIVE_UNIVERSAL, |
|
DIRECTIVE_UTCTime, |
|
DIRECTIVE_UTF8String, |
|
DIRECTIVE_UniversalString, |
|
DIRECTIVE_VideotexString, |
|
DIRECTIVE_VisibleString, |
|
DIRECTIVE_WITH, |
|
NR__DIRECTIVES, |
|
TOKEN_ASSIGNMENT = NR__DIRECTIVES, |
|
TOKEN_OPEN_CURLY, |
|
TOKEN_CLOSE_CURLY, |
|
TOKEN_OPEN_SQUARE, |
|
TOKEN_CLOSE_SQUARE, |
|
TOKEN_OPEN_ACTION, |
|
TOKEN_CLOSE_ACTION, |
|
TOKEN_COMMA, |
|
TOKEN_NUMBER, |
|
TOKEN_TYPE_NAME, |
|
TOKEN_ELEMENT_NAME, |
|
NR__TOKENS |
|
}; |
|
|
|
static const unsigned char token_to_tag[NR__TOKENS] = { |
|
/* EOC goes first */ |
|
[DIRECTIVE_BOOLEAN] = ASN1_BOOL, |
|
[DIRECTIVE_INTEGER] = ASN1_INT, |
|
[DIRECTIVE_BIT] = ASN1_BTS, |
|
[DIRECTIVE_OCTET] = ASN1_OTS, |
|
[DIRECTIVE_NULL] = ASN1_NULL, |
|
[DIRECTIVE_OBJECT] = ASN1_OID, |
|
[DIRECTIVE_ObjectDescriptor] = ASN1_ODE, |
|
[DIRECTIVE_EXTERNAL] = ASN1_EXT, |
|
[DIRECTIVE_REAL] = ASN1_REAL, |
|
[DIRECTIVE_ENUMERATED] = ASN1_ENUM, |
|
[DIRECTIVE_EMBEDDED] = 0, |
|
[DIRECTIVE_UTF8String] = ASN1_UTF8STR, |
|
[DIRECTIVE_RELATIVE_OID] = ASN1_RELOID, |
|
/* 14 */ |
|
/* 15 */ |
|
[DIRECTIVE_SEQUENCE] = ASN1_SEQ, |
|
[DIRECTIVE_SET] = ASN1_SET, |
|
[DIRECTIVE_NumericString] = ASN1_NUMSTR, |
|
[DIRECTIVE_PrintableString] = ASN1_PRNSTR, |
|
[DIRECTIVE_T61String] = ASN1_TEXSTR, |
|
[DIRECTIVE_TeletexString] = ASN1_TEXSTR, |
|
[DIRECTIVE_VideotexString] = ASN1_VIDSTR, |
|
[DIRECTIVE_IA5String] = ASN1_IA5STR, |
|
[DIRECTIVE_UTCTime] = ASN1_UNITIM, |
|
[DIRECTIVE_GeneralizedTime] = ASN1_GENTIM, |
|
[DIRECTIVE_GraphicString] = ASN1_GRASTR, |
|
[DIRECTIVE_VisibleString] = ASN1_VISSTR, |
|
[DIRECTIVE_GeneralString] = ASN1_GENSTR, |
|
[DIRECTIVE_UniversalString] = ASN1_UNITIM, |
|
[DIRECTIVE_CHARACTER] = ASN1_CHRSTR, |
|
[DIRECTIVE_BMPString] = ASN1_BMPSTR, |
|
}; |
|
|
|
static const char asn1_classes[4][5] = { |
|
[ASN1_UNIV] = "UNIV", |
|
[ASN1_APPL] = "APPL", |
|
[ASN1_CONT] = "CONT", |
|
[ASN1_PRIV] = "PRIV" |
|
}; |
|
|
|
static const char asn1_methods[2][5] = { |
|
[ASN1_UNIV] = "PRIM", |
|
[ASN1_APPL] = "CONS" |
|
}; |
|
|
|
static const char *const asn1_universal_tags[32] = { |
|
"EOC", |
|
"BOOL", |
|
"INT", |
|
"BTS", |
|
"OTS", |
|
"NULL", |
|
"OID", |
|
"ODE", |
|
"EXT", |
|
"REAL", |
|
"ENUM", |
|
"EPDV", |
|
"UTF8STR", |
|
"RELOID", |
|
NULL, /* 14 */ |
|
NULL, /* 15 */ |
|
"SEQ", |
|
"SET", |
|
"NUMSTR", |
|
"PRNSTR", |
|
"TEXSTR", |
|
"VIDSTR", |
|
"IA5STR", |
|
"UNITIM", |
|
"GENTIM", |
|
"GRASTR", |
|
"VISSTR", |
|
"GENSTR", |
|
"UNISTR", |
|
"CHRSTR", |
|
"BMPSTR", |
|
NULL /* 31 */ |
|
}; |
|
|
|
static const char *filename; |
|
static const char *grammar_name; |
|
static const char *outputname; |
|
static const char *headername; |
|
|
|
static const char *const directives[NR__DIRECTIVES] = { |
|
#define _(X) [DIRECTIVE_##X] = #X |
|
_(ABSENT), |
|
_(ALL), |
|
_(ANY), |
|
_(APPLICATION), |
|
_(AUTOMATIC), |
|
_(BEGIN), |
|
_(BIT), |
|
_(BMPString), |
|
_(BOOLEAN), |
|
_(BY), |
|
_(CHARACTER), |
|
_(CHOICE), |
|
_(CLASS), |
|
_(COMPONENT), |
|
_(COMPONENTS), |
|
_(CONSTRAINED), |
|
_(CONTAINING), |
|
_(DEFAULT), |
|
_(DEFINED), |
|
_(DEFINITIONS), |
|
_(EMBEDDED), |
|
_(ENCODED), |
|
[DIRECTIVE_ENCODING_CONTROL] = "ENCODING-CONTROL", |
|
_(END), |
|
_(ENUMERATED), |
|
_(EXCEPT), |
|
_(EXPLICIT), |
|
_(EXPORTS), |
|
_(EXTENSIBILITY), |
|
_(EXTERNAL), |
|
_(FALSE), |
|
_(FROM), |
|
_(GeneralString), |
|
_(GeneralizedTime), |
|
_(GraphicString), |
|
_(IA5String), |
|
_(IDENTIFIER), |
|
_(IMPLICIT), |
|
_(IMPLIED), |
|
_(IMPORTS), |
|
_(INCLUDES), |
|
_(INSTANCE), |
|
_(INSTRUCTIONS), |
|
_(INTEGER), |
|
_(INTERSECTION), |
|
_(ISO646String), |
|
_(MAX), |
|
_(MIN), |
|
[DIRECTIVE_MINUS_INFINITY] = "MINUS-INFINITY", |
|
[DIRECTIVE_NULL] = "NULL", |
|
_(NumericString), |
|
_(OBJECT), |
|
_(OCTET), |
|
_(OF), |
|
_(OPTIONAL), |
|
_(ObjectDescriptor), |
|
_(PATTERN), |
|
_(PDV), |
|
[DIRECTIVE_PLUS_INFINITY] = "PLUS-INFINITY", |
|
_(PRESENT), |
|
_(PRIVATE), |
|
_(PrintableString), |
|
_(REAL), |
|
[DIRECTIVE_RELATIVE_OID] = "RELATIVE-OID", |
|
_(SEQUENCE), |
|
_(SET), |
|
_(SIZE), |
|
_(STRING), |
|
_(SYNTAX), |
|
_(T61String), |
|
_(TAGS), |
|
_(TRUE), |
|
_(TeletexString), |
|
_(UNION), |
|
_(UNIQUE), |
|
_(UNIVERSAL), |
|
_(UTCTime), |
|
_(UTF8String), |
|
_(UniversalString), |
|
_(VideotexString), |
|
_(VisibleString), |
|
_(WITH) |
|
}; |
|
|
|
struct action { |
|
struct action *next; |
|
char *name; |
|
unsigned char index; |
|
}; |
|
|
|
static struct action *action_list; |
|
static unsigned nr_actions; |
|
|
|
struct token { |
|
unsigned short line; |
|
enum token_type token_type : 8; |
|
unsigned char size; |
|
struct action *action; |
|
char *content; |
|
struct type *type; |
|
}; |
|
|
|
static struct token *token_list; |
|
static unsigned nr_tokens; |
|
static bool verbose_opt; |
|
static bool debug_opt; |
|
|
|
#define verbose(fmt, ...) do { if (verbose_opt) printf(fmt, ## __VA_ARGS__); } while (0) |
|
#define debug(fmt, ...) do { if (debug_opt) printf(fmt, ## __VA_ARGS__); } while (0) |
|
|
|
static int directive_compare(const void *_key, const void *_pdir) |
|
{ |
|
const struct token *token = _key; |
|
const char *const *pdir = _pdir, *dir = *pdir; |
|
size_t dlen, clen; |
|
int val; |
|
|
|
dlen = strlen(dir); |
|
clen = (dlen < token->size) ? dlen : token->size; |
|
|
|
//debug("cmp(%s,%s) = ", token->content, dir); |
|
|
|
val = memcmp(token->content, dir, clen); |
|
if (val != 0) { |
|
//debug("%d [cmp]\n", val); |
|
return val; |
|
} |
|
|
|
if (dlen == token->size) { |
|
//debug("0\n"); |
|
return 0; |
|
} |
|
//debug("%d\n", (int)dlen - (int)token->size); |
|
return dlen - token->size; /* shorter -> negative */ |
|
} |
|
|
|
/* |
|
* Tokenise an ASN.1 grammar |
|
*/ |
|
static void tokenise(char *buffer, char *end) |
|
{ |
|
struct token *tokens; |
|
char *line, *nl, *start, *p, *q; |
|
unsigned tix, lineno; |
|
|
|
/* Assume we're going to have half as many tokens as we have |
|
* characters |
|
*/ |
|
token_list = tokens = calloc((end - buffer) / 2, sizeof(struct token)); |
|
if (!tokens) { |
|
perror(NULL); |
|
exit(1); |
|
} |
|
tix = 0; |
|
|
|
lineno = 0; |
|
while (buffer < end) { |
|
/* First of all, break out a line */ |
|
lineno++; |
|
line = buffer; |
|
nl = memchr(line, '\n', end - buffer); |
|
if (!nl) { |
|
buffer = nl = end; |
|
} else { |
|
buffer = nl + 1; |
|
*nl = '\0'; |
|
} |
|
|
|
/* Remove "--" comments */ |
|
p = line; |
|
next_comment: |
|
while ((p = memchr(p, '-', nl - p))) { |
|
if (p[1] == '-') { |
|
/* Found a comment; see if there's a terminator */ |
|
q = p + 2; |
|
while ((q = memchr(q, '-', nl - q))) { |
|
if (q[1] == '-') { |
|
/* There is - excise the comment */ |
|
q += 2; |
|
memmove(p, q, nl - q); |
|
goto next_comment; |
|
} |
|
q++; |
|
} |
|
*p = '\0'; |
|
nl = p; |
|
break; |
|
} else { |
|
p++; |
|
} |
|
} |
|
|
|
p = line; |
|
while (p < nl) { |
|
/* Skip white space */ |
|
while (p < nl && isspace(*p)) |
|
*(p++) = 0; |
|
if (p >= nl) |
|
break; |
|
|
|
tokens[tix].line = lineno; |
|
start = p; |
|
|
|
/* Handle string tokens */ |
|
if (isalpha(*p)) { |
|
const char **dir; |
|
|
|
/* Can be a directive, type name or element |
|
* name. Find the end of the name. |
|
*/ |
|
q = p + 1; |
|
while (q < nl && (isalnum(*q) || *q == '-' || *q == '_')) |
|
q++; |
|
tokens[tix].size = q - p; |
|
p = q; |
|
|
|
tokens[tix].content = malloc(tokens[tix].size + 1); |
|
if (!tokens[tix].content) { |
|
perror(NULL); |
|
exit(1); |
|
} |
|
memcpy(tokens[tix].content, start, tokens[tix].size); |
|
tokens[tix].content[tokens[tix].size] = 0; |
|
|
|
/* If it begins with a lowercase letter then |
|
* it's an element name |
|
*/ |
|
if (islower(tokens[tix].content[0])) { |
|
tokens[tix++].token_type = TOKEN_ELEMENT_NAME; |
|
continue; |
|
} |
|
|
|
/* Otherwise we need to search the directive |
|
* table |
|
*/ |
|
dir = bsearch(&tokens[tix], directives, |
|
sizeof(directives) / sizeof(directives[1]), |
|
sizeof(directives[1]), |
|
directive_compare); |
|
if (dir) { |
|
tokens[tix++].token_type = dir - directives; |
|
continue; |
|
} |
|
|
|
tokens[tix++].token_type = TOKEN_TYPE_NAME; |
|
continue; |
|
} |
|
|
|
/* Handle numbers */ |
|
if (isdigit(*p)) { |
|
/* Find the end of the number */ |
|
q = p + 1; |
|
while (q < nl && (isdigit(*q))) |
|
q++; |
|
tokens[tix].size = q - p; |
|
p = q; |
|
tokens[tix].content = malloc(tokens[tix].size + 1); |
|
if (!tokens[tix].content) { |
|
perror(NULL); |
|
exit(1); |
|
} |
|
memcpy(tokens[tix].content, start, tokens[tix].size); |
|
tokens[tix].content[tokens[tix].size] = 0; |
|
tokens[tix++].token_type = TOKEN_NUMBER; |
|
continue; |
|
} |
|
|
|
if (nl - p >= 3) { |
|
if (memcmp(p, "::=", 3) == 0) { |
|
p += 3; |
|
tokens[tix].size = 3; |
|
tokens[tix].content = "::="; |
|
tokens[tix++].token_type = TOKEN_ASSIGNMENT; |
|
continue; |
|
} |
|
} |
|
|
|
if (nl - p >= 2) { |
|
if (memcmp(p, "({", 2) == 0) { |
|
p += 2; |
|
tokens[tix].size = 2; |
|
tokens[tix].content = "({"; |
|
tokens[tix++].token_type = TOKEN_OPEN_ACTION; |
|
continue; |
|
} |
|
if (memcmp(p, "})", 2) == 0) { |
|
p += 2; |
|
tokens[tix].size = 2; |
|
tokens[tix].content = "})"; |
|
tokens[tix++].token_type = TOKEN_CLOSE_ACTION; |
|
continue; |
|
} |
|
} |
|
|
|
if (nl - p >= 1) { |
|
tokens[tix].size = 1; |
|
switch (*p) { |
|
case '{': |
|
p += 1; |
|
tokens[tix].content = "{"; |
|
tokens[tix++].token_type = TOKEN_OPEN_CURLY; |
|
continue; |
|
case '}': |
|
p += 1; |
|
tokens[tix].content = "}"; |
|
tokens[tix++].token_type = TOKEN_CLOSE_CURLY; |
|
continue; |
|
case '[': |
|
p += 1; |
|
tokens[tix].content = "["; |
|
tokens[tix++].token_type = TOKEN_OPEN_SQUARE; |
|
continue; |
|
case ']': |
|
p += 1; |
|
tokens[tix].content = "]"; |
|
tokens[tix++].token_type = TOKEN_CLOSE_SQUARE; |
|
continue; |
|
case ',': |
|
p += 1; |
|
tokens[tix].content = ","; |
|
tokens[tix++].token_type = TOKEN_COMMA; |
|
continue; |
|
default: |
|
break; |
|
} |
|
} |
|
|
|
fprintf(stderr, "%s:%u: Unknown character in grammar: '%c'\n", |
|
filename, lineno, *p); |
|
exit(1); |
|
} |
|
} |
|
|
|
nr_tokens = tix; |
|
verbose("Extracted %u tokens\n", nr_tokens); |
|
|
|
#if 0 |
|
{ |
|
int n; |
|
for (n = 0; n < nr_tokens; n++) |
|
debug("Token %3u: '%s'\n", n, token_list[n].content); |
|
} |
|
#endif |
|
} |
|
|
|
static void build_type_list(void); |
|
static void parse(void); |
|
static void dump_elements(void); |
|
static void render(FILE *out, FILE *hdr); |
|
|
|
/* |
|
* |
|
*/ |
|
int main(int argc, char **argv) |
|
{ |
|
struct stat st; |
|
ssize_t readlen; |
|
FILE *out, *hdr; |
|
char *buffer, *p; |
|
char *kbuild_verbose; |
|
int fd; |
|
|
|
kbuild_verbose = getenv("KBUILD_VERBOSE"); |
|
if (kbuild_verbose) |
|
verbose_opt = atoi(kbuild_verbose); |
|
|
|
while (argc > 4) { |
|
if (strcmp(argv[1], "-v") == 0) |
|
verbose_opt = true; |
|
else if (strcmp(argv[1], "-d") == 0) |
|
debug_opt = true; |
|
else |
|
break; |
|
memmove(&argv[1], &argv[2], (argc - 2) * sizeof(char *)); |
|
argc--; |
|
} |
|
|
|
if (argc != 4) { |
|
fprintf(stderr, "Format: %s [-v] [-d] <grammar-file> <c-file> <hdr-file>\n", |
|
argv[0]); |
|
exit(2); |
|
} |
|
|
|
filename = argv[1]; |
|
outputname = argv[2]; |
|
headername = argv[3]; |
|
|
|
fd = open(filename, O_RDONLY); |
|
if (fd < 0) { |
|
perror(filename); |
|
exit(1); |
|
} |
|
|
|
if (fstat(fd, &st) < 0) { |
|
perror(filename); |
|
exit(1); |
|
} |
|
|
|
if (!(buffer = malloc(st.st_size + 1))) { |
|
perror(NULL); |
|
exit(1); |
|
} |
|
|
|
if ((readlen = read(fd, buffer, st.st_size)) < 0) { |
|
perror(filename); |
|
exit(1); |
|
} |
|
|
|
if (close(fd) < 0) { |
|
perror(filename); |
|
exit(1); |
|
} |
|
|
|
if (readlen != st.st_size) { |
|
fprintf(stderr, "%s: Short read\n", filename); |
|
exit(1); |
|
} |
|
|
|
p = strrchr(argv[1], '/'); |
|
p = p ? p + 1 : argv[1]; |
|
grammar_name = strdup(p); |
|
if (!p) { |
|
perror(NULL); |
|
exit(1); |
|
} |
|
p = strchr(grammar_name, '.'); |
|
if (p) |
|
*p = '\0'; |
|
|
|
buffer[readlen] = 0; |
|
tokenise(buffer, buffer + readlen); |
|
build_type_list(); |
|
parse(); |
|
dump_elements(); |
|
|
|
out = fopen(outputname, "w"); |
|
if (!out) { |
|
perror(outputname); |
|
exit(1); |
|
} |
|
|
|
hdr = fopen(headername, "w"); |
|
if (!hdr) { |
|
perror(headername); |
|
exit(1); |
|
} |
|
|
|
render(out, hdr); |
|
|
|
if (fclose(out) < 0) { |
|
perror(outputname); |
|
exit(1); |
|
} |
|
|
|
if (fclose(hdr) < 0) { |
|
perror(headername); |
|
exit(1); |
|
} |
|
|
|
return 0; |
|
} |
|
|
|
enum compound { |
|
NOT_COMPOUND, |
|
SET, |
|
SET_OF, |
|
SEQUENCE, |
|
SEQUENCE_OF, |
|
CHOICE, |
|
ANY, |
|
TYPE_REF, |
|
TAG_OVERRIDE |
|
}; |
|
|
|
struct element { |
|
struct type *type_def; |
|
struct token *name; |
|
struct token *type; |
|
struct action *action; |
|
struct element *children; |
|
struct element *next; |
|
struct element *render_next; |
|
struct element *list_next; |
|
uint8_t n_elements; |
|
enum compound compound : 8; |
|
enum asn1_class class : 8; |
|
enum asn1_method method : 8; |
|
uint8_t tag; |
|
unsigned entry_index; |
|
unsigned flags; |
|
#define ELEMENT_IMPLICIT 0x0001 |
|
#define ELEMENT_EXPLICIT 0x0002 |
|
#define ELEMENT_TAG_SPECIFIED 0x0004 |
|
#define ELEMENT_RENDERED 0x0008 |
|
#define ELEMENT_SKIPPABLE 0x0010 |
|
#define ELEMENT_CONDITIONAL 0x0020 |
|
}; |
|
|
|
struct type { |
|
struct token *name; |
|
struct token *def; |
|
struct element *element; |
|
unsigned ref_count; |
|
unsigned flags; |
|
#define TYPE_STOP_MARKER 0x0001 |
|
#define TYPE_BEGIN 0x0002 |
|
}; |
|
|
|
static struct type *type_list; |
|
static struct type **type_index; |
|
static unsigned nr_types; |
|
|
|
static int type_index_compare(const void *_a, const void *_b) |
|
{ |
|
const struct type *const *a = _a, *const *b = _b; |
|
|
|
if ((*a)->name->size != (*b)->name->size) |
|
return (*a)->name->size - (*b)->name->size; |
|
else |
|
return memcmp((*a)->name->content, (*b)->name->content, |
|
(*a)->name->size); |
|
} |
|
|
|
static int type_finder(const void *_key, const void *_ti) |
|
{ |
|
const struct token *token = _key; |
|
const struct type *const *ti = _ti; |
|
const struct type *type = *ti; |
|
|
|
if (token->size != type->name->size) |
|
return token->size - type->name->size; |
|
else |
|
return memcmp(token->content, type->name->content, |
|
token->size); |
|
} |
|
|
|
/* |
|
* Build up a list of types and a sorted index to that list. |
|
*/ |
|
static void build_type_list(void) |
|
{ |
|
struct type *types; |
|
unsigned nr, t, n; |
|
|
|
nr = 0; |
|
for (n = 0; n < nr_tokens - 1; n++) |
|
if (token_list[n + 0].token_type == TOKEN_TYPE_NAME && |
|
token_list[n + 1].token_type == TOKEN_ASSIGNMENT) |
|
nr++; |
|
|
|
if (nr == 0) { |
|
fprintf(stderr, "%s: No defined types\n", filename); |
|
exit(1); |
|
} |
|
|
|
nr_types = nr; |
|
types = type_list = calloc(nr + 1, sizeof(type_list[0])); |
|
if (!type_list) { |
|
perror(NULL); |
|
exit(1); |
|
} |
|
type_index = calloc(nr, sizeof(type_index[0])); |
|
if (!type_index) { |
|
perror(NULL); |
|
exit(1); |
|
} |
|
|
|
t = 0; |
|
types[t].flags |= TYPE_BEGIN; |
|
for (n = 0; n < nr_tokens - 1; n++) { |
|
if (token_list[n + 0].token_type == TOKEN_TYPE_NAME && |
|
token_list[n + 1].token_type == TOKEN_ASSIGNMENT) { |
|
types[t].name = &token_list[n]; |
|
type_index[t] = &types[t]; |
|
t++; |
|
} |
|
} |
|
types[t].name = &token_list[n + 1]; |
|
types[t].flags |= TYPE_STOP_MARKER; |
|
|
|
qsort(type_index, nr, sizeof(type_index[0]), type_index_compare); |
|
|
|
verbose("Extracted %u types\n", nr_types); |
|
#if 0 |
|
for (n = 0; n < nr_types; n++) { |
|
struct type *type = type_index[n]; |
|
debug("- %*.*s\n", type->name->content); |
|
} |
|
#endif |
|
} |
|
|
|
static struct element *parse_type(struct token **_cursor, struct token *stop, |
|
struct token *name); |
|
|
|
/* |
|
* Parse the token stream |
|
*/ |
|
static void parse(void) |
|
{ |
|
struct token *cursor; |
|
struct type *type; |
|
|
|
/* Parse one type definition statement at a time */ |
|
type = type_list; |
|
do { |
|
cursor = type->name; |
|
|
|
if (cursor[0].token_type != TOKEN_TYPE_NAME || |
|
cursor[1].token_type != TOKEN_ASSIGNMENT) |
|
abort(); |
|
cursor += 2; |
|
|
|
type->element = parse_type(&cursor, type[1].name, NULL); |
|
type->element->type_def = type; |
|
|
|
if (cursor != type[1].name) { |
|
fprintf(stderr, "%s:%d: Parse error at token '%s'\n", |
|
filename, cursor->line, cursor->content); |
|
exit(1); |
|
} |
|
|
|
} while (type++, !(type->flags & TYPE_STOP_MARKER)); |
|
|
|
verbose("Extracted %u actions\n", nr_actions); |
|
} |
|
|
|
static struct element *element_list; |
|
|
|
static struct element *alloc_elem(struct token *type) |
|
{ |
|
struct element *e = calloc(1, sizeof(*e)); |
|
if (!e) { |
|
perror(NULL); |
|
exit(1); |
|
} |
|
e->list_next = element_list; |
|
element_list = e; |
|
return e; |
|
} |
|
|
|
static struct element *parse_compound(struct token **_cursor, struct token *end, |
|
int alternates); |
|
|
|
/* |
|
* Parse one type definition statement |
|
*/ |
|
static struct element *parse_type(struct token **_cursor, struct token *end, |
|
struct token *name) |
|
{ |
|
struct element *top, *element; |
|
struct action *action, **ppaction; |
|
struct token *cursor = *_cursor; |
|
struct type **ref; |
|
char *p; |
|
int labelled = 0, implicit = 0; |
|
|
|
top = element = alloc_elem(cursor); |
|
element->class = ASN1_UNIV; |
|
element->method = ASN1_PRIM; |
|
element->tag = token_to_tag[cursor->token_type]; |
|
element->name = name; |
|
|
|
/* Extract the tag value if one given */ |
|
if (cursor->token_type == TOKEN_OPEN_SQUARE) { |
|
cursor++; |
|
if (cursor >= end) |
|
goto overrun_error; |
|
switch (cursor->token_type) { |
|
case DIRECTIVE_UNIVERSAL: |
|
element->class = ASN1_UNIV; |
|
cursor++; |
|
break; |
|
case DIRECTIVE_APPLICATION: |
|
element->class = ASN1_APPL; |
|
cursor++; |
|
break; |
|
case TOKEN_NUMBER: |
|
element->class = ASN1_CONT; |
|
break; |
|
case DIRECTIVE_PRIVATE: |
|
element->class = ASN1_PRIV; |
|
cursor++; |
|
break; |
|
default: |
|
fprintf(stderr, "%s:%d: Unrecognised tag class token '%s'\n", |
|
filename, cursor->line, cursor->content); |
|
exit(1); |
|
} |
|
|
|
if (cursor >= end) |
|
goto overrun_error; |
|
if (cursor->token_type != TOKEN_NUMBER) { |
|
fprintf(stderr, "%s:%d: Missing tag number '%s'\n", |
|
filename, cursor->line, cursor->content); |
|
exit(1); |
|
} |
|
|
|
element->tag &= ~0x1f; |
|
element->tag |= strtoul(cursor->content, &p, 10); |
|
element->flags |= ELEMENT_TAG_SPECIFIED; |
|
if (p - cursor->content != cursor->size) |
|
abort(); |
|
cursor++; |
|
|
|
if (cursor >= end) |
|
goto overrun_error; |
|
if (cursor->token_type != TOKEN_CLOSE_SQUARE) { |
|
fprintf(stderr, "%s:%d: Missing closing square bracket '%s'\n", |
|
filename, cursor->line, cursor->content); |
|
exit(1); |
|
} |
|
cursor++; |
|
if (cursor >= end) |
|
goto overrun_error; |
|
labelled = 1; |
|
} |
|
|
|
/* Handle implicit and explicit markers */ |
|
if (cursor->token_type == DIRECTIVE_IMPLICIT) { |
|
element->flags |= ELEMENT_IMPLICIT; |
|
implicit = 1; |
|
cursor++; |
|
if (cursor >= end) |
|
goto overrun_error; |
|
} else if (cursor->token_type == DIRECTIVE_EXPLICIT) { |
|
element->flags |= ELEMENT_EXPLICIT; |
|
cursor++; |
|
if (cursor >= end) |
|
goto overrun_error; |
|
} |
|
|
|
if (labelled) { |
|
if (!implicit) |
|
element->method |= ASN1_CONS; |
|
element->compound = implicit ? TAG_OVERRIDE : SEQUENCE; |
|
element->children = alloc_elem(cursor); |
|
element = element->children; |
|
element->class = ASN1_UNIV; |
|
element->method = ASN1_PRIM; |
|
element->tag = token_to_tag[cursor->token_type]; |
|
element->name = name; |
|
} |
|
|
|
/* Extract the type we're expecting here */ |
|
element->type = cursor; |
|
switch (cursor->token_type) { |
|
case DIRECTIVE_ANY: |
|
element->compound = ANY; |
|
cursor++; |
|
break; |
|
|
|
case DIRECTIVE_NULL: |
|
case DIRECTIVE_BOOLEAN: |
|
case DIRECTIVE_ENUMERATED: |
|
case DIRECTIVE_INTEGER: |
|
element->compound = NOT_COMPOUND; |
|
cursor++; |
|
break; |
|
|
|
case DIRECTIVE_EXTERNAL: |
|
element->method = ASN1_CONS; |
|
|
|
case DIRECTIVE_BMPString: |
|
case DIRECTIVE_GeneralString: |
|
case DIRECTIVE_GraphicString: |
|
case DIRECTIVE_IA5String: |
|
case DIRECTIVE_ISO646String: |
|
case DIRECTIVE_NumericString: |
|
case DIRECTIVE_PrintableString: |
|
case DIRECTIVE_T61String: |
|
case DIRECTIVE_TeletexString: |
|
case DIRECTIVE_UniversalString: |
|
case DIRECTIVE_UTF8String: |
|
case DIRECTIVE_VideotexString: |
|
case DIRECTIVE_VisibleString: |
|
case DIRECTIVE_ObjectDescriptor: |
|
case DIRECTIVE_GeneralizedTime: |
|
case DIRECTIVE_UTCTime: |
|
element->compound = NOT_COMPOUND; |
|
cursor++; |
|
break; |
|
|
|
case DIRECTIVE_BIT: |
|
case DIRECTIVE_OCTET: |
|
element->compound = NOT_COMPOUND; |
|
cursor++; |
|
if (cursor >= end) |
|
goto overrun_error; |
|
if (cursor->token_type != DIRECTIVE_STRING) |
|
goto parse_error; |
|
cursor++; |
|
break; |
|
|
|
case DIRECTIVE_OBJECT: |
|
element->compound = NOT_COMPOUND; |
|
cursor++; |
|
if (cursor >= end) |
|
goto overrun_error; |
|
if (cursor->token_type != DIRECTIVE_IDENTIFIER) |
|
goto parse_error; |
|
cursor++; |
|
break; |
|
|
|
case TOKEN_TYPE_NAME: |
|
element->compound = TYPE_REF; |
|
ref = bsearch(cursor, type_index, nr_types, sizeof(type_index[0]), |
|
type_finder); |
|
if (!ref) { |
|
fprintf(stderr, "%s:%d: Type '%s' undefined\n", |
|
filename, cursor->line, cursor->content); |
|
exit(1); |
|
} |
|
cursor->type = *ref; |
|
(*ref)->ref_count++; |
|
cursor++; |
|
break; |
|
|
|
case DIRECTIVE_CHOICE: |
|
element->compound = CHOICE; |
|
cursor++; |
|
element->children = parse_compound(&cursor, end, 1); |
|
break; |
|
|
|
case DIRECTIVE_SEQUENCE: |
|
element->compound = SEQUENCE; |
|
element->method = ASN1_CONS; |
|
cursor++; |
|
if (cursor >= end) |
|
goto overrun_error; |
|
if (cursor->token_type == DIRECTIVE_OF) { |
|
element->compound = SEQUENCE_OF; |
|
cursor++; |
|
if (cursor >= end) |
|
goto overrun_error; |
|
element->children = parse_type(&cursor, end, NULL); |
|
} else { |
|
element->children = parse_compound(&cursor, end, 0); |
|
} |
|
break; |
|
|
|
case DIRECTIVE_SET: |
|
element->compound = SET; |
|
element->method = ASN1_CONS; |
|
cursor++; |
|
if (cursor >= end) |
|
goto overrun_error; |
|
if (cursor->token_type == DIRECTIVE_OF) { |
|
element->compound = SET_OF; |
|
cursor++; |
|
if (cursor >= end) |
|
goto parse_error; |
|
element->children = parse_type(&cursor, end, NULL); |
|
} else { |
|
element->children = parse_compound(&cursor, end, 1); |
|
} |
|
break; |
|
|
|
default: |
|
fprintf(stderr, "%s:%d: Token '%s' does not introduce a type\n", |
|
filename, cursor->line, cursor->content); |
|
exit(1); |
|
} |
|
|
|
/* Handle elements that are optional */ |
|
if (cursor < end && (cursor->token_type == DIRECTIVE_OPTIONAL || |
|
cursor->token_type == DIRECTIVE_DEFAULT) |
|
) { |
|
cursor++; |
|
top->flags |= ELEMENT_SKIPPABLE; |
|
} |
|
|
|
if (cursor < end && cursor->token_type == TOKEN_OPEN_ACTION) { |
|
cursor++; |
|
if (cursor >= end) |
|
goto overrun_error; |
|
if (cursor->token_type != TOKEN_ELEMENT_NAME) { |
|
fprintf(stderr, "%s:%d: Token '%s' is not an action function name\n", |
|
filename, cursor->line, cursor->content); |
|
exit(1); |
|
} |
|
|
|
action = malloc(sizeof(struct action)); |
|
if (!action) { |
|
perror(NULL); |
|
exit(1); |
|
} |
|
action->index = 0; |
|
action->name = cursor->content; |
|
|
|
for (ppaction = &action_list; |
|
*ppaction; |
|
ppaction = &(*ppaction)->next |
|
) { |
|
int cmp = strcmp(action->name, (*ppaction)->name); |
|
if (cmp == 0) { |
|
free(action); |
|
action = *ppaction; |
|
goto found; |
|
} |
|
if (cmp < 0) { |
|
action->next = *ppaction; |
|
*ppaction = action; |
|
nr_actions++; |
|
goto found; |
|
} |
|
} |
|
action->next = NULL; |
|
*ppaction = action; |
|
nr_actions++; |
|
found: |
|
|
|
element->action = action; |
|
cursor->action = action; |
|
cursor++; |
|
if (cursor >= end) |
|
goto overrun_error; |
|
if (cursor->token_type != TOKEN_CLOSE_ACTION) { |
|
fprintf(stderr, "%s:%d: Missing close action, got '%s'\n", |
|
filename, cursor->line, cursor->content); |
|
exit(1); |
|
} |
|
cursor++; |
|
} |
|
|
|
*_cursor = cursor; |
|
return top; |
|
|
|
parse_error: |
|
fprintf(stderr, "%s:%d: Unexpected token '%s'\n", |
|
filename, cursor->line, cursor->content); |
|
exit(1); |
|
|
|
overrun_error: |
|
fprintf(stderr, "%s: Unexpectedly hit EOF\n", filename); |
|
exit(1); |
|
} |
|
|
|
/* |
|
* Parse a compound type list |
|
*/ |
|
static struct element *parse_compound(struct token **_cursor, struct token *end, |
|
int alternates) |
|
{ |
|
struct element *children, **child_p = &children, *element; |
|
struct token *cursor = *_cursor, *name; |
|
|
|
if (cursor->token_type != TOKEN_OPEN_CURLY) { |
|
fprintf(stderr, "%s:%d: Expected compound to start with brace not '%s'\n", |
|
filename, cursor->line, cursor->content); |
|
exit(1); |
|
} |
|
cursor++; |
|
if (cursor >= end) |
|
goto overrun_error; |
|
|
|
if (cursor->token_type == TOKEN_OPEN_CURLY) { |
|
fprintf(stderr, "%s:%d: Empty compound\n", |
|
filename, cursor->line); |
|
exit(1); |
|
} |
|
|
|
for (;;) { |
|
name = NULL; |
|
if (cursor->token_type == TOKEN_ELEMENT_NAME) { |
|
name = cursor; |
|
cursor++; |
|
if (cursor >= end) |
|
goto overrun_error; |
|
} |
|
|
|
element = parse_type(&cursor, end, name); |
|
if (alternates) |
|
element->flags |= ELEMENT_SKIPPABLE | ELEMENT_CONDITIONAL; |
|
|
|
*child_p = element; |
|
child_p = &element->next; |
|
|
|
if (cursor >= end) |
|
goto overrun_error; |
|
if (cursor->token_type != TOKEN_COMMA) |
|
break; |
|
cursor++; |
|
if (cursor >= end) |
|
goto overrun_error; |
|
} |
|
|
|
children->flags &= ~ELEMENT_CONDITIONAL; |
|
|
|
if (cursor->token_type != TOKEN_CLOSE_CURLY) { |
|
fprintf(stderr, "%s:%d: Expected compound closure, got '%s'\n", |
|
filename, cursor->line, cursor->content); |
|
exit(1); |
|
} |
|
cursor++; |
|
|
|
*_cursor = cursor; |
|
return children; |
|
|
|
overrun_error: |
|
fprintf(stderr, "%s: Unexpectedly hit EOF\n", filename); |
|
exit(1); |
|
} |
|
|
|
static void dump_element(const struct element *e, int level) |
|
{ |
|
const struct element *c; |
|
const struct type *t = e->type_def; |
|
const char *name = e->name ? e->name->content : "."; |
|
const char *tname = t && t->name ? t->name->content : "."; |
|
char tag[32]; |
|
|
|
if (e->class == 0 && e->method == 0 && e->tag == 0) |
|
strcpy(tag, "<...>"); |
|
else if (e->class == ASN1_UNIV) |
|
sprintf(tag, "%s %s %s", |
|
asn1_classes[e->class], |
|
asn1_methods[e->method], |
|
asn1_universal_tags[e->tag]); |
|
else |
|
sprintf(tag, "%s %s %u", |
|
asn1_classes[e->class], |
|
asn1_methods[e->method], |
|
e->tag); |
|
|
|
printf("%c%c%c%c%c %c %*s[*] \e[33m%s\e[m %s %s \e[35m%s\e[m\n", |
|
e->flags & ELEMENT_IMPLICIT ? 'I' : '-', |
|
e->flags & ELEMENT_EXPLICIT ? 'E' : '-', |
|
e->flags & ELEMENT_TAG_SPECIFIED ? 'T' : '-', |
|
e->flags & ELEMENT_SKIPPABLE ? 'S' : '-', |
|
e->flags & ELEMENT_CONDITIONAL ? 'C' : '-', |
|
"-tTqQcaro"[e->compound], |
|
level, "", |
|
tag, |
|
tname, |
|
name, |
|
e->action ? e->action->name : ""); |
|
if (e->compound == TYPE_REF) |
|
dump_element(e->type->type->element, level + 3); |
|
else |
|
for (c = e->children; c; c = c->next) |
|
dump_element(c, level + 3); |
|
} |
|
|
|
static void dump_elements(void) |
|
{ |
|
if (debug_opt) |
|
dump_element(type_list[0].element, 0); |
|
} |
|
|
|
static void render_element(FILE *out, struct element *e, struct element *tag); |
|
static void render_out_of_line_list(FILE *out); |
|
|
|
static int nr_entries; |
|
static int render_depth = 1; |
|
static struct element *render_list, **render_list_p = &render_list; |
|
|
|
__attribute__((format(printf, 2, 3))) |
|
static void render_opcode(FILE *out, const char *fmt, ...) |
|
{ |
|
va_list va; |
|
|
|
if (out) { |
|
fprintf(out, "\t[%4d] =%*s", nr_entries, render_depth, ""); |
|
va_start(va, fmt); |
|
vfprintf(out, fmt, va); |
|
va_end(va); |
|
} |
|
nr_entries++; |
|
} |
|
|
|
__attribute__((format(printf, 2, 3))) |
|
static void render_more(FILE *out, const char *fmt, ...) |
|
{ |
|
va_list va; |
|
|
|
if (out) { |
|
va_start(va, fmt); |
|
vfprintf(out, fmt, va); |
|
va_end(va); |
|
} |
|
} |
|
|
|
/* |
|
* Render the grammar into a state machine definition. |
|
*/ |
|
static void render(FILE *out, FILE *hdr) |
|
{ |
|
struct element *e; |
|
struct action *action; |
|
struct type *root; |
|
int index; |
|
|
|
fprintf(hdr, "/*\n"); |
|
fprintf(hdr, " * Automatically generated by asn1_compiler. Do not edit\n"); |
|
fprintf(hdr, " *\n"); |
|
fprintf(hdr, " * ASN.1 parser for %s\n", grammar_name); |
|
fprintf(hdr, " */\n"); |
|
fprintf(hdr, "#include <linux/asn1_decoder.h>\n"); |
|
fprintf(hdr, "\n"); |
|
fprintf(hdr, "extern const struct asn1_decoder %s_decoder;\n", grammar_name); |
|
if (ferror(hdr)) { |
|
perror(headername); |
|
exit(1); |
|
} |
|
|
|
fprintf(out, "/*\n"); |
|
fprintf(out, " * Automatically generated by asn1_compiler. Do not edit\n"); |
|
fprintf(out, " *\n"); |
|
fprintf(out, " * ASN.1 parser for %s\n", grammar_name); |
|
fprintf(out, " */\n"); |
|
fprintf(out, "#include <linux/asn1_ber_bytecode.h>\n"); |
|
fprintf(out, "#include \"%s.asn1.h\"\n", grammar_name); |
|
fprintf(out, "\n"); |
|
if (ferror(out)) { |
|
perror(outputname); |
|
exit(1); |
|
} |
|
|
|
/* Tabulate the action functions we might have to call */ |
|
fprintf(hdr, "\n"); |
|
index = 0; |
|
for (action = action_list; action; action = action->next) { |
|
action->index = index++; |
|
fprintf(hdr, |
|
"extern int %s(void *, size_t, unsigned char," |
|
" const void *, size_t);\n", |
|
action->name); |
|
} |
|
fprintf(hdr, "\n"); |
|
|
|
fprintf(out, "enum %s_actions {\n", grammar_name); |
|
for (action = action_list; action; action = action->next) |
|
fprintf(out, "\tACT_%s = %u,\n", |
|
action->name, action->index); |
|
fprintf(out, "\tNR__%s_actions = %u\n", grammar_name, nr_actions); |
|
fprintf(out, "};\n"); |
|
|
|
fprintf(out, "\n"); |
|
fprintf(out, "static const asn1_action_t %s_action_table[NR__%s_actions] = {\n", |
|
grammar_name, grammar_name); |
|
for (action = action_list; action; action = action->next) |
|
fprintf(out, "\t[%4u] = %s,\n", action->index, action->name); |
|
fprintf(out, "};\n"); |
|
|
|
if (ferror(out)) { |
|
perror(outputname); |
|
exit(1); |
|
} |
|
|
|
/* We do two passes - the first one calculates all the offsets */ |
|
verbose("Pass 1\n"); |
|
nr_entries = 0; |
|
root = &type_list[0]; |
|
render_element(NULL, root->element, NULL); |
|
render_opcode(NULL, "ASN1_OP_COMPLETE,\n"); |
|
render_out_of_line_list(NULL); |
|
|
|
for (e = element_list; e; e = e->list_next) |
|
e->flags &= ~ELEMENT_RENDERED; |
|
|
|
/* And then we actually render */ |
|
verbose("Pass 2\n"); |
|
fprintf(out, "\n"); |
|
fprintf(out, "static const unsigned char %s_machine[] = {\n", |
|
grammar_name); |
|
|
|
nr_entries = 0; |
|
root = &type_list[0]; |
|
render_element(out, root->element, NULL); |
|
render_opcode(out, "ASN1_OP_COMPLETE,\n"); |
|
render_out_of_line_list(out); |
|
|
|
fprintf(out, "};\n"); |
|
|
|
fprintf(out, "\n"); |
|
fprintf(out, "const struct asn1_decoder %s_decoder = {\n", grammar_name); |
|
fprintf(out, "\t.machine = %s_machine,\n", grammar_name); |
|
fprintf(out, "\t.machlen = sizeof(%s_machine),\n", grammar_name); |
|
fprintf(out, "\t.actions = %s_action_table,\n", grammar_name); |
|
fprintf(out, "};\n"); |
|
} |
|
|
|
/* |
|
* Render the out-of-line elements |
|
*/ |
|
static void render_out_of_line_list(FILE *out) |
|
{ |
|
struct element *e, *ce; |
|
const char *act; |
|
int entry; |
|
|
|
while ((e = render_list)) { |
|
render_list = e->render_next; |
|
if (!render_list) |
|
render_list_p = &render_list; |
|
|
|
render_more(out, "\n"); |
|
e->entry_index = entry = nr_entries; |
|
render_depth++; |
|
for (ce = e->children; ce; ce = ce->next) |
|
render_element(out, ce, NULL); |
|
render_depth--; |
|
|
|
act = e->action ? "_ACT" : ""; |
|
switch (e->compound) { |
|
case SEQUENCE: |
|
render_opcode(out, "ASN1_OP_END_SEQ%s,\n", act); |
|
break; |
|
case SEQUENCE_OF: |
|
render_opcode(out, "ASN1_OP_END_SEQ_OF%s,\n", act); |
|
render_opcode(out, "_jump_target(%u),\n", entry); |
|
break; |
|
case SET: |
|
render_opcode(out, "ASN1_OP_END_SET%s,\n", act); |
|
break; |
|
case SET_OF: |
|
render_opcode(out, "ASN1_OP_END_SET_OF%s,\n", act); |
|
render_opcode(out, "_jump_target(%u),\n", entry); |
|
break; |
|
default: |
|
break; |
|
} |
|
if (e->action) |
|
render_opcode(out, "_action(ACT_%s),\n", |
|
e->action->name); |
|
render_opcode(out, "ASN1_OP_RETURN,\n"); |
|
} |
|
} |
|
|
|
/* |
|
* Render an element. |
|
*/ |
|
static void render_element(FILE *out, struct element *e, struct element *tag) |
|
{ |
|
struct element *ec, *x; |
|
const char *cond, *act; |
|
int entry, skippable = 0, outofline = 0; |
|
|
|
if (e->flags & ELEMENT_SKIPPABLE || |
|
(tag && tag->flags & ELEMENT_SKIPPABLE)) |
|
skippable = 1; |
|
|
|
if ((e->type_def && e->type_def->ref_count > 1) || |
|
skippable) |
|
outofline = 1; |
|
|
|
if (e->type_def && out) { |
|
render_more(out, "\t// %s\n", e->type_def->name->content); |
|
} |
|
|
|
/* Render the operation */ |
|
cond = (e->flags & ELEMENT_CONDITIONAL || |
|
(tag && tag->flags & ELEMENT_CONDITIONAL)) ? "COND_" : ""; |
|
act = e->action ? "_ACT" : ""; |
|
switch (e->compound) { |
|
case ANY: |
|
render_opcode(out, "ASN1_OP_%sMATCH_ANY%s%s,", |
|
cond, act, skippable ? "_OR_SKIP" : ""); |
|
if (e->name) |
|
render_more(out, "\t\t// %s", e->name->content); |
|
render_more(out, "\n"); |
|
goto dont_render_tag; |
|
|
|
case TAG_OVERRIDE: |
|
render_element(out, e->children, e); |
|
return; |
|
|
|
case SEQUENCE: |
|
case SEQUENCE_OF: |
|
case SET: |
|
case SET_OF: |
|
render_opcode(out, "ASN1_OP_%sMATCH%s%s,", |
|
cond, |
|
outofline ? "_JUMP" : "", |
|
skippable ? "_OR_SKIP" : ""); |
|
break; |
|
|
|
case CHOICE: |
|
goto dont_render_tag; |
|
|
|
case TYPE_REF: |
|
if (e->class == ASN1_UNIV && e->method == ASN1_PRIM && e->tag == 0) |
|
goto dont_render_tag; |
|
default: |
|
render_opcode(out, "ASN1_OP_%sMATCH%s%s,", |
|
cond, act, |
|
skippable ? "_OR_SKIP" : ""); |
|
break; |
|
} |
|
|
|
x = tag ?: e; |
|
if (x->name) |
|
render_more(out, "\t\t// %s", x->name->content); |
|
render_more(out, "\n"); |
|
|
|
/* Render the tag */ |
|
if (!tag || !(tag->flags & ELEMENT_TAG_SPECIFIED)) |
|
tag = e; |
|
|
|
if (tag->class == ASN1_UNIV && |
|
tag->tag != 14 && |
|
tag->tag != 15 && |
|
tag->tag != 31) |
|
render_opcode(out, "_tag(%s, %s, %s),\n", |
|
asn1_classes[tag->class], |
|
asn1_methods[tag->method | e->method], |
|
asn1_universal_tags[tag->tag]); |
|
else |
|
render_opcode(out, "_tagn(%s, %s, %2u),\n", |
|
asn1_classes[tag->class], |
|
asn1_methods[tag->method | e->method], |
|
tag->tag); |
|
tag = NULL; |
|
dont_render_tag: |
|
|
|
/* Deal with compound types */ |
|
switch (e->compound) { |
|
case TYPE_REF: |
|
render_element(out, e->type->type->element, tag); |
|
if (e->action) |
|
render_opcode(out, "ASN1_OP_%sACT,\n", |
|
skippable ? "MAYBE_" : ""); |
|
break; |
|
|
|
case SEQUENCE: |
|
if (outofline) { |
|
/* Render out-of-line for multiple use or |
|
* skipability */ |
|
render_opcode(out, "_jump_target(%u),", e->entry_index); |
|
if (e->type_def && e->type_def->name) |
|
render_more(out, "\t\t// --> %s", |
|
e->type_def->name->content); |
|
render_more(out, "\n"); |
|
if (!(e->flags & ELEMENT_RENDERED)) { |
|
e->flags |= ELEMENT_RENDERED; |
|
*render_list_p = e; |
|
render_list_p = &e->render_next; |
|
} |
|
return; |
|
} else { |
|
/* Render inline for single use */ |
|
render_depth++; |
|
for (ec = e->children; ec; ec = ec->next) |
|
render_element(out, ec, NULL); |
|
render_depth--; |
|
render_opcode(out, "ASN1_OP_END_SEQ%s,\n", act); |
|
} |
|
break; |
|
|
|
case SEQUENCE_OF: |
|
case SET_OF: |
|
if (outofline) { |
|
/* Render out-of-line for multiple use or |
|
* skipability */ |
|
render_opcode(out, "_jump_target(%u),", e->entry_index); |
|
if (e->type_def && e->type_def->name) |
|
render_more(out, "\t\t// --> %s", |
|
e->type_def->name->content); |
|
render_more(out, "\n"); |
|
if (!(e->flags & ELEMENT_RENDERED)) { |
|
e->flags |= ELEMENT_RENDERED; |
|
*render_list_p = e; |
|
render_list_p = &e->render_next; |
|
} |
|
return; |
|
} else { |
|
/* Render inline for single use */ |
|
entry = nr_entries; |
|
render_depth++; |
|
render_element(out, e->children, NULL); |
|
render_depth--; |
|
if (e->compound == SEQUENCE_OF) |
|
render_opcode(out, "ASN1_OP_END_SEQ_OF%s,\n", act); |
|
else |
|
render_opcode(out, "ASN1_OP_END_SET_OF%s,\n", act); |
|
render_opcode(out, "_jump_target(%u),\n", entry); |
|
} |
|
break; |
|
|
|
case SET: |
|
/* I can't think of a nice way to do SET support without having |
|
* a stack of bitmasks to make sure no element is repeated. |
|
* The bitmask has also to be checked that no non-optional |
|
* elements are left out whilst not preventing optional |
|
* elements from being left out. |
|
*/ |
|
fprintf(stderr, "The ASN.1 SET type is not currently supported.\n"); |
|
exit(1); |
|
|
|
case CHOICE: |
|
for (ec = e->children; ec; ec = ec->next) |
|
render_element(out, ec, ec); |
|
if (!skippable) |
|
render_opcode(out, "ASN1_OP_COND_FAIL,\n"); |
|
if (e->action) |
|
render_opcode(out, "ASN1_OP_ACT,\n"); |
|
break; |
|
|
|
default: |
|
break; |
|
} |
|
|
|
if (e->action) |
|
render_opcode(out, "_action(ACT_%s),\n", e->action->name); |
|
}
|
|
|