/****************************************************************************** * Copyright (c) 2000-2021 Ericsson Telecom AB * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v2.0 * which accompanies this distribution, and is available at * https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.html * * Contributors: * * Baji, Laszlo * Balasko, Jeno * Baranyi, Botond * Beres, Szabolcs * Cserveni, Akos * Czerman, Oliver * Delic, Adam * Feher, Csaba * Forstner, Matyas * Kovacs, Ferenc * Kremer, Peter * Lovassy, Arpad * Ormandi, Matyas * Raduly, Csaba * Szabados, Kristof * Szabo, Bence Janos * Szabo, Janos Zoltan – initial implementation * Zalanyi, Balazs Andor * ******************************************************************************/ /* Main program for the merged compiler */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <ctype.h> #include <errno.h> #include <vector> #include <sstream> #if defined SOLARIS || defined SOLARIS8 # include <sys/utsname.h> #endif #ifdef USAGE_STATS #include "../common/usage_stats.hh" #include <signal.h> #endif #include "../common/dbgnew.hh" #include "../common/path.h" #include "../common/version_internal.h" #include "../common/userinfo.h" #include "datatypes.h" #include "main.hh" #include "asn1/asn1_preparser.h" #include "asn1/asn1.hh" #include "ttcn3/ttcn3_preparser.h" #include "ttcn3/compiler.h" #include "AST.hh" #include "asn1/AST_asn1.hh" #include "ttcn3/AST_ttcn3.hh" #include "CodeGenHelper.hh" #include "Stopwatch.hh" #include "ttcn3/Ttcn2Json.hh" #include "ttcn3/profiler.h" #include "../common/openssl_version.h" #ifdef LICENSE #include "../common/license.h" #endif using namespace Common; const char *output_dir = NULL; const char *tcov_file_name = NULL; static const char *profiler_file_name = NULL; static const char *file_list_file_name = NULL; tcov_file_list *tcov_files = NULL; expstring_t effective_module_lines = NULL; expstring_t effective_module_functions = NULL; size_t nof_top_level_pdus = 0; const char **top_level_pdu = NULL; boolean generate_skeleton = FALSE, force_overwrite = FALSE, include_line_info = FALSE, include_location_info = FALSE, duplicate_underscores = FALSE, parse_only = FALSE, semantic_check_only = FALSE, output_only_linenum = FALSE, default_as_optional = FALSE, enable_set_bound_out_param = FALSE, use_runtime_2 = FALSE, gcc_compat = FALSE, asn1_xer = FALSE, check_subtype = TRUE, suppress_context = FALSE, display_up_to_date = FALSE, implicit_json_encoding = FALSE, json_refs_for_all_types = TRUE, force_gen_seof = FALSE, omit_in_value_list = FALSE, warnings_for_bad_variants = FALSE, debugger_active = FALSE, legacy_unbound_union_fields = FALSE, split_to_slices = FALSE, legacy_untagged_union, disable_user_info, legacy_codec_handling = FALSE, realtime_features = FALSE, oop_features = FALSE, charstring_compat = FALSE; // Default code splitting mode is set to 'no splitting'. CodeGenHelper::split_type code_splitting_mode = CodeGenHelper::SPLIT_NONE; #if defined SOLARIS || defined SOLARIS8 /** Automatic detection of Solaris version based on uname() system call. * Distinguishing is needed because some socket functions use socklen_t * (which is an alias for unsigned int) as length arguments on Solaris 8. * On Solaris 2.6 the argument type is simply int and no socklen_t or other * alias exists. * Note: It was discovered later that Solaris 7 (which is used rarely within * Ericsson) already uses socklen_t thus the SOLARIS8 platform identifier is a * bit misleading. */ static const char *get_platform_string(void) { struct utsname name; int major, minor; if (uname(&name) < 0) { WARNING("System call uname() failed: %s", strerror(errno)); errno = 0; return "SOLARIS"; } if (sscanf(name.release, "%d.%d", &major, &minor) == 2 && major == 5) { if (minor <= 6) return "SOLARIS"; else return "SOLARIS8"; } else { ERROR("Invalid OS release: %s", name.release); return "SOLARIS"; } } #elif defined LINUX #define get_platform_string() "LINUX" #elif defined FREEBSD #define get_platform_string() "FREEBSD" #elif defined WIN32 #define get_platform_string() "WIN32" #elif defined INTERIX #define get_platform_string() "INTERIX" #else #error Platform was not set. #endif const char *expected_platform = get_platform_string(); /// "The" AST. Modules *modules = NULL; // Features can be disabled in the license or by commandline switches static bool raw_disabled = false, ber_disabled = false, per_disabled = false, text_disabled = false, xer_disabled = false, json_disabled = false, oer_disabled = false; static bool attribute_validation_disabled = FALSE; #ifdef LICENSE static bool has_raw_feature = false, has_ber_feature = false, has_per_feature = false, has_text_feature = false, has_xer_feature = false; #endif boolean enable_raw() { if (raw_disabled) return FALSE; #ifdef LICENSE if (!has_raw_feature) { WARNING("The license key does not allow the generation of " "RAW encoder/decoder functions."); raw_disabled = true; return FALSE; } #endif return TRUE; } boolean enable_ber() { if (ber_disabled) return FALSE; #ifdef LICENSE if (!has_ber_feature) { WARNING("The license key does not allow the generation of " "BER encoder/decoder functions."); ber_disabled = true; return FALSE; } #endif return TRUE; } boolean enable_per() { if (per_disabled) return FALSE; #ifdef LICENSE if (!has_per_feature) { WARNING("The license key does not allow the generation of " "PER encoder/decoder functions."); per_disabled = true; return FALSE; } #endif return TRUE; } boolean enable_text() { if (text_disabled) return FALSE; #ifdef LICENSE if (!has_text_feature) { WARNING("The license key does not allow the generation of " "TEXT encoder/decoder functions."); text_disabled = true; return FALSE; } #endif return TRUE; } boolean enable_xer() { if (xer_disabled) return FALSE; #ifdef LICENSE if (!has_xer_feature) { WARNING("The license key does not allow the generation of " "XER encoder/decoder functions."); xer_disabled = true; return FALSE; } #endif return TRUE; } boolean enable_json() { return !json_disabled; } boolean enable_oer() { return !oer_disabled; } boolean disable_attribute_validation() { if (attribute_validation_disabled) return TRUE; return FALSE; } char *canonize_input_file(const char *path_name) { switch (get_path_status(path_name)) { case PS_NONEXISTENT: ERROR("Input file `%s' does not exist.", path_name); return NULL; case PS_DIRECTORY: ERROR("Argument `%s' is a directory.", path_name); return NULL; default: break; } char *dir_name = get_dir_from_path(path_name); char *abs_dir = get_absolute_dir(dir_name, NULL, true); Free(dir_name); char *file_name = get_file_from_path(path_name); char *ret_val = compose_path_name(abs_dir, file_name); Free(abs_dir); Free(file_name); return ret_val; } struct module_struct { const char *file_name; char *absolute_path; Module::moduletype_t module_type; bool need_codegen; /**< Code is generated for a module if - the module appears on the command line after the dash, or - there is no dash (code is generated for all modules) */ }; static void add_module(size_t& n_modules, module_struct*& module_list, const char *file_name, Module::moduletype_t module_type) { char *absolute_path = canonize_input_file(file_name); if (absolute_path == NULL) return; for (size_t i = 0; i < n_modules; i++) { const module_struct *module = module_list + i; if (module->module_type == module_type && !strcmp(module->absolute_path, absolute_path)) { ERROR("Input file `%s' was given more than once.", file_name); Free(absolute_path); return; } } module_list = (module_struct*) Realloc(module_list, (n_modules + 1) * sizeof(module_struct)); module_struct *module = module_list + n_modules; module->file_name = file_name; module->absolute_path = absolute_path; module->module_type = module_type; module->need_codegen = false; n_modules++; } const char *get_tcov_file_name(const char *file_name) { tcov_file_list *tcov_file = tcov_files; expstring_t file_name_pp = mputprintf(NULL, "%spp", file_name); while (tcov_file != NULL) { // This name can be a `.ttcnpp' too. const char *real_file_name = static_cast<const char *>(tcov_file->file_name); if (!strcmp(file_name, real_file_name) || !strcmp(static_cast<const char *>(file_name_pp), real_file_name)) { Free(file_name_pp); return real_file_name; } tcov_file = tcov_file->next; } Free(file_name_pp); return NULL; } boolean in_tcov_files(const char *file_name) { return get_tcov_file_name(file_name) ? TRUE : FALSE; } static bool check_file_list(const char *file_name, module_struct *module_list, size_t n_modules, tcov_file_list *&file_list_head) { FILE *fp = fopen(file_name, "r"); if (fp == NULL) { ERROR("File `%s' does not exist.", file_name); return false; } #ifndef PATH_MAX #define PATH_MAX 1024 #endif char line[PATH_MAX]; bool unlisted_files = false; while (fgets(line, sizeof(line), fp) != NULL) { // Remove trailing '\n'. size_t line_len = strlen(line) - 1; if (line[line_len] == '\n') line[line_len] = 0; // Handle `.ttcnpp' files in input file. if (line_len > 1) { char last = line[line_len - 1]; char before_last = line[line_len - 2]; if (last == 'p' && before_last == 'p') line_len -= 2; } if (line_len < 1) continue; size_t i = 0; for (; i < n_modules; ++i) { const module_struct *module = module_list + i; if (!strncmp(module->file_name, line, line_len)) { tcov_file_list *next_file = (tcov_file_list*)Malloc(sizeof(tcov_file_list)); next_file->next = file_list_head; // We'll need the `.ttcnpp' file name. next_file->file_name = mcopystr(line); file_list_head = next_file; break; } } if (i == n_modules) { ERROR("File `%s' was listed in `%s', but not in the command line.", line, file_name); unlisted_files = true; } } fclose(fp); if (unlisted_files) { while (file_list_head != NULL) { tcov_file_list *next_file = file_list_head->next; Free(file_list_head->file_name); Free(file_list_head); file_list_head = next_file; } file_list_head = NULL; } return !unlisted_files; } static boolean is_valid_asn1_filename(const char* file_name) { // only check the actual file name, not the whole path const char* file_name_start = strrchr(file_name, '/'); if (0 == strchr(file_name_start != NULL ? file_name_start : file_name, '-' )) { return TRUE; } return FALSE; } static void usage() { fprintf(stderr, "\n" "usage: %s [-abcdDeEfFghiIjklLMnNOpqrRsStuwxXyY0] [-J file] [-K file] [-z file] [-V verb_level]\n" " [-o dir] [-U none|type|'number'] [-P modulename.top_level_pdu_name] [-Q number] ...\n" " [-T] module.ttcn [-A] module.asn ...\n" " or %s -v\n" " or %s --ttcn2json [-jf] ... [-T] module.ttcn [-A] module.asn ... [- schema.json]\n" "\n" "OPTIONS:\n" " -a: force XER in ASN.1 files\n" " -b: disable BER encoder/decoder functions\n" " -B: allow selected union field to be unbound (legacy behavior)\n" " -c: write out checksums in case of error\n" " -d: treat default fields as omit\n" " -D: disable user and time information generation in the generated files\n" " -e: enforce legacy handling of 'encode' and 'variant' attributes\n" " -E: display only warnings for unrecognized encoding variants\n" " -f: force overwriting of output files\n" " -F: force generation of records of/sets of basic types and remove their compatibility\n" " -g: emulate GCC error/warning message format\n" " -h: allow unsafe universal charstring to charstring conversion\n" " -i: use only line numbers in error/warning messages\n" " -I: enable real-time testing features\n" " -j: disable JSON encoder/decoder functions\n" " -J file: read input files from file\n" " -k: enable object-oriented features\n" " -K file: enable selective code coverage\n" " -l: include source line info in C++ code\n" " -L: add source line info for logging\n" " -M: allow 'omit' in template value lists (legacy behavior)\n" " -n: activate debugger (generates extra code for debugging)\n" " -N: ignore UNTAGGED encoding instruction on top level unions (legacy behaviour)\n" " -o dir: output files will be placed into dir\n" " -O: disable OER encoder/decoder functions\n" " -p: parse only (no semantic check or code generation)\n" " -P pduname: define top-level pdu\n" " -q: suppress all messages (quiet mode)\n" " -Qn: quit after n errors\n" " -r: disable RAW encoder/decoder functions\n" " -R: use function test runtime (TITAN_RUNTIME_2)\n" " -s: parse and semantic check only (no code generation)\n" " -S: suppress context information\n" " -t: generate skeletons for test ports and external classes\n" " -u: duplicate underscores in file names\n" " -U none|type|'number': select code splitting mode for the generated C++ code\n" " -V verb_level: set verbosity level bitmask (decimal)\n" " -w: suppress warnings\n" " -x: disable TEXT encoder/decoder functions\n" " -X: disable XER encoder/decoder functions\n" " -y: disable subtype checking\n" " -Y: enforce legacy behaviour for \"out\" function parameters (see refguide)\n" " -z file: enable profiling and code coverage for the TTCN-3 files in the argument\n" " -0: disable attribute checks for `encvalue' and `decvalue'\n" " -T file: force interpretation of file as TTCN-3 module\n" " -A file: force interpretation of file as ASN.1 module\n" " -v: show version\n" " --ttcn2json: generate JSON schema from input modules\n" "JSON schema generator options:\n" " -j: only include types with JSON encoding\n" " -f: only generate references to types with JSON encoding/decoding functions\n", argv0, argv0, argv0); } #define SET_FLAG(x) if (x##flag) {\ ERROR("Flag -" #x " was specified more than once.");\ errflag = true;\ } else x##flag = true extern int ttcn3_debug; extern int asn1_yydebug; extern int pattern_yydebug; extern int pattern_unidebug; extern int rawAST_debug; extern int coding_attrib_debug; int main(int argc, char *argv[]) { argv0 = argv[0]; #ifndef NDEBUG asn1_yydebug = !! getenv("DEBUG_ASN1"); ttcn3_debug = !! getenv("DEBUG_TTCN"); pattern_unidebug= pattern_yydebug = !! getenv("DEBUG_PATTERN"); rawAST_debug = !! getenv("DEBUG_RAW"); coding_attrib_debug = !!getenv("DEBUG_ATRIB") || getenv("DEBUG_ATTRIB"); #endif #ifdef MEMORY_DEBUG #if defined(__CYGWIN__) || defined(INTERIX) //nothing to do #else debug_new_counter.set_program_name(argv0); #endif #endif if (argc == 1) { fputs("TTCN-3 and ASN.1 Compiler for the TTCN-3 Test Executor, version " PRODUCT_NUMBER "\n", stderr); usage(); return EXIT_FAILURE; } bool Aflag = false, Lflag = false, Yflag = false, Pflag = false, Tflag = false, Vflag = false, bflag = false, cflag = false, fflag = false, iflag = false, lflag = false, oflag = false, pflag = false, qflag = false, rflag = false, sflag = false, tflag = false, uflag = false, vflag = false, wflag = false, xflag = false, dflag = false, Xflag = false, Rflag = false, gflag = false, aflag = false, s0flag = false, Cflag = false, yflag = false, Uflag = false, Qflag = false, Sflag = false, Kflag = false, jflag = false, zflag = false, Fflag = false, Mflag = false, Eflag = false, nflag = false, Bflag = false, errflag = false, print_usage = false, ttcn2json = false, Nflag = false, Dflag = false, eflag = false, Oflag = false, Iflag = false, kflag = false, hflag = false; CodeGenHelper cgh; bool asn1_modules_present = false; #ifdef LICENSE bool ttcn3_modules_present = false; #endif size_t n_modules = 0; module_struct *module_list = NULL; char* json_schema_name = NULL; size_t n_files_from_file = 0; char ** files_from_file = NULL; if (0 == strcmp(argv[1], "--ttcn2json")) { ttcn2json = true; display_up_to_date = TRUE; implicit_json_encoding = TRUE; // ttcn2json uses the legacy handling of JSON encode attributes and variants legacy_codec_handling = TRUE; for (int i = 2; i < argc; ++i) { // A dash (-) is used to separate the schema file name from the input files if (0 == strcmp(argv[i], "-")) { if (i == argc - 2) { json_schema_name = mcopystr(argv[i + 1]); } else { ERROR("Expected JSON schema name (1 argument) after option `--ttcn2json' and `-'"); errflag = true; } break; } else if (0 == strcmp(argv[i], "-A")) { ++i; if (i == argc) { ERROR("Option `-A' must be followed by an ASN.1 file name"); errflag = true; break; } add_module(n_modules, module_list, argv[i], Module::MOD_ASN); asn1_modules_present = true; } else if (0 == strcmp(argv[i], "-T")) { ++i; if (i == argc) { ERROR("Option `-T' must be followed by a TTCN-3 file name"); errflag = true; break; } add_module(n_modules, module_list, argv[i], Module::MOD_TTCN); } else if (0 == strcmp(argv[i], "-j")) { implicit_json_encoding = FALSE; } else if (0 == strcmp(argv[i], "-f")) { json_refs_for_all_types = FALSE; } else if (0 == strcmp(argv[i], "-fj") || 0 == strcmp(argv[i], "-jf")) { implicit_json_encoding = FALSE; json_refs_for_all_types = FALSE; } else if (argv[i][0] == '-') { ERROR("Invalid option `%s' after option `--ttcn2json'", argv[i]); print_usage = true; errflag = true; break; } else { add_module(n_modules, module_list, argv[i], Module::MOD_UNKNOWN); } } if (!errflag && 0 == n_modules) { ERROR("No TTCN-3 or ASN.1 modules specified after option `--ttcn2json'"); errflag = true; print_usage = true; } if (!errflag && NULL == json_schema_name) { // Create the schema name using the first TTCN-3 or ASN.1 file's name const module_struct& first = module_list[0]; if (0 == strncmp(first.file_name + strlen(first.file_name) - 4, ".asn", 4)) { json_schema_name = mcopystrn(first.file_name, strlen(first.file_name) - 4); json_schema_name = mputstrn(json_schema_name, ".json", 5); } else if (0 == strncmp(first.file_name + strlen(first.file_name) - 5, ".ttcn", 5)) { json_schema_name = mcopystrn(first.file_name, strlen(first.file_name) - 5); json_schema_name = mputstrn(json_schema_name, ".json", 5); } else { json_schema_name = mprintf("%s.json", first.file_name); } } } if (!ttcn2json) { for ( ; ; ) { int c = getopt(argc, argv, "aA:bBcC:dDeEfFghiIjJ:kK:lLMnNo:OpP:qQ:rRsStT:uU:vV:wxXyYz:0-"); if (c == -1) break; switch (c) { case 'a': SET_FLAG(a); asn1_xer = TRUE; break; case 'A': Aflag = true; add_module(n_modules, module_list, optarg, Module::MOD_ASN); asn1_modules_present = true; break; case 'C': SET_FLAG(C); expected_platform = optarg; break; case 'L': SET_FLAG(L); include_location_info = TRUE; break; case 'P': Pflag = true; nof_top_level_pdus++; top_level_pdu=(const char**) Realloc(top_level_pdu, nof_top_level_pdus*sizeof(*top_level_pdu)); top_level_pdu[nof_top_level_pdus-1] = optarg; break; case 'T': Tflag = true; add_module(n_modules, module_list, optarg, Module::MOD_TTCN); #ifdef LICENSE ttcn3_modules_present = true; #endif break; case 'V': SET_FLAG(V); /* set verbosity level bitmask */ if (isdigit(optarg[0])) { verb_level = atoi(optarg); // don't bother with overflow errno = 0; } else { ERROR("Option `-V' requires a decimal number as argument instead of " "`%s'.", optarg); errflag = true; } break; case 'b': SET_FLAG(b); ber_disabled = TRUE; break; case 'c': SET_FLAG(c); break; case 'd': SET_FLAG(d); default_as_optional = TRUE; break; case 'D': SET_FLAG(D); disable_user_info = TRUE; break; case 'f': SET_FLAG(f); force_overwrite = TRUE; break; case 'g': SET_FLAG(g); gcc_compat = TRUE; break; case 'h': SET_FLAG(h); charstring_compat = TRUE; break; case 'i': SET_FLAG(i); output_only_linenum = TRUE; break; case 'I': SET_FLAG(I); realtime_features = TRUE; break; case 'J': file_list_file_name = optarg; break; case 'k': SET_FLAG(k); oop_features = TRUE; break; case 'K': SET_FLAG(K); tcov_file_name = optarg; break; case 'l': SET_FLAG(l); include_line_info = TRUE; break; case 'o': SET_FLAG(o); output_dir = optarg; break; case 'O': SET_FLAG(O); oer_disabled = TRUE; break; case 'Y': SET_FLAG(Y); enable_set_bound_out_param = TRUE; break; case 'p': SET_FLAG(p); parse_only = TRUE; break; case 'q': SET_FLAG(q); /* quiet; suppress all message */ verb_level = 0; break; case 'r': SET_FLAG(r); raw_disabled = TRUE; break; case 'R': SET_FLAG(R); use_runtime_2 = TRUE; break; case 's': SET_FLAG(s); semantic_check_only = TRUE; break; case 'S': SET_FLAG(S); suppress_context = TRUE; break; case '0': SET_FLAG(s0); attribute_validation_disabled = TRUE; break; case 't': SET_FLAG(t); generate_skeleton = TRUE; break; case 'u': SET_FLAG(u); duplicate_underscores = TRUE; break; case 'U': SET_FLAG(U); if (!cgh.set_split_mode(optarg)) { ERROR("Wrong code splitting option: '%s'. Valid values are: 'none', " "'type', or a positive number.", optarg); errflag = true; } break; case 'v': SET_FLAG(v); break; case 'w': SET_FLAG(w); /* suppress warnings and "not supported" messages */ verb_level &= ~(1|2); break; case 'x': SET_FLAG(x); text_disabled = TRUE; break; case 'X': SET_FLAG(X); xer_disabled = TRUE; break; case 'j': SET_FLAG(j); json_disabled = TRUE; break; case 'y': SET_FLAG(y); check_subtype = FALSE; break; case 'z': SET_FLAG(z); profiler_file_name = optarg; break; case 'F': SET_FLAG(F); force_gen_seof = TRUE; break; case 'M': SET_FLAG(M); omit_in_value_list = TRUE; break; case 'E': SET_FLAG(E); warnings_for_bad_variants = TRUE; break; case 'n': SET_FLAG(n); debugger_active = TRUE; break; case 'N': SET_FLAG(N); legacy_untagged_union = TRUE; break; case 'B': SET_FLAG(B); legacy_unbound_union_fields = TRUE; break; case 'e': SET_FLAG(e); legacy_codec_handling = TRUE; break; case 'Q': { long max_errs; SET_FLAG(Q); printf("Q: %s\n", optarg); errno = 0; max_errs = strtol(optarg, (char**)NULL, 10); if (errno != 0 || (long)(int)max_errs != max_errs) { // does not fit into int ERROR("Invalid value %s: %s", optarg, strerror(errno)); errflag = true; } else if (max_errs < 0) { ERROR("Negative value %s not allowed", optarg); errflag = true; } else { // all good if (max_errs == 0) max_errs = 1; } Error_Context::set_max_errors(max_errs); break; } case '-': if (!strcmp(argv[optind], "--ttcn2json")) { ERROR("Option `--ttcn2json' is only allowed as the first option"); } else { ERROR("Invalid option: `%s'", argv[optind]); } // no break default: errflag = true; print_usage = true; break; } } /* Checking incompatible options */ if (vflag) { if (Aflag || Lflag || Pflag || Tflag || Vflag || Yflag || bflag || fflag || iflag || lflag || oflag || pflag || qflag || rflag || sflag || tflag || uflag || wflag || xflag || Xflag || Rflag || Uflag || yflag || Kflag || jflag || zflag || Fflag || Mflag || Eflag || nflag || Bflag || Dflag || eflag || Oflag || Iflag || s0flag || kflag || hflag) { errflag = true; print_usage = true; } } else { if (pflag) { if (sflag) { ERROR("Options `-p' and `-s' are incompatible with each other."); // totally confusing: exit immediately errflag = true; } } if (Kflag && !Lflag) { ERROR("Source line information `-L' is necessary for code coverage `-K'."); errflag = true; } if (zflag && !Lflag) { ERROR("Source line information `-L' is necessary for profiling `-z'."); errflag = true; } if (nflag && !Lflag) { ERROR("Source line information `-L' is necessary for debugging `-n'."); errflag = true; } if (iflag && gflag) { WARNING("Option `-g' overrides `-i'."); iflag = false; // -g gives more information } if (oflag && get_path_status(output_dir) != PS_DIRECTORY) { ERROR("The argument of -o switch (`%s') must be a directory.", output_dir); errflag = true; } if (s0flag && !sflag) { ERROR("Option `-0' requires option `-s'"); errflag = true; } if (file_list_file_name != NULL) { FILE *fp = fopen(file_list_file_name, "r"); if (fp != NULL) { char buff[1024]; // We store the -A and -T here too while (fscanf(fp, "%s", buff) == 1) { n_files_from_file++; files_from_file = (char**) Realloc(files_from_file, n_files_from_file * sizeof(*files_from_file)); files_from_file[n_files_from_file - 1] = mcopystr(buff); } fclose(fp); } else { ERROR("Cannot open file `%s' for reading: %s", file_list_file_name, strerror(errno)); errno = 0; errflag = true; } bool next_is_asn1 = false; bool next_is_ttcn = false; for (size_t i = 0; i < n_files_from_file; i++) { // Check if -A or -T is present and continue to the next word if yes if (next_is_ttcn == false && next_is_asn1 == false) { if (strcmp(files_from_file[i], "-A") == 0) { next_is_asn1 = true; continue; } else if (strcmp(files_from_file[i], "-T") == 0) { next_is_ttcn = true; continue; } } Module::moduletype_t module_type = Module::MOD_UNKNOWN; const char* file = files_from_file[i]; if (next_is_asn1) { module_type = Module::MOD_ASN; next_is_asn1 = false; } else if(next_is_ttcn) { module_type = Module::MOD_TTCN; next_is_ttcn = false; } else if (strlen(files_from_file[i]) > 2) { // The -A or -T can be given as -TMyTtcnfile.ttcn too if (files_from_file[i][0] == '-') { if (files_from_file[i][1] == 'A') { file = files_from_file[i] + 2; module_type = Module::MOD_ASN; } else if (files_from_file[i][1] == 'T') { file = files_from_file[i] + 2; module_type = Module::MOD_TTCN; } } } if (module_type == Module::MOD_TTCN) { #ifdef LICENSE ttcn3_modules_present = true; #endif } else if (module_type == Module::MOD_ASN) { asn1_modules_present = true; } add_module(n_modules, module_list, file, module_type); } } if (optind == argc && n_modules == 0 && n_files_from_file == 0) { ERROR("No input TTCN-3 or ASN.1 module was given."); errflag = true; } } } // if (!ttcn2json) if (errflag) { if (print_usage) usage(); Free(json_schema_name); return EXIT_FAILURE; } if (vflag) { fputs("TTCN-3 and ASN.1 Compiler for the TTCN-3 Test Executor\n" "Version: " VERSION_STRING "\n" "Build date: " __DATE__ " " __TIME__ "\n" "Compiled with: " C_COMPILER_VERSION "\n", stderr); fputs("Using ", stderr); fputs(openssl_version_str(), stderr); if (strlen(GIT_COMMIT_ID)) { fputs("\nCommit id: ", stderr); fputs(GIT_COMMIT_ID, stderr); } fputs("\n\n" COPYRIGHT_STRING "\n\n", stderr); #ifdef LICENSE print_license_info(); fputs("\n\n", stderr); #endif return EXIT_SUCCESS; } #ifdef LICENSE init_openssl(); license_struct lstr; load_license(&lstr); int license_valid = verify_license(&lstr); free_openssl(); if (!license_valid) { free_license(&lstr); exit(EXIT_FAILURE); } #endif if (!ttcn2json) { /* the position of '-' switch in argv list */ int dash_position = -1; /* Add the remaining files until switch '-' to the module_list */ for(int i = optind; i < argc; i++) { if (strcmp(argv[i], "-")) add_module(n_modules, module_list, argv[i], Module::MOD_UNKNOWN); else { dash_position = i; break; } } if (dash_position == -1) { /** if '-' was not present in the command line code should be generated for * all modules */ for (size_t i = 0; i < n_modules; i++) module_list[i].need_codegen = true; } else { for (int i = dash_position + 1; i < argc; i++) { char *absolute_path = canonize_input_file(argv[i]); if (absolute_path == NULL) continue; bool found = false; for (size_t j = 0; j < n_modules; j++) { module_struct *module = module_list + j; if (!strcmp(module->absolute_path, absolute_path)) { module->need_codegen = true; found = true; // do not stop: the same file may be present on the list twice // (as both ASN.1 and TTCN-3 module) } } Free(absolute_path); if (!found) { ERROR("File `%s' was not given before the `-' switch for selective " "code generation.", argv[i]); // go further (i.e. check all files after the `-') } } } } // if (!ttcn2json) { STOPWATCH("Determining module types"); // check the readability of all files and // determine the type of unknown modules for (size_t i = 0; i < n_modules; i++) { module_struct *module = module_list + i; FILE *fp = fopen(module->file_name, "r"); if (fp != NULL) { if (module->module_type == Module::MOD_UNKNOWN) { // try the ASN.1 and TTCN-3 preparsers // To display the module name in the error message char *module_name = NULL; boolean asn1_module = is_asn1_module(module->file_name, fp, &module_name); boolean ttcn3_module = is_ttcn3_module(module->file_name, fp, NULL); if (asn1_module) { if (!is_valid_asn1_filename (module->file_name)) { ERROR("The file name `%s' (without suffix) shall be identical to the module name `%s'.\n" "If the name of the ASN.1 module contains a hyphen, the corresponding " "file name shall contain an underscore character instead.", module->file_name, module_name); } Free(module_name); if (ttcn3_module) { ERROR("File `%s' looks so strange that it can contain both an " "ASN.1 and a TTCN-3 module. Use the command-line switch `-A' or " "`-T' to set its type.", module->file_name); } else { bool found = false; for (size_t j = 0; j < n_modules; j++) { module_struct *module2 = module_list + j; if (module2->module_type == Module::MOD_ASN && !strcmp(module->absolute_path, module2->absolute_path)) { found = true; break; } } if (found) { ERROR("Input file `%s' was given more than once.", module->file_name); } else { module->module_type = Module::MOD_ASN; asn1_modules_present = true; } } } else if (ttcn3_module) { bool found = false; for (size_t j = 0; j < n_modules; j++) { module_struct *module2 = module_list + j; if (module2->module_type == Module::MOD_TTCN && !strcmp(module->absolute_path, module2->absolute_path)) { found = true; break; } } if (found) { ERROR("Input file `%s' was given more than once.", module->file_name); } else { module->module_type = Module::MOD_TTCN; #ifdef LICENSE ttcn3_modules_present = true; #endif } } else { ERROR("Cannot recognize file `%s' as an ASN.1 or TTCN-3 module. " "Use the command-line switch `-A' or `-T' to set its type.", module->file_name); } } fclose(fp); } else { ERROR("Cannot open input file `%s' for reading: %s", module->file_name, strerror(errno)); errno = 0; // do not invoke the real parsers on that file module->module_type = Module::MOD_UNKNOWN; } } } #if defined(MINGW) if (!semantic_check_only) { NOTIFY("On native win32 builds code generation is disabled."); semantic_check_only = TRUE; } #endif #ifdef LICENSE /* Checking of required license features */ if (asn1_modules_present && !check_feature(&lstr, FEATURE_ASN1)) { ERROR("The license key does not allow the parsing of " "ASN.1 modules."); return EXIT_FAILURE; } if (ttcn3_modules_present && !check_feature(&lstr, FEATURE_TTCN3)) { ERROR("The license key does not allow the parsing of " "TTCN-3 modules."); return EXIT_FAILURE; } if (!parse_only && !semantic_check_only && !check_feature(&lstr, FEATURE_CODEGEN)) { WARNING("The license key does not allow the generation of " "C++ code."); semantic_check_only = TRUE; } if (generate_skeleton && !check_feature(&lstr, FEATURE_TPGEN)) { WARNING("The license key does not allow the generation of " "Test Port skeletons."); generate_skeleton = FALSE; } has_raw_feature = check_feature(&lstr, FEATURE_RAW); has_ber_feature = check_feature(&lstr, FEATURE_BER); has_per_feature = check_feature(&lstr, FEATURE_PER); has_text_feature = check_feature(&lstr, FEATURE_TEXT); has_xer_feature = check_feature(&lstr, FEATURE_XER); free_license(&lstr); #endif if (Kflag && !check_file_list(tcov_file_name, module_list, n_modules, tcov_files)) { ERROR("Error while processing `%s' provided for code coverage data " "generation.", tcov_file_name); return EXIT_FAILURE; } if (zflag) { tcov_file_list *file_list_head = NULL; if(!check_file_list(profiler_file_name, module_list, n_modules, file_list_head)) { ERROR("Error while processing `%s' provided for profiling and code coverage.", profiler_file_name); return EXIT_FAILURE; } init_profiler_data(file_list_head); } { STOPWATCH("Parsing modules"); // asn1_yydebug=1; if (asn1_modules_present) asn1_init(); modules = new Common::Modules(); for (size_t i = 0; i < n_modules; i++) { const module_struct *module = module_list + i; switch (module->module_type) { case Module::MOD_ASN: asn1_parse_file(module->file_name, module->need_codegen); break; case Module::MOD_TTCN: ttcn3_parse_file(module->file_name, module->need_codegen); break; default: // MOD_UNKNOWN ? break; } } for (size_t i = 0; i < n_modules; i++) Free(module_list[i].absolute_path); Free(module_list); } if (!parse_only && 0 == Error_Context::get_error_count()) { NOTIFY("Checking modules..."); { STOPWATCH("Semantic check"); modules->chk(); } } if (verb_level > 7) modules->dump(); int ret_val = EXIT_SUCCESS; unsigned int error_count = Error_Context::get_error_count(); if (error_count > 0) ret_val = EXIT_FAILURE; #ifdef USAGE_STATS pthread_t stats_thread = 0; thread_data* stats_data = NULL; #endif if (parse_only || semantic_check_only) { // print detailed statistics Error_Context::print_error_statistics(); } else { if (error_count == 0) { #ifdef USAGE_STATS { // Ignore SIGPIPE signals struct sigaction sig_act; if (sigaction(SIGPIPE, NULL, &sig_act)) ERROR("System call sigaction() failed when getting signal " "handling information for %s.", "SIGINT"); sig_act.sa_handler = SIG_IGN; sig_act.sa_flags = 0; if (sigaction(SIGPIPE, &sig_act, NULL)) ERROR("System call sigaction() failed when disabling signal " "%s.", "SIGINT"); } std::stringstream stream; stream << "compiler"; std::set<ModuleVersion> versions = modules->getVersionsWithProductNumber(); if (!versions.empty()) { stream << "&products="; for (std::set<ModuleVersion>::iterator it = versions.begin(); it != versions.end(); ++it) { if (it != versions.begin()) { stream << ","; } stream << it->toString(); } } stats_data = new thread_data; stats_data->sndr = new HttpSender; stats_thread = UsageData::getInstance().sendDataThreaded(stream.str(), stats_data); #endif if (ttcn2json) { NOTIFY("Generating JSON schema..."); STOPWATCH("Generating JSON schema"); // the Ttcn2Json constructor starts the process Ttcn::Ttcn2Json t2j(modules, json_schema_name); } else { NOTIFY("Generating code..."); STOPWATCH("Generating code"); modules->generate_code(cgh); report_nof_updated_files(); } } else { NOTIFY("Error%s found in the input module%s. Code will not be generated.", error_count > 1 ? "s" : "", n_modules > 1 ? "s" : ""); if (cflag) { modules->write_checksums(); } } } if (Kflag) { while (tcov_files != NULL) { tcov_file_list *next_file = tcov_files->next; Free(tcov_files->file_name); Free(tcov_files); tcov_files = next_file; } tcov_files = NULL; } delete modules; Free(top_level_pdu); if (asn1_modules_present) asn1_free(); Type::free_pools(); Common::Node::chk_counter(); Location::delete_source_file_names(); Free(json_schema_name); if (zflag) { free_profiler_data(); } for (size_t i = 0; i < n_files_from_file; i++) { Free(files_from_file[i]); } Free(files_from_file); // dbgnew.hh already does it: check_mem_leak(argv[0]); #ifdef USAGE_STATS if (stats_thread != 0) { // cancel the usage stats thread if it hasn't finished yet pthread_cancel(stats_thread); pthread_join(stats_thread, NULL); } if (stats_data != NULL) { if (stats_data->sndr != NULL) { delete stats_data->sndr; } delete stats_data; } #endif return ret_val; }