2 * Copyright (c) 2003-2017 Lev Walkin <vlm@lionet.info> and contributors.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * This is the program that connects the libasn1* libraries together.
30 * It uses them in turn to parse, fix and then compile or print the ASN.1 tree.
32 #include "sys-common.h"
35 #define COPYRIGHT "Copyright (c) 2003-2017 Lev Walkin <vlm@lionet.info> and contributors.\n"
37 #include <asn1parser.h> /* Parse the ASN.1 file and build a tree */
38 #include <asn1fix.h> /* Fix the ASN.1 tree */
39 #include <asn1print.h> /* Print the ASN.1 tree */
40 #include <asn1compiler.h> /* Compile the ASN.1 tree */
41 #include <asn1fix_export.h>
43 #include <asn1c_compat.h> /* Portable basename(3) and dirname(3) */
52 static void usage(const char *av0); /* Print the Usage screen and exit */
53 static int importStandardModules(asn1p_t *asn, const char *skeletons_dir);
56 main(int ac, char **av) {
57 enum asn1p_flags asn1_parser_flags = A1P_NOFLAGS;
58 enum asn1f_flags asn1_fixer_flags = A1F_NOFLAGS;
59 enum asn1c_flags asn1_compiler_flags =
60 A1C_NO_C99 | A1C_GEN_OER | A1C_GEN_PER | A1C_GEN_EXAMPLE;
61 enum asn1print_flags asn1_printer_flags = APF_NOFLAGS;
62 int print_arg__print_out = 0; /* Don't compile, just print parsed */
63 int print_arg__fix_n_print = 0; /* Fix and print */
64 int warnings_as_errors = 0; /* Treat warnings as errors */
65 char *skeletons_dir = NULL; /* Directory with supplementary stuff */
66 char *destdir = NULL; /* Destination for generated files */
67 char **debug_type_names = 0; /* Debug stuff */
68 size_t debug_type_names_count = 0;
69 asn1p_t *asn = 0; /* An ASN.1 parsed tree */
70 int ret; /* Return value from misc functions */
71 int ch; /* Command line character */
72 int i; /* Index in some loops */
73 int exit_code = 0; /* Exit code */
76 * Process command-line options.
78 while((ch = getopt(ac, av, "D:d:EFf:g:hn:LPp:RS:vW:X")) != -1) switch(ch) {
80 if(optarg && *optarg) {
81 size_t optarg_len = strlen(optarg);
83 destdir = calloc(1, optarg_len + 2); /* + "/\0" */
85 strcpy(destdir, optarg);
86 if(destdir[optarg_len - 1] != '/') {
87 destdir[optarg_len] = '/';
95 if(strncmp(optarg, "ebug-type-naming=", 17) == 0) {
96 char **p = realloc(debug_type_names,
97 (debug_type_names_count + 2) * sizeof(*p));
100 debug_type_names[debug_type_names_count++] =
102 debug_type_names[debug_type_names_count] = NULL;
104 } else if(strcmp(optarg, "ebug-output-origin-lines") == 0) {
105 asn1_compiler_flags |= A1C_DEBUG_OUTPUT_ORIGIN_LINES;
110 print_arg__print_out = 1;
113 print_arg__fix_n_print = 1;
116 if(strcmp(optarg, "all-defs-global") == 0) {
117 asn1_compiler_flags |= A1C_ALL_DEFS_GLOBAL;
118 } else if(strcmp(optarg, "bless-SIZE") == 0) {
119 asn1_fixer_flags |= A1F_EXTENDED_SizeConstraint;
120 } else if(strcmp(optarg, "compound-names") == 0) {
121 asn1_compiler_flags |= A1C_COMPOUND_NAMES;
122 } else if(strcmp(optarg, "indirect-choice") == 0) {
123 asn1_compiler_flags |= A1C_INDIRECT_CHOICE;
124 } else if(strncmp(optarg, "known-extern-type=", 18) == 0) {
125 char *known_type = optarg + 18;
126 ret = asn1f_make_known_external_type(known_type);
127 assert(ret == 0 || errno == EEXIST);
128 } else if(strcmp(optarg, "native-types") == 0) {
129 fprintf(stderr, "-f%s: Deprecated option\n", optarg);
130 asn1_compiler_flags &= ~A1C_USE_WIDE_TYPES;
131 } else if(strcmp(optarg, "wide-types") == 0) {
132 asn1_compiler_flags |= A1C_USE_WIDE_TYPES;
133 } else if(strcmp(optarg, "line-refs") == 0) {
134 asn1_compiler_flags |= A1C_LINE_REFS;
135 } else if(strcmp(optarg, "no-constraints") == 0) {
136 asn1_compiler_flags |= A1C_NO_CONSTRAINTS;
137 } else if(strcmp(optarg, "no-include-deps") == 0) {
138 asn1_compiler_flags |= A1C_NO_INCLUDE_DEPS;
139 } else if(strcmp(optarg, "includes-quoted") == 0) {
140 asn1_compiler_flags |= A1C_INCLUDES_QUOTED;
141 } else if(strcmp(optarg, "unnamed-unions") == 0) {
142 asn1_compiler_flags |= A1C_UNNAMED_UNIONS;
143 } else if(strcmp(optarg, "skeletons-copy") == 0) {
144 fprintf(stderr, "-f%s: Deprecated option\n", optarg);
145 asn1_compiler_flags &= ~A1C_LINK_SKELETONS;
146 } else if(strcmp(optarg, "link-skeletons") == 0) {
147 asn1_compiler_flags |= A1C_LINK_SKELETONS;
149 fprintf(stderr, "-f%s: Invalid argument\n", optarg);
154 if(strcmp(optarg, "en-PER") == 0) {
155 asn1_compiler_flags |= A1C_GEN_PER;
156 } else if(strcmp(optarg, "en-OER") == 0) {
157 asn1_compiler_flags |= A1C_GEN_OER;
158 } else if(strcmp(optarg, "en-example") == 0) {
159 asn1_compiler_flags |= A1C_GEN_EXAMPLE;
160 } else if(strcmp(optarg, "en-autotools") == 0) {
161 asn1_compiler_flags |= A1C_GEN_AUTOTOOLS_EXAMPLE;
163 fprintf(stderr, "-g%s: Invalid argument\n", optarg);
170 if(strcmp(optarg, "o-gen-PER") == 0) {
171 asn1_compiler_flags &= ~A1C_GEN_PER;
172 } else if(strcmp(optarg, "o-gen-OER") == 0) {
173 asn1_compiler_flags &= ~A1C_GEN_OER;
174 } else if(strcmp(optarg, "o-gen-example") == 0) {
175 asn1_compiler_flags &= ~A1C_GEN_EXAMPLE;
176 } else if(strcmp(optarg, "o-gen-autotools") == 0) {
177 asn1_compiler_flags &= ~A1C_GEN_AUTOTOOLS_EXAMPLE;
179 fprintf(stderr, "-n%s: Invalid argument\n", optarg);
184 asn1_compiler_flags |= A1C_PRINT_COMPILED;
185 asn1_compiler_flags &= ~A1C_NO_C99;
188 if(strncmp(optarg, "du=", 3) == 0) {
189 char *pduname = optarg + 3;
190 if(strcmp(pduname, "all") == 0) {
191 asn1_compiler_flags |= A1C_PDU_ALL;
192 } else if(strcmp(pduname, "auto") == 0) {
193 asn1_compiler_flags |= A1C_PDU_AUTO;
194 } else if(pduname[0] >= 'A' && pduname[0] <= 'Z') {
195 asn1c__add_pdu_type(pduname);
196 asn1_compiler_flags |= A1C_PDU_TYPE;
198 fprintf(stderr, "-pdu=%s: expected -pdu={all|auto|Type}\n",
202 } else if(strcmp(optarg, "rint-class-matrix") == 0) {
203 asn1_printer_flags |= APF_PRINT_CLASS_MATRIX;
204 } else if(strcmp(optarg, "rint-constraints") == 0) {
205 asn1_printer_flags |= APF_PRINT_CONSTRAINTS;
206 } else if(strcmp(optarg, "rint-lines") == 0) {
207 asn1_printer_flags |= APF_LINE_COMMENTS;
209 fprintf(stderr, "-p%s: Invalid argument\n", optarg);
214 asn1_compiler_flags |= A1C_OMIT_SUPPORT_CODE;
217 skeletons_dir = optarg;
220 fprintf(stderr, "ASN.1 Compiler, v" VERSION "\n" COPYRIGHT);
224 if(strcmp(optarg, "error") == 0) {
225 warnings_as_errors = 1;
227 } else if(strcmp(optarg, "debug-lexer") == 0) {
228 asn1_parser_flags |= A1P_DEBUG_LEXER;
230 } else if(strcmp(optarg, "debug-parser") == 0) {
231 asn1_parser_flags |= A1P_DEBUG_PARSER;
233 } else if(strcmp(optarg, "debug-fixer") == 0) {
234 asn1_fixer_flags |= A1F_DEBUG;
236 } else if(strcmp(optarg, "debug-compiler") == 0) {
237 asn1_compiler_flags |= A1C_DEBUG;
240 fprintf(stderr, "-W%s: Invalid argument\n", optarg);
245 print_arg__print_out = 1; /* Implicit -E */
246 print_arg__fix_n_print = 1; /* Implicit -F */
247 asn1_printer_flags |= APF_PRINT_XML_DTD;
254 * Validate the options combination.
256 if(print_arg__print_out) {
257 if((asn1_printer_flags & APF_PRINT_CONSTRAINTS)
258 && !print_arg__fix_n_print) {
260 "Error: -print-constraints argument requires -E -F\n");
264 if(print_arg__fix_n_print) {
265 fprintf(stderr, "Error: -F requires -E\n");
268 if(asn1_printer_flags) {
269 fprintf(stderr, "Error: -print-... arguments require -E\n");
275 * Ensure that there are some input files present.
281 const char *bin_name = a1c_basename(av[0], NULL);
283 "%s: No input files specified. "
284 "Try '%s -h' for more information\n",
290 * Make sure the skeleton directory is out there.
292 if(skeletons_dir == NULL) {
294 skeletons_dir = DATADIR;
295 if((av[-optind][0] == '.' || av[-optind][1] == '/')
296 && stat(skeletons_dir, &sb)) {
298 * The default skeletons directory does not exist,
299 * compute it from my file name:
300 * ./asn1c/asn1c -> ./skeletons
302 const char *skel_dir;
305 skel_dir = a1c_dirname(av[-optind]);
307 len = strlen(skel_dir) + sizeof("/../skeletons");
308 skeletons_dir = malloc(len);
309 assert(skeletons_dir);
310 snprintf(skeletons_dir, len, "%s/../skeletons", skel_dir);
311 if(stat(skeletons_dir, &sb)) {
313 "WARNING: skeletons are neither in "
314 "\"%s\" nor in \"%s\"!\n",
315 DATADIR, skeletons_dir);
316 if(warnings_as_errors) exit(EX_OSFILE);
322 * Iterate over input files and parse each.
323 * All syntax trees from all files will be bundled together.
325 for(i = 0; i < ac; i++) {
328 new_asn = asn1p_parse_file(av[i], asn1_parser_flags);
329 if(new_asn == NULL) {
330 fprintf(stderr, "Cannot parse \"%s\"\n", av[i]);
331 exit_code = EX_DATAERR;
336 * Bundle the parsed tree with existing one.
340 while((mod = TQ_REMOVE(&(new_asn->modules), mod_next)))
341 TQ_ADD(&(asn->modules), mod, mod_next);
342 asn1p_delete(new_asn);
348 /* These are mostly notes for the human readers */
350 assert(skeletons_dir);
353 * Dump the parsed ASN.1 tree if -E specified and -F is NOT given.
355 if(print_arg__print_out && !print_arg__fix_n_print) {
356 if(asn1print(asn, asn1_printer_flags)) {
357 exit_code = EX_SOFTWARE;
364 * Read in the files from skeletons/standard-modules
366 if(importStandardModules(asn, skeletons_dir)) {
367 if(warnings_as_errors) {
368 exit_code = EX_DATAERR;
372 asn1f_use_standard_namespaces(asn);
376 * Process the ASN.1 specification: perform semantic checks,
377 * expand references, etc, etc.
378 * This function will emit necessary warnings and error messages.
380 ret = asn1f_process(asn, asn1_fixer_flags,
381 NULL /* default fprintf(stderr) */);
384 break; /* All clear */
386 if(!warnings_as_errors) {
391 exit_code = EX_DATAERR; /* Fatal failure */
396 * Dump the parsed ASN.1 tree if -E specified and -F is given.
398 if(print_arg__print_out && print_arg__fix_n_print) {
399 if(asn1print(asn, asn1_printer_flags)) {
400 exit_code = EX_SOFTWARE;
407 * -debug-type-naming=Type
409 if(debug_type_names) {
410 asn1c_debug_type_naming(asn, asn1_compiler_flags, debug_type_names);
415 * Compile the ASN.1 tree into a set of source files
416 * of another language.
418 if(asn1_compile(asn, skeletons_dir, destdir ? destdir : "",
419 asn1_compiler_flags, ac + optind, optind, av - optind)) {
420 exit_code = EX_SOFTWARE;
426 if (exit_code) exit(exit_code);
432 * Parse and import *.asn1 from skeletons/standard-modules
435 importStandardModules(asn1p_t *asn, const char *skeletons_dir) {
438 const char *filename;
445 struct _finddata_t c_file;
453 /* Notes for the human reader */
455 assert(skeletons_dir);
458 * Figure out the standard-modules directory.
460 target_dir_len = strlen(skeletons_dir) + sizeof("/standard-modules") - 1;
461 target_dir = malloc(target_dir_len + 1);
463 snprintf(target_dir, target_dir_len + 1, "%s/standard-modules",
467 len = target_dir_len + sizeof("/*.asn1");
468 pattern = malloc(len);
470 snprintf(pattern, len, "%s/*.asn1", target_dir);
471 dir = _findfirst(pattern, &c_file);
474 dir = opendir(target_dir);
477 fprintf(stderr, "WARNING: Cannot find standard modules in %s\n",
484 filename = c_file.name;
486 while((dp = readdir(dir))) {
487 filename = dp->d_name;
489 len = strlen(filename);
490 if(len <= 5 || strcmp(filename + len - 5, ".asn1")) continue;
491 len = target_dir_len + 1 + len + 1;
492 fullname = malloc(len);
493 if(!fullname) continue; /* Just skip it, no big deal */
494 snprintf(fullname, len, "%s/%s", target_dir, filename);
497 new_asn = asn1p_parse_file(filename, A1P_NOFLAGS);
498 if(new_asn == NULL) {
499 fprintf(stderr, "WARNING: Cannot parse standard module \"%s\"\n",
505 /* Import these modules and mark them as "standard" */
506 while((mod = TQ_REMOVE(&(new_asn->modules), mod_next))) {
507 mod->_tags |= MT_STANDARD_MODULE;
508 TQ_ADD(&(asn->modules), mod, mod_next);
510 asn1p_delete(new_asn);
514 } while(_findnext(dir, &c_file) == 0);
518 } /* while(readdir()) */
531 * Print the usage screen and exit(EX_USAGE).
533 static void __attribute__((noreturn))
534 usage(const char *av0) {
535 /* clang-format off */
537 "ASN.1 Compiler, v" VERSION "\n" COPYRIGHT
538 "Usage: %s [options] file ...\n"
540 " -E Run only the ASN.1 parser and print out the tree\n"
541 " -F During -E operation, also perform tree fixing\n"
543 " -P Concatenate and print the compiled text\n"
544 " -R Restrict output (tables only, no support code)\n"
545 " -S <dir> Directory with support (skeleton?) files\n"
546 " (Default is \"%s\")\n"
547 " -D <dir> Destination directory for generated files (default current dir)\n"
548 " -X Generate and print the XML DTD\n"
551 " -Werror Treat warnings as errors; abort if any warning\n"
552 " -Wdebug-lexer Enable verbose debugging output from lexer\n"
553 " -Wdebug-parser Enable verbose debugging output from parser\n"
554 " -Wdebug-fixer --//-- semantics processor\n"
555 " -Wdebug-compiler --//-- compiler\n"
558 " -fbless-SIZE Allow SIZE() constraint for INTEGER etc (non-std.)\n"
559 " -fcompound-names Disambiguate C's struct NAME's inside top-level types\n"
560 " -findirect-choice Compile members of CHOICE as indirect pointers\n"
561 " -fincludes-quoted Generate #includes in \"double\" instead of <angle> quotes\n"
562 " -fknown-extern-type=<name> Pretend the specified type is known\n"
563 " -fline-refs Include ASN.1 module's line numbers in comments\n"
564 " -fno-constraints Do not generate the constraint checking code\n"
565 " -fno-include-deps Do not generate the courtesy #includes for dependencies\n"
566 " -funnamed-unions Enable unnamed unions in structures\n"
567 " -fwide-types Use INTEGER_t instead of \"long\" by default, etc.\n"
570 " -no-gen-OER Do not generate the OER (X.696) support code\n"
571 " -no-gen-PER Do not generate the PER (X.691) support code\n"
572 " -no-gen-example Do not generate the ASN.1 format converter example\n"
573 " -gen-autotools Generate example top-level configure.ac and Makefile.am\n"
574 " -pdu={all|auto|Type} Generate PDU table (discover PDUs automatically)\n"
577 " -print-class-matrix Print out the collected object class matrix (debug)\n"
578 " -print-constraints Explain subtype constraints (debug)\n"
579 " -print-lines Generate \"-- #line\" comments in -E output\n"
582 a1c_basename(av0, NULL), DATADIR);
583 /* clang-format on */