NativeEnumerated.c vars NULL init and check
[com/asn1c.git] / asn1c / asn1c.c
1 /*
2  * Copyright (c) 2003-2017 Lev Walkin <vlm@lionet.info> and contributors.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
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.
13  *
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
24  * SUCH DAMAGE.
25  *
26  * $Id$
27  */
28 /*
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.
31  */
32 #include "sys-common.h"
33
34 #undef COPYRIGHT
35 #define COPYRIGHT "Copyright (c) 2003-2017 Lev Walkin <vlm@lionet.info> and contributors.\n"
36
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>
42
43 #include <asn1c_compat.h> /* Portable basename(3) and dirname(3) */
44
45 #ifdef _WIN32
46 #include <io.h>
47 #include <direct.h>
48 #else
49 #include <dirent.h>
50 #endif
51
52 static void usage(const char *av0); /* Print the Usage screen and exit */
53 static int importStandardModules(asn1p_t *asn, const char *skeletons_dir);
54
55 int
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 */
74
75     /*
76      * Process command-line options.
77      */
78     while((ch = getopt(ac, av, "D:d:EFf:g:hn:LPp:RS:vW:X")) != -1) switch(ch) {
79         case 'D':
80             if(optarg && *optarg) {
81                 size_t optarg_len = strlen(optarg);
82                 free(destdir);
83                 destdir = calloc(1, optarg_len + 2); /* + "/\0" */
84                 assert(destdir);
85                 strcpy(destdir, optarg);
86                 if(destdir[optarg_len - 1] != '/') {
87                     destdir[optarg_len] = '/';
88                 }
89             } else {
90                 free(destdir);
91                 destdir = NULL;
92             }
93             break;
94         case 'd':
95             if(strncmp(optarg, "ebug-type-naming=", 17) == 0) {
96                 char **p = realloc(debug_type_names,
97                                    (debug_type_names_count + 2) * sizeof(*p));
98                 assert(p);
99                 debug_type_names = p;
100                 debug_type_names[debug_type_names_count++] =
101                     strdup(optarg + 17);
102                 debug_type_names[debug_type_names_count] = NULL;
103                 break;
104             } else if(strcmp(optarg, "ebug-output-origin-lines") == 0) {
105                 asn1_compiler_flags |= A1C_DEBUG_OUTPUT_ORIGIN_LINES;
106                 break;
107             }
108             usage(av[0]);
109         case 'E':
110             print_arg__print_out = 1;
111             break;
112         case 'F':
113             print_arg__fix_n_print = 1;
114             break;
115         case 'f':
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;
148             } else {
149                 fprintf(stderr, "-f%s: Invalid argument\n", optarg);
150                 exit(EX_USAGE);
151             }
152             break;
153         case 'g':
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;
162             } else {
163                 fprintf(stderr, "-g%s: Invalid argument\n", optarg);
164                 exit(EX_USAGE);
165             }
166             break;
167         case 'h':
168             usage(av[0]);
169         case 'n':
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;
178             } else {
179                 fprintf(stderr, "-n%s: Invalid argument\n", optarg);
180                 exit(EX_USAGE);
181             }
182             break;
183         case 'P':
184             asn1_compiler_flags |= A1C_PRINT_COMPILED;
185             asn1_compiler_flags &= ~A1C_NO_C99;
186             break;
187         case 'p':
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;
197                 } else {
198                     fprintf(stderr, "-pdu=%s: expected -pdu={all|auto|Type}\n",
199                             pduname);
200                     exit(EX_USAGE);
201                 }
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;
208             } else {
209                 fprintf(stderr, "-p%s: Invalid argument\n", optarg);
210                 exit(EX_USAGE);
211             }
212             break;
213         case 'R':
214             asn1_compiler_flags |= A1C_OMIT_SUPPORT_CODE;
215             break;
216         case 'S':
217             skeletons_dir = optarg;
218             break;
219         case 'v':
220             fprintf(stderr, "ASN.1 Compiler, v" VERSION "\n" COPYRIGHT);
221             exit(0);
222             break;
223         case 'W':
224             if(strcmp(optarg, "error") == 0) {
225                 warnings_as_errors = 1;
226                 break;
227             } else if(strcmp(optarg, "debug-lexer") == 0) {
228                 asn1_parser_flags |= A1P_DEBUG_LEXER;
229                 break;
230             } else if(strcmp(optarg, "debug-parser") == 0) {
231                 asn1_parser_flags |= A1P_DEBUG_PARSER;
232                 break;
233             } else if(strcmp(optarg, "debug-fixer") == 0) {
234                 asn1_fixer_flags |= A1F_DEBUG;
235                 break;
236             } else if(strcmp(optarg, "debug-compiler") == 0) {
237                 asn1_compiler_flags |= A1C_DEBUG;
238                 break;
239             } else {
240                 fprintf(stderr, "-W%s: Invalid argument\n", optarg);
241                 exit(EX_USAGE);
242             }
243             break;
244         case 'X':
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;
248             break;
249         default:
250             usage(av[0]);
251         }
252
253     /*
254      * Validate the options combination.
255      */
256     if(print_arg__print_out) {
257         if((asn1_printer_flags & APF_PRINT_CONSTRAINTS)
258            && !print_arg__fix_n_print) {
259             fprintf(stderr,
260                     "Error: -print-constraints argument requires -E -F\n");
261             exit(EX_USAGE);
262         }
263     } else {
264         if(print_arg__fix_n_print) {
265             fprintf(stderr, "Error: -F requires -E\n");
266             exit(EX_USAGE);
267         }
268         if(asn1_printer_flags) {
269             fprintf(stderr, "Error: -print-... arguments require -E\n");
270             exit(EX_USAGE);
271         }
272     }
273
274     /*
275      * Ensure that there are some input files present.
276      */
277     if(ac > optind) {
278         ac -= optind;
279         av += optind;
280     } else {
281         const char *bin_name = a1c_basename(av[0], NULL);
282         fprintf(stderr,
283                 "%s: No input files specified. "
284                 "Try '%s -h' for more information\n",
285                 bin_name, bin_name);
286         exit(1);
287     }
288
289     /*
290      * Make sure the skeleton directory is out there.
291      */
292     if(skeletons_dir == NULL) {
293         struct stat sb;
294         skeletons_dir = DATADIR;
295         if((av[-optind][0] == '.' || av[-optind][1] == '/')
296            && stat(skeletons_dir, &sb)) {
297             /*
298              * The default skeletons directory does not exist,
299              * compute it from my file name:
300              * ./asn1c/asn1c -> ./skeletons
301              */
302             const char *skel_dir;
303             size_t len;
304
305             skel_dir = a1c_dirname(av[-optind]);
306
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)) {
312                 fprintf(stderr,
313                         "WARNING: skeletons are neither in "
314                         "\"%s\" nor in \"%s\"!\n",
315                         DATADIR, skeletons_dir);
316                 if(warnings_as_errors) exit(EX_OSFILE);
317             }
318         }
319     }
320
321     /*
322      * Iterate over input files and parse each.
323      * All syntax trees from all files will be bundled together.
324      */
325     for(i = 0; i < ac; i++) {
326         asn1p_t *new_asn;
327
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;
332             goto cleanup;
333         }
334
335         /*
336          * Bundle the parsed tree with existing one.
337          */
338         if(asn) {
339             asn1p_module_t *mod;
340             while((mod = TQ_REMOVE(&(new_asn->modules), mod_next)))
341                 TQ_ADD(&(asn->modules), mod, mod_next);
342             asn1p_delete(new_asn);
343         } else {
344             asn = new_asn;
345         }
346     }
347
348     /* These are mostly notes for the human readers */
349     assert(asn);
350     assert(skeletons_dir);
351
352     /*
353      * Dump the parsed ASN.1 tree if -E specified and -F is NOT given.
354      */
355     if(print_arg__print_out && !print_arg__fix_n_print) {
356         if(asn1print(asn, asn1_printer_flags)) {
357             exit_code = EX_SOFTWARE;
358             goto cleanup;
359         }
360         return 0;
361     }
362
363     /*
364      * Read in the files from skeletons/standard-modules
365      */
366     if(importStandardModules(asn, skeletons_dir)) {
367         if(warnings_as_errors) {
368             exit_code = EX_DATAERR;
369             goto cleanup;
370         }
371     } else {
372         asn1f_use_standard_namespaces(asn);
373     }
374
375     /*
376      * Process the ASN.1 specification: perform semantic checks,
377      * expand references, etc, etc.
378      * This function will emit necessary warnings and error messages.
379      */
380     ret = asn1f_process(asn, asn1_fixer_flags,
381                         NULL /* default fprintf(stderr) */);
382     switch(ret) {
383     case 0:
384         break; /* All clear */
385     case 1:
386         if(!warnings_as_errors) {
387             break;
388         }
389         /* Fall through */
390     case -1:
391         exit_code = EX_DATAERR; /* Fatal failure */
392         goto cleanup;
393     }
394
395     /*
396      * Dump the parsed ASN.1 tree if -E specified and -F is given.
397      */
398     if(print_arg__print_out && print_arg__fix_n_print) {
399         if(asn1print(asn, asn1_printer_flags)) {
400             exit_code = EX_SOFTWARE;
401             goto cleanup;
402         }
403         return 0;
404     }
405
406     /*
407      * -debug-type-naming=Type
408      */
409     if(debug_type_names) {
410         asn1c_debug_type_naming(asn, asn1_compiler_flags, debug_type_names);
411         return 0;
412     }
413
414     /*
415      * Compile the ASN.1 tree into a set of source files
416      * of another language.
417      */
418     if(asn1_compile(asn, skeletons_dir, destdir ? destdir : "",
419                     asn1_compiler_flags, ac + optind, optind, av - optind)) {
420         exit_code = EX_SOFTWARE;
421     }
422
423 cleanup:
424     asn1p_delete(asn);
425     asn1p_lex_destroy();
426     if (exit_code) exit(exit_code);
427
428     return 0;
429 }
430
431 /*
432  * Parse and import *.asn1 from skeletons/standard-modules
433  */
434 static int
435 importStandardModules(asn1p_t *asn, const char *skeletons_dir) {
436     asn1p_t *new_asn;
437     asn1p_module_t *mod;
438     const char *filename;
439     char *fullname;
440     char *target_dir;
441     int target_dir_len;
442     int len;
443 #ifdef _WIN32
444     intptr_t dir;
445     struct _finddata_t c_file;
446     char *pattern;
447 #else
448     struct dirent *dp;
449     DIR *dir;
450 #endif
451     int ret = 0;
452
453     /* Notes for the human reader */
454     assert(asn);
455     assert(skeletons_dir);
456
457     /*
458      * Figure out the standard-modules directory.
459      */
460     target_dir_len = strlen(skeletons_dir) + sizeof("/standard-modules") - 1;
461     target_dir = malloc(target_dir_len + 1);
462     assert(target_dir);
463     snprintf(target_dir, target_dir_len + 1, "%s/standard-modules",
464              skeletons_dir);
465
466 #ifdef _WIN32
467     len = target_dir_len + sizeof("/*.asn1");
468     pattern = malloc(len);
469     assert(pattern);
470     snprintf(pattern, len, "%s/*.asn1", target_dir);
471     dir = _findfirst(pattern, &c_file);
472     if(dir == -1L) {
473 #else
474     dir = opendir(target_dir);
475     if(!dir) {
476 #endif
477         fprintf(stderr, "WARNING: Cannot find standard modules in %s\n",
478                 target_dir);
479         return -1;
480     }
481
482 #ifdef _WIN32
483     do {
484         filename = c_file.name;
485 #else
486     while((dp = readdir(dir))) {
487         filename = dp->d_name;
488 #endif
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);
495         filename = fullname;
496
497         new_asn = asn1p_parse_file(filename, A1P_NOFLAGS);
498         if(new_asn == NULL) {
499             fprintf(stderr, "WARNING: Cannot parse standard module \"%s\"\n",
500                     filename);
501             ret = -1;
502             continue;
503         }
504
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);
509         }
510         asn1p_delete(new_asn);
511         asn1p_lex_destroy();
512
513 #ifdef _WIN32
514     } while(_findnext(dir, &c_file) == 0);
515     _findclose(dir);
516 #else
517         free(fullname);
518     } /* while(readdir()) */
519     closedir(dir);
520 #endif
521
522 #ifdef _WIN32
523     free(pattern);
524 #endif
525     free(target_dir);
526
527     return ret;
528 }
529
530 /*
531  * Print the usage screen and exit(EX_USAGE).
532  */
533 static void __attribute__((noreturn))
534 usage(const char *av0) {
535     /* clang-format off */
536         fprintf(stderr,
537 "ASN.1 Compiler, v" VERSION "\n" COPYRIGHT
538 "Usage: %s [options] file ...\n"
539 "Options:\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"
542 "\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"
549 "\n"
550
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"
556 "\n"
557
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"
568 "\n"
569
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"
575 "\n"
576
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"
580
581         ,
582         a1c_basename(av0, NULL), DATADIR);
583     /* clang-format on */
584     exit(EX_USAGE);
585 }