/* * %CopyrightBegin% * * Copyright Ericsson AB 1996-2021. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * %CopyrightEnd% */ #include "etc_common.h" #include "erl_driver.h" #include "erl_misc_utils.h" #ifdef __WIN32__ # include "erl_version.h" # include "init_file.h" # include #endif #define NO 0 #define YES 1 #define DEFAULT_PROGNAME "erl" #ifdef __WIN32__ #define INI_FILENAME L"erl.ini" #define INI_SECTION "erlang" #define DIRSEP "\\" #define PATHSEP ";" #define NULL_DEVICE "nul" #define BINARY_EXT "" #define DLL_EXT ".dll" #define EMULATOR_EXECUTABLE "beam.dll" #else #define PATHSEP ":" #define DIRSEP "/" #define NULL_DEVICE "/dev/null" #define BINARY_EXT "" #define EMULATOR_EXECUTABLE "beam" #endif #define QUOTE(s) s /* +M alloc_util allocators */ static const char plusM_au_allocs[]= { 'u', /* all alloc_util allocators */ 'B', /* binary_alloc */ 'I', /* literal_alloc */ 'D', /* std_alloc */ 'E', /* ets_alloc */ 'F', /* fix_alloc */ 'H', /* eheap_alloc */ 'L', /* ll_alloc */ 'R', /* driver_alloc */ 'S', /* sl_alloc */ 'T', /* temp_alloc */ 'Z', /* test_alloc */ '\0' }; /* +M alloc_util allocator specific arguments */ static char *plusM_au_alloc_switches[] = { "as", "asbcst", "atags", "acul", "acnl", "acfml", "cp", "e", "t", "lmbcs", "mbcgs", "mbsd", "mmbcs", "mmmbc", "mmsbc", "msbclt", "ramv", "rmbcmt", "rsbcmt", "rsbcst", "sbct", "smbcs", NULL }; /* +M other arguments */ static char *plusM_other_switches[] = { "ea", "ummc", "uycs", "usac", "im", "is", "it", "lpm", "Mamcbf", "Mrmcbf", "Mmcs", "Mscs", "Mscrfsd", "Msco", "Mscrpm", "Ye", "Ym", "Ytp", "Ytt", "Iscs", "Xscs", NULL }; /* +s arguments with values */ static char *pluss_val_switches[] = { "bt", "bwtdcpu", "bwtdio", "bwt", "cl", "ct", "ecio", "fwi", "tbt", "wct", "wtdcpu", "wtdio", "wt", "ws", "ss", "ssdcpu", "ssdio", "pp", "ub", NULL }; /* +h arguments with values */ static char *plush_val_switches[] = { "ms", "mbs", "pds", "max", "maxk", "maxel", "mqd", "", NULL }; /* +r arguments with values */ static char *plusr_val_switches[] = { "g", NULL }; /* +z arguments with values */ static char *plusz_val_switches[] = { "dbbl", "dntgc", "ebwt", NULL }; /* * Define sleep(seconds) in terms of Sleep() on Windows. */ #ifdef __WIN32__ #define sleep(seconds) Sleep(seconds*1000) #endif #define DEFAULT_SUFFIX "smp" void usage(const char *switchname); static void usage_format(char *format, ...); void start_epmd_daemon(char *epmd); void error(char* format, ...); /* * Local functions. */ static void usage_notsup(const char *switchname, const char *alt); static char **build_args_from_env(char *env_var); static char **build_args_from_string(char *env_var, int allow_comments); static void initial_argv_massage(int *argc, char ***argv); static void get_parameters(int argc, char** argv); static void add_arg(char *new_arg); static void add_args(char *first_arg, ...); static void ensure_EargsSz(int sz); static void add_Eargs(char *new_arg); static void *emalloc(size_t size); static void *erealloc(void *p, size_t size); static void efree(void *p); static char* strsave(char* string); static int is_one_of_strings(char *str, char *strs[]); static char *write_str(char *to, const char *from); static void get_home(void); static void add_epmd_port(void); #ifdef __WIN32__ static void get_start_erl_data(char *); static char* get_value(HKEY key, char* value_name, BOOL mustExit); static char* possibly_quote(char* arg); /* * Functions from win_erlexec.c */ int start_win_emulator(char* emu, char *startprog,char** argv, int start_detached); int start_emulator(char* emu, char*start_prog, char** argv, int start_detached); #endif /* * Variables. */ int nohup = 0; int keep_window = 0; static char **Eargsp = NULL; /* Emulator arguments (to appear first). */ static int EargsSz = 0; /* Size of Eargsp */ static int EargsCnt = 0; /* Number of emulator arguments. */ static char **argsp = NULL; /* Common arguments. */ static int argsCnt = 0; /* Number of common arguments */ static int argsSz = 0; /* Size of argsp */ static char tmpStr[10240]; /* Temporary string buffer. */ static int verbose = 0; /* If non-zero, print some extra information. */ static int start_detached = 0; /* If non-zero, the emulator should be * started detached (in the background). */ static const char* emu_type = NULL; /* Type of emulator (lcnt, valgrind, etc) */ static const char* emu_flavor = DEFAULT_SUFFIX; /* Flavor of emulator (smp, jit or emu) */ #ifdef __WIN32__ static char *start_emulator_program = NULL; /* For detached mode - erl.exe/werl.exe */ static char* key_val_name = ERLANG_VERSION; /* Used by the registry * access functions. */ static char* boot_script = NULL; /* used by option -start_erl and -boot */ static char** config_scripts = NULL; /* used by option -start_erl and -config */ static int config_script_cnt = 0; static int got_start_erl = 0; static HANDLE this_module_handle; static int run_werl; static WCHAR *utf8_to_utf16(unsigned char *bytes); static char *utf16_to_utf8(WCHAR *wstr); static WCHAR *latin1_to_utf16(char *str); #endif /* * Parameters to be fetched from the environment (Unix) * or the ini file (Win32). */ static char* bindir; /* Location of executables. */ static char* rootdir; /* Root location of Erlang installation. */ static char* emu; /* Emulator to run. */ static char* progname; /* Name of this program. */ static char* home; /* Path of user's home directory, if any. */ static void set_env(char *key, char *value) { #ifdef __WIN32__ WCHAR *wkey = latin1_to_utf16(key); WCHAR *wvalue = utf8_to_utf16(value); if (!SetEnvironmentVariableW(wkey, wvalue)) error("SetEnvironmentVariable(\"%s\", \"%s\") failed!", key, value); efree(wkey); efree(wvalue); #else size_t size = strlen(key) + 1 + strlen(value) + 1; char *str = emalloc(size); sprintf(str, "%s=%s", key, value); if (putenv(str) != 0) error("putenv(\"%s\") failed!", str); #ifdef HAVE_COPYING_PUTENV efree(str); #endif #endif } static char * get_env(char *key) { #ifdef __WIN32__ DWORD size = 32; WCHAR *value = NULL; WCHAR *wkey = latin1_to_utf16(key); char *res; while (1) { DWORD nsz; if (value) efree(value); value = emalloc(size*sizeof(WCHAR)); SetLastError(0); nsz = GetEnvironmentVariableW(wkey, value, size); if (nsz == 0 && GetLastError() == ERROR_ENVVAR_NOT_FOUND) { efree(value); efree(wkey); return NULL; } if (nsz <= size) { efree(wkey); res = utf16_to_utf8(value); efree(value); return res; } size = nsz; } #else return getenv(key); #endif } static void free_env_val(char *value) { #ifdef __WIN32__ if (value) free(value); #endif } /* * Add the type and architecture suffix to the program name if needed. * On Windows, we insert it just before ".DLL". */ static char* add_extra_suffixes(char *prog) { char *res; char *p; int len; #ifdef __WIN32__ char *dll_p; int dll = 0; #endif len = strlen(prog); /* Allocate enough extra space for suffixes */ p = emalloc(len + 100); res = p; p = write_str(p, prog); #ifdef __WIN32__ dll_p = res + len - 4; if (dll_p >= res) { if (dll_p[0] == '.' && (dll_p[1] == 'd' || dll_p[1] == 'D') && (dll_p[2] == 'l' || dll_p[2] == 'L') && (dll_p[3] == 'l' || dll_p[3] == 'L')) { p = dll_p; dll = 1; } } #endif if (emu_type && strcmp("opt",emu_type) != 0) { p = write_str(p, "."); p = write_str(p, emu_type); } p = write_str(p, "."); p = write_str(p, emu_flavor); #ifdef __WIN32__ if (dll) { p = write_str(p, DLL_EXT); } #endif return res; } #ifdef __WIN32__ static void add_boot_config(void) { int i; if (boot_script) add_args("-boot", boot_script, NULL); for (i = 0; i < config_script_cnt; i++) { add_args("-config", config_scripts[i], NULL); } } # define ADD_BOOT_CONFIG add_boot_config() #else # define ADD_BOOT_CONFIG #endif #define NEXT_ARG_CHECK_NAMED(Option) \ do { \ if (i+1 >= argc || strncmp(argv[i+1], "--", 3) == 0) \ usage(Option); \ } while(0) #define NEXT_ARG_CHECK() NEXT_ARG_CHECK_NAMED(argv[i]) #ifdef __WIN32__ __declspec(dllexport) int win_erlexec(int argc, char **argv, HANDLE module, int windowed) #else int main(int argc, char **argv) #endif { int haltAfterwards = 0; /* If true, put '-s erlang halt' at the end * of the arguments. */ int isdistributed = 0; int no_epmd = 0; int proto_dist = 0; int start_epmd = 1; /* Whether epmd should be started */ int i; char* s; char *epmd_prog = NULL; int process_args = 1; int print_args_exit = 0; int print_qouted_cmd_exit = 0; char* emu_name; #ifdef __WIN32__ this_module_handle = module; run_werl = windowed; /* if we started this erl just to get a detached emulator, * the arguments are already prepared for beam, so we skip * directly to start_emulator */ s = get_env("ERL_CONSOLE_MODE"); if (s != NULL && strcmp(s, "detached")==0) { free_env_val(s); s = get_env("ERL_EMULATOR_DLL"); if (s != NULL) { argv[0] = strsave(s); } else { argv[0] = strsave(EMULATOR_EXECUTABLE); } ensure_EargsSz(argc + 1); memcpy((void *) Eargsp, (void *) argv, argc * sizeof(char *)); Eargsp[argc] = NULL; emu = argv[0]; start_emulator_program = strsave(argv[0]); goto skip_arg_massage; } free_env_val(s); #else int reset_cerl_detached = 0; s = get_env("CERL_DETACHED_PROG"); if (s && strcmp(s, "") != 0) { emu = s; start_detached = 1; reset_cerl_detached = 1; ensure_EargsSz(argc + 1); memcpy((void *) Eargsp, (void *) argv, argc * sizeof(char *)); Eargsp[argc] = emu; Eargsp[argc] = NULL; goto skip_arg_massage; } free_env_val(s); #endif initial_argv_massage(&argc, &argv); /* Merge with env; expand -args_file */ i = 1; #ifdef __WIN32__ /* Not used? /rickard */ if ((argc > 2) && (strcmp(argv[i], "-regkey") == 0)) { key_val_name = strsave(argv[i+1]); i = 3; } #endif get_parameters(argc, argv); /* * Construct the path of the executable. */ #if defined(__WIN32__) && defined(WIN32_ALWAYS_DEBUG) emu_type = "debug"; #endif /* We need to do this before the ordinary processing. */ while (i < argc) { if (argv[i][0] == '-') { if (strcmp(argv[i], "-smp") == 0) { if (i + 1 >= argc) goto smp; if (strcmp(argv[i+1], "auto") == 0) { i++; } else if (strcmp(argv[i+1], "enable") == 0) { i++; smp_enable: ; } else if (strcmp(argv[i+1], "disable") == 0) { i++; smp_disable: usage_notsup("-smp disable", " Use \"+S 1\" instead."); } else { smp: ; } } else if (strcmp(argv[i], "-smpenable") == 0) { goto smp_enable; } else if (strcmp(argv[i], "-smpauto") == 0) { ; } else if (strcmp(argv[i], "-smpdisable") == 0) { goto smp_disable; } else if (strcmp(argv[i], "-extra") == 0) { break; } else if (strcmp(argv[i], "-emu_type") == 0) { NEXT_ARG_CHECK(); emu_type = argv[i+1]; i++; } else if (strcmp(argv[i], "-emu_flavor") == 0) { NEXT_ARG_CHECK(); emu_flavor = argv[i+1]; i++; } } i++; } emu = add_extra_suffixes(emu); emu_name = strsave(emu); erts_snprintf(tmpStr, sizeof(tmpStr), "%s" DIRSEP "%s" BINARY_EXT, bindir, emu); emu = strsave(tmpStr); s = get_env("ESCRIPT_NAME"); if(s) { add_Eargs(s); /* argv[0] = scriptname*/ } else { add_Eargs(emu); /* argv[0] = erl or cerl */ } /* Add the bindir to the front of the PATH, and remove all subsequent * occurrences to avoid ballooning it on repeated up/downgrades. */ s = get_env("PATH"); if (s == NULL) { erts_snprintf(tmpStr, sizeof(tmpStr), "%s" PATHSEP "%s" DIRSEP "bin" PATHSEP, bindir, rootdir); } else if (strstr(s, rootdir) == NULL) { erts_snprintf(tmpStr, sizeof(tmpStr), "%s" PATHSEP "%s" DIRSEP "bin" PATHSEP "%s", bindir, rootdir, s); } else { const char *bindir_slug, *bindir_slug_index; int bindir_slug_length; const char *in_index; char *out_index; erts_snprintf(tmpStr, sizeof(tmpStr), "%s" PATHSEP, bindir); bindir_slug = strsave(tmpStr); bindir_slug_length = strlen(bindir_slug); out_index = &tmpStr[bindir_slug_length]; in_index = s; while ((bindir_slug_index = strstr(in_index, bindir_slug))) { int block_length = (bindir_slug_index - in_index); memcpy(out_index, in_index, block_length); in_index = bindir_slug_index + bindir_slug_length; out_index += block_length; } efree((void*)bindir_slug); strcpy(out_index, in_index); } free_env_val(s); set_env("PATH", tmpStr); i = 1; get_home(); /* Add the home parameter when available. This is optional to support systems that don't have the notion of a home directory and setups that don't have the HOME environment variable set (ERL-476). */ if (home != NULL) { add_args("-home", home, NULL); } add_epmd_port(); add_arg("--"); while (i < argc) { if (!process_args) { /* Copy arguments after '-extra' */ add_arg(argv[i]); i++; } else { switch (argv[i][0]) { case '-': switch (argv[i][1]) { #ifdef __WIN32__ case 'b': if (strcmp(argv[i], "-boot") == 0) { if (boot_script) error("Conflicting -boot options"); if (got_start_erl) error("Conflicting -start_erl and -boot options"); NEXT_ARG_CHECK(); boot_script = strsave(argv[i+1]); i++; } else { add_arg(argv[i]); } break; #endif case 'c': if (strcmp(argv[i], "-compile") == 0) { /* * Note that the shell script erl.exec does a recursive call * on itself here. We'll avoid doing that. */ add_args("-noshell", "-noinput", "-s", "c", "lc_batch", NULL); add_Eargs("-B"); haltAfterwards = 0; } #ifdef __WIN32__ else if (strcmp(argv[i], "-config") == 0){ if (got_start_erl) error("Conflicting -start_erl and -config options"); NEXT_ARG_CHECK(); do { config_script_cnt++; config_scripts = erealloc(config_scripts, config_script_cnt * sizeof(char*)); config_scripts[config_script_cnt-1] = strsave(argv[i+1]); i++; } while ((i+1) < argc && argv[i+1][0] != '-' && argv[i+1][0] != '+'); } #endif else if (strcmp(argv[i], "-configfd") == 0) { NEXT_ARG_CHECK(); if ( strcmp(argv[i+1], "0") != 0 ) { add_arg(argv[i]); } else { add_args("-noshell", "-noinput", NULL); add_arg(argv[i]); } } else { add_arg(argv[i]); } break; case 'd': if (strcmp(argv[i], "-detached") != 0) { add_arg(argv[i]); } else { start_detached = 1; add_args("-noshell", "-noinput", NULL); } break; case 'e': if (strcmp(argv[i], "-extra") == 0) { process_args = 0; ADD_BOOT_CONFIG; add_arg(argv[i]); } else if (strcmp(argv[i], "-emu_args") == 0) { /* -emu_args */ verbose = 1; } else if (strcmp(argv[i], "-emu_args_exit") == 0) { print_args_exit = 1; } else if (strcmp(argv[i], "-emu_name_exit") == 0) { printf("%s\n", emu_name); exit(0); } else if (strcmp(argv[i], "-emu_qouted_cmd_exit") == 0) { print_qouted_cmd_exit = 1; } else if (strcmp(argv[i], "-env") == 0) { /* -env VARNAME VARVALUE */ NEXT_ARG_CHECK(); i += 1; NEXT_ARG_CHECK_NAMED("-env"); set_env(argv[i], argv[i+1]); i += 1; } else if (strcmp(argv[i], "-epmd") == 0) { NEXT_ARG_CHECK(); epmd_prog = argv[i+1]; ++i; } else { add_arg(argv[i]); } break; case 'k': if (strcmp(argv[i], "-keep_window") == 0) { keep_window = 1; } else add_arg(argv[i]); break; case 'm': /* * Note that the shell script erl.exec does a recursive call * on itself here. We'll avoid doing that. */ if (strcmp(argv[i], "-make") == 0) { add_args("-noshell", "-noinput", "-s", "make", "all_or_nothing", NULL); add_Eargs("-B"); haltAfterwards = 1; i = argc; /* Skip rest of command line */ } else if (strcmp(argv[i], "-man") == 0) { #if defined(__WIN32__) error("-man not supported on Windows"); #else argv[i] = "man"; erts_snprintf(tmpStr, sizeof(tmpStr), "%s/man", rootdir); set_env("MANPATH", tmpStr); execvp("man", argv+i); error("Could not execute the 'man' command."); #endif } else add_arg(argv[i]); break; case 'n': if (strcmp(argv[i], "-name") == 0) { /* -name NAME */ NEXT_ARG_CHECK(); /* * Note: Cannot use add_args() here, due to non-defined * evaluation order. */ add_arg(argv[i]); add_arg(argv[i+1]); isdistributed = 1; i++; } else if (strcmp(argv[i], "-noinput") == 0) { add_args("-noshell", "-noinput", NULL); } else if (strcmp(argv[i], "-nohup") == 0) { add_arg("-nohup"); nohup = 1; } else if (strcmp(argv[i], "-no_epmd") == 0) { add_arg("-no_epmd"); no_epmd = 1; start_epmd = 0; } else { add_arg(argv[i]); } break; case 'p': if (strcmp(argv[i], "-proto_dist") == 0) { NEXT_ARG_CHECK(); add_arg(argv[i]); add_arg(argv[i+1]); proto_dist = 1; i++; } else { add_arg(argv[i]); } break; case 's': /* -sname NAME */ if (strcmp(argv[i], "-sname") == 0) { NEXT_ARG_CHECK(); add_arg(argv[i]); add_arg(argv[i+1]); isdistributed = 1; i++; } #ifdef __WIN32__ else if (strcmp(argv[i], "-service_event") == 0) { NEXT_ARG_CHECK(); add_arg(argv[i]); add_arg(argv[i+1]); i++; } else if (strcmp(argv[i], "-start_erl") == 0) { if (i+1 < argc && argv[i+1][0] != '-') { get_start_erl_data(argv[i+1]); i++; } else get_start_erl_data((char *) NULL); } #endif else if (strcmp(argv[i], "-start_epmd") == 0) { NEXT_ARG_CHECK(); if (strcmp(argv[i+1], "true") == 0) { /* The default */ start_epmd = 1; } else if (strcmp(argv[i+1], "false") == 0) { start_epmd = 0; } else usage_format("Expected boolean argument for \'-start_epmd\'.\n"); add_arg(argv[i]); add_arg(argv[i+1]); i++; } else add_arg(argv[i]); break; case 'v': /* -version */ if (strcmp(argv[i], "-version") == 0) { add_Eargs("-V"); } else { add_arg(argv[i]); } break; default: add_arg(argv[i]); break; } /* switch(argv[i][1] */ break; case '+': switch (argv[i][1]) { case 'a': case 'A': case 'C': case 'e': case 'i': case 'n': case 'P': case 'Q': case 't': case 'T': case 'R': case 'W': case 'K': if (argv[i][2] != '\0') goto the_default; NEXT_ARG_CHECK(); argv[i][0] = '-'; add_Eargs(argv[i]); add_Eargs(argv[i+1]); i++; break; case 'I': if (argv[i][2] == 'O' && (argv[i][3] == 't' || argv[i][3] == 'p')) { if (argv[i][4] != '\0') goto the_default; NEXT_ARG_CHECK(); argv[i][0] = '-'; add_Eargs(argv[i]); add_Eargs(argv[i+1]); i++; break; } if (argv[i][2] == 'O' && argv[i][3] == 'P' && (argv[i][4] == 't' || argv[i][4] == 'p')) { if (argv[i][5] != '\0') goto the_default; NEXT_ARG_CHECK(); argv[i][0] = '-'; add_Eargs(argv[i]); add_Eargs(argv[i+1]); i++; break; } usage(argv[i]); break; case 'J': NEXT_ARG_CHECK(); argv[i][0] = '-'; add_Eargs(argv[i]); add_Eargs(argv[i+1]); i++; break; case 'S': if (argv[i][2] == 'P') { if (argv[i][3] != '\0') goto the_default; } else if (argv[i][2] == 'D') { char* type = argv[i]+3; if (strncmp(type, "cpu", 3) != 0 && strncmp(type, "Pcpu", 4) != 0 && strncmp(type, "io", 2) != 0) usage(argv[i]); if ((argv[i][3] == 'c' && argv[i][6] != '\0') || (argv[i][3] == 'P' && argv[i][7] != '\0') || (argv[i][3] == 'i' && argv[i][5] != '\0')) goto the_default; } else if (argv[i][2] != '\0') goto the_default; NEXT_ARG_CHECK(); argv[i][0] = '-'; add_Eargs(argv[i]); add_Eargs(argv[i+1]); i++; break; case 'B': argv[i][0] = '-'; if (argv[i][2] != '\0') { if ((argv[i][2] != 'i') && (argv[i][2] != 'c') && (argv[i][2] != 'd')) { usage(argv[i]); } else { add_Eargs(argv[i]); break; } } if (i+1 < argc) { if (argv[i+1][1] == '\0') { if ((argv[i+1][0] == 'i') || (argv[i+1][0] == 'c') || (argv[i+1][0] == 'd') ) { add_Eargs(argv[i]); add_Eargs(argv[i+1]); i++; break; } } } add_Eargs(argv[i]); break; case 'c': argv[i][0] = '-'; if (argv[i][2] == '\0' && i+1 < argc) { if (strcmp(argv[i+1], "true") == 0 || strcmp(argv[i+1], "false") == 0) { add_Eargs(argv[i]); add_Eargs(argv[i+1]); i++; break; } } add_Eargs(argv[i]); break; case 'M': { int x; for (x = 0; plusM_au_allocs[x]; x++) if (plusM_au_allocs[x] == argv[i][2]) break; if ((plusM_au_allocs[x] && is_one_of_strings(&argv[i][3], plusM_au_alloc_switches)) || is_one_of_strings(&argv[i][2], plusM_other_switches)) { NEXT_ARG_CHECK(); argv[i][0] = '-'; add_Eargs(argv[i]); add_Eargs(argv[i+1]); i++; } else goto the_default; break; } case 'h': if (!is_one_of_strings(&argv[i][2], plush_val_switches)) { goto the_default; } else { NEXT_ARG_CHECK(); argv[i][0] = '-'; add_Eargs(argv[i]); add_Eargs(argv[i+1]); i++; } break; case 'r': if (!is_one_of_strings(&argv[i][2], plusr_val_switches)) goto the_default; else { NEXT_ARG_CHECK(); argv[i][0] = '-'; add_Eargs(argv[i]); add_Eargs(argv[i+1]); i++; } break; case 's': if (!is_one_of_strings(&argv[i][2], pluss_val_switches)) goto the_default; else { NEXT_ARG_CHECK(); argv[i][0] = '-'; add_Eargs(argv[i]); add_Eargs(argv[i+1]); i++; } break; case 'p': if (argv[i][2] != 'c' || argv[i][3] != '\0') goto the_default; NEXT_ARG_CHECK(); argv[i][0] = '-'; add_Eargs(argv[i]); add_Eargs(argv[i+1]); i++; break; case 'z': if (!is_one_of_strings(&argv[i][2], plusz_val_switches)) { goto the_default; } else { NEXT_ARG_CHECK(); argv[i][0] = '-'; add_Eargs(argv[i]); add_Eargs(argv[i+1]); i++; } break; default: the_default: argv[i][0] = '-'; /* Change +option to -option. */ add_Eargs(argv[i]); } break; default: add_arg(argv[i]); } /* switch(argv[i][0] */ i++; } } efree(emu_name); if (process_args) { ADD_BOOT_CONFIG; } #undef ADD_BOOT_CONFIG /* The default distribution protocol (inet_tcp) relies on epmd, so the -no_epmd option can only work when using an alternative protocol for Erlang distribution. */ if (no_epmd && !proto_dist) { error("Missing -proto_dist option, expected when using -no_epmd."); } /* Doesn't conflict with -extra, since -make skips all the rest of the arguments. */ if (haltAfterwards) { add_args("-s", "erlang", "halt", NULL); } if (isdistributed && start_epmd) start_epmd_daemon(epmd_prog); #if (! defined(__WIN32__)) && defined(DEBUG) if (start_detached && get_env("ERL_CONSOLE_MODE")) { /* Start the emulator within an xterm. * Move up all arguments and insert * "xterm -e " first. * The path must be searched for this * to work, i.e execvp() must be used. */ ensure_EargsSz(EargsCnt+2); for (i = EargsCnt; i > 0; i--) Eargsp[i+1] = Eargsp[i-1]; /* Two args to insert */ EargsCnt += 2; /* Two args to insert */ Eargsp[0] = emu = "xterm"; Eargsp[1] = "-e"; } #endif add_Eargs("--"); add_Eargs("-root"); add_Eargs(rootdir); add_Eargs("-progname"); add_Eargs(progname); add_Eargs("--"); ensure_EargsSz(EargsCnt + argsCnt + 1); for (i = 0; i < argsCnt; i++) Eargsp[EargsCnt++] = argsp[i]; Eargsp[EargsCnt] = NULL; if (print_qouted_cmd_exit) { printf("\"%s\" ", emu); for (i = 1; i < EargsCnt; i++) printf("\"%s\" ", Eargsp[i]); printf("\n"); exit(0); } if (print_args_exit) { for (i = 1; i < EargsCnt; i++) printf("%s\n", Eargsp[i]); exit(0); } if (verbose) { printf("Executing: %s", emu); for (i = 0; i < EargsCnt; i++) printf(" %s", Eargsp[i]); printf("\n\n"); } #ifdef __WIN32__ if (EargsSz != EargsCnt + 1) Eargsp = (char **) erealloc((void *) Eargsp, (EargsCnt + 1) * sizeof(char *)); efree((void *) argsp); skip_arg_massage: /*DebugBreak();*/ if (run_werl) { if (start_detached) { char *p; /* transform werl to erl */ p = start_emulator_program+strlen(start_emulator_program); while (--p >= start_emulator_program && *p != '/' && *p != '\\' && *p != 'W' && *p != 'w') ; if (p >= start_emulator_program && (*p == 'W' || *p == 'w') && (p[1] == 'E' || p[1] == 'e') && (p[2] == 'R' || p[2] == 'r') && (p[3] == 'L' || p[3] == 'l')) { memmove(p,p+1,strlen(p)); } } return start_win_emulator(emu, start_emulator_program, Eargsp, start_detached); } else { return start_emulator(emu, start_emulator_program, Eargsp, start_detached); } #else skip_arg_massage: if (start_detached) { int status = fork(); if (status != 0) /* Parent */ return 0; if (reset_cerl_detached) putenv("CERL_DETACHED_PROG="); /* Detach from controlling terminal */ #ifdef HAVE_SETSID setsid(); #elif defined(TIOCNOTTY) { int fd = open("/dev/tty", O_RDWR); if (fd >= 0) { ioctl(fd, TIOCNOTTY, NULL); close(fd); } } #endif status = fork(); if (status != 0) /* Parent */ return 0; /* * Grandchild. */ close(0); open("/dev/null", O_RDONLY); close(1); open("/dev/null", O_WRONLY); close(2); open("/dev/null", O_WRONLY); #ifdef DEBUG execvp(emu, Eargsp); /* "xterm ..." needs to search the path */ #endif } #ifdef DEBUG else #endif { execv(emu, Eargsp); } if (errno == ENOENT) { if (strcmp(emu_flavor,DEFAULT_SUFFIX) || emu_type) { /* The executable did not exist and a flavor/type flags was given. * We collect the possible combinations and print that in the error * in order to help the user. */ char buff[255], *currbuff = buff; DIR *dp = opendir(bindir); if (dp) { struct dirent *ep; while ((ep = readdir(dp)) != NULL) { if (strncmp("beam",ep->d_name,4) == 0) { char *type = strstr(ep->d_name,".") + 1; char *flavor = strstr(type,"."); currbuff += sprintf(currbuff,"\n "); if (flavor == NULL) { flavor = type; } else { char* emu_type = strndup(type,flavor - type); currbuff += sprintf(currbuff,"-emu_type %s ", emu_type); free(emu_type); flavor++; } currbuff += sprintf(currbuff,"-emu_flavor %s", flavor); } } closedir(dp); } error("Invalid emulator type or flavor. Available combinations are: %s\n",buff); } else { error("The emulator \'%s\' does not exist."); } } else { error("Error %d executing \'%s\'.", errno, emu); } return 1; #endif } static void usage_aux(void) { fprintf(stderr, "Usage: erl [-version] [-sname NAME | -name NAME] " "[-noshell] [-noinput] [-env VAR VALUE] [-compile file ...] " #ifdef __WIN32__ "[-start_erl [datafile]] " #endif "[-make] [-man [manopts] MANPAGE] [-x] [-emu_args] [-start_epmd BOOLEAN] " "[-emu_type TYPE] [-emu_flavor FLAVOR] " "[-args_file FILENAME] [+A THREADS] [+a SIZE] [+B[c|d|i]] [+c [BOOLEAN]] " "[+C MODE] [+dcg DECENTRALIZED_COUNTER_GROUPS_LIMIT] [+h HEAP_SIZE_OPTION] " "[+J[Pperf] JIT_OPTION] " "[+M ] [+P MAX_PROCS] [+Q MAX_PORTS] " "[+R COMPAT_REL] " "[+r] [+rg READER_GROUPS_LIMIT] [+s SCHEDULER_OPTION] " "[+S NO_SCHEDULERS:NO_SCHEDULERS_ONLINE] " "[+SP PERCENTAGE_SCHEDULERS:PERCENTAGE_SCHEDULERS_ONLINE] " "[+T LEVEL] [+V] [+v] " "[+W] [+z MISC_OPTION] [args ...]\n"); exit(1); } void usage(const char *switchname) { fprintf(stderr, "Missing argument(s) for \'%s\'.\n", switchname); usage_aux(); } static void usage_notsup(const char *switchname, const char *alt) { fprintf(stderr, "Argument \'%s\' not supported.%s\n", switchname, alt); usage_aux(); } static void usage_format(char *format, ...) { va_list args; va_start(args, format); vfprintf(stderr, format, args); va_end(args); usage_aux(); } void start_epmd_daemon(char *epmd) { char epmd_cmd[MAXPATHLEN+100]; #ifdef __WIN32__ char* arg1 = NULL; #endif int result; if (!epmd) { epmd = epmd_cmd; #ifdef __WIN32__ erts_snprintf(epmd_cmd, sizeof(epmd_cmd), "\"%s" DIRSEP "epmd\"", bindir); arg1 = "-daemon"; #else erts_snprintf(epmd_cmd, sizeof(epmd_cmd), "\"%s" DIRSEP "epmd\" -daemon", bindir); #endif } #ifdef __WIN32__ if (arg1 != NULL) { strcat(epmd, " "); strcat(epmd, arg1); } { wchar_t wcepmd[MAXPATHLEN+100]; STARTUPINFOW start; PROCESS_INFORMATION pi; memset(&start, 0, sizeof (start)); start.cb = sizeof (start); MultiByteToWideChar(CP_UTF8, 0, epmd, -1, wcepmd, MAXPATHLEN+100); if (!CreateProcessW(NULL, wcepmd, NULL, NULL, FALSE, CREATE_DEFAULT_ERROR_MODE | DETACHED_PROCESS, NULL, NULL, &start, &pi)) result = -1; else result = 0; } #else result = system(epmd); #endif if (result == -1) { fprintf(stderr, "Error spawning %s (error %d)\n", epmd_cmd,errno); exit(1); } } static void add_arg(char *new_arg) { if (argsCnt >= argsSz) argsp = (char **) erealloc((void *) argsp, sizeof(char *) * (argsSz += 20)); argsp[argsCnt++] = QUOTE(new_arg); } static void add_args(char *first_arg, ...) { va_list ap; char* arg; add_arg(first_arg); va_start(ap, first_arg); while ((arg = va_arg(ap, char *)) != NULL) { add_arg(arg); } va_end(ap); } static void ensure_EargsSz(int sz) { if (EargsSz < sz) Eargsp = (char **) erealloc((void *) Eargsp, sizeof(char *) * (EargsSz = sz)); ASSERT(Eargsp); } static void add_Eargs(char *new_arg) { if (EargsCnt >= EargsSz) Eargsp = (char **) erealloc((void *) Eargsp, sizeof(char *) * (EargsSz += 20)); Eargsp[EargsCnt++] = QUOTE(new_arg); } #if !defined(__WIN32__) void error(char* format, ...) { char sbuf[1024]; va_list ap; va_start(ap, format); erts_vsnprintf(sbuf, sizeof(sbuf), format, ap); va_end(ap); fprintf(stderr, "erlexec: %s\n", sbuf); exit(1); } #endif static void * emalloc(size_t size) { void *p = malloc(size); if (p == NULL) error("Insufficient memory"); return p; } static void * erealloc(void *p, size_t size) { void *res = realloc(p, size); if (res == NULL) error("Insufficient memory"); return res; } static void efree(void *p) { free(p); } static int is_one_of_strings(char *str, char *strs[]) { int i, j; for (i = 0; strs[i]; i++) { for (j = 0; str[j] && strs[i][j] && str[j] == strs[i][j]; j++); if (!str[j] && !strs[i][j]) return 1; } return 0; } static char *write_str(char *to, const char *from) { while (*from) *(to++) = *(from++); *to = '\0'; return to; } char* strsave(char* string) { char* p = emalloc(strlen(string)+1); strcpy(p, string); return p; } #if defined(__WIN32__) static void get_start_erl_data(char *file) { static char* a_config_script; int fp; char tmpbuffer[512]; char start_erl_data[512]; int bytesread; char* env; char* reldir; char* otpstring; char* tprogname; if (boot_script) error("Conflicting -start_erl and -boot options"); if (config_scripts) error("Conflicting -start_erl and -config options"); env = get_env("RELDIR"); if (env) reldir = strsave(env); else { erts_snprintf(tmpbuffer, sizeof(tmpbuffer), "%s/releases", rootdir); reldir = strsave(tmpbuffer); } free_env_val(env); if (file == NULL) erts_snprintf(start_erl_data, sizeof(start_erl_data), "%s/start_erl.data", reldir); else erts_snprintf(start_erl_data, sizeof(start_erl_data), "%s", file); fp = _open(start_erl_data, _O_RDONLY ); if( fp == -1 ) error( "open failed on %s",start_erl_data ); else { if( ( bytesread = _read( fp, tmpbuffer, 512 ) ) <= 0 ) error( "Problem reading file %s", start_erl_data ); else { tmpbuffer[bytesread]='\0'; if ((otpstring = strchr(tmpbuffer,' ')) != NULL) { *otpstring = '\0'; otpstring++; /* * otpstring is the otpversion * tmpbuffer is the emuversion */ } } } tprogname = otpstring; while (*tprogname) { if (*tprogname <= ' ') { *tprogname='\0'; break; } tprogname++; } bindir = emalloc(512); erts_snprintf(bindir,512,"%s/erts-%s/bin",rootdir,tmpbuffer); /* BINDIR=$ROOTDIR/erts-$ERTS_VSN/bin */ tprogname = progname; progname = emalloc(strlen(tprogname) + 20); erts_snprintf(progname,strlen(tprogname) + 20,"%s -start_erl",tprogname); boot_script = emalloc(512); a_config_script = emalloc(512); erts_snprintf(boot_script, 512, "%s/%s/start", reldir, otpstring); erts_snprintf(a_config_script, 512, "%s/%s/sys", reldir, otpstring); config_scripts = &a_config_script; config_script_cnt = 1; got_start_erl = 1; } static wchar_t *replace_filename(wchar_t *path, wchar_t *new_base) { int plen = wcslen(path); wchar_t *res = (wchar_t *) emalloc((plen+wcslen(new_base)+1)*sizeof(wchar_t)); wchar_t *p; wcscpy(res,path); for (p = res+plen-1 ;p >= res && *p != L'\\'; --p) ; *(p+1) =L'\0'; wcscat(res,new_base); return res; } static char *path_massage(wchar_t *long_path) { char *p; int len; len = WideCharToMultiByte(CP_UTF8, 0, long_path, -1, NULL, 0, NULL, NULL); p = emalloc(len*sizeof(char)); WideCharToMultiByte(CP_UTF8, 0, long_path, -1, p, len, NULL, NULL); return p; } static char *do_lookup_in_section(InitSection *inis, char *name, char *section, wchar_t *filename, int is_path) { char *p = lookup_init_entry(inis, name); if (p == NULL) { error("Could not find key %s in section %s of file %S", name,section,filename); } return strsave(p); } // Setup bindir, rootdir and progname as utf8 buffers static void get_parameters(int argc, char** argv) { wchar_t *p; wchar_t buffer[MAX_PATH]; wchar_t *ini_filename; HANDLE module = GetModuleHandle(NULL); /* This might look strange, but we want the erl.ini that resides in the same dir as erl.exe, not an erl.ini in our directory */ InitFile *inif; InitSection *inis; if (module == NULL) { error("Cannot GetModuleHandle()"); } if (GetModuleFileNameW(module,buffer,MAX_PATH) == 0) { error("Could not GetModuleFileName"); } ini_filename = replace_filename(buffer,INI_FILENAME); if ((inif = load_init_file(ini_filename)) == NULL) { wchar_t wbindir[MAX_PATH]; wchar_t wrootdir[MAX_PATH]; /* Assume that the path is absolute and that it does not contain any symbolic link */ /* Determine bindir */ if (GetEnvironmentVariableW(L"ERLEXEC_DIR", buffer, MAX_PATH) == 0) { wcscpy(buffer, ini_filename); for (p = buffer+wcslen(buffer)-1; p >= buffer && *p != L'\\'; --p) ; *p = L'\0'; } bindir = path_massage(buffer); /* Determine rootdir */ for (p = buffer+wcslen(buffer)-1; p >= buffer && *p != L'\\'; --p) ; p--; for (;p >= buffer && *p != L'\\'; --p) ; *p =L'\0'; rootdir = path_massage(buffer); /* Hardcoded progname */ progname = strsave(DEFAULT_PROGNAME); } else { if ((inis = lookup_init_section(inif,INI_SECTION)) == NULL) { error("Could not find section %s in init file %s", INI_SECTION, ini_filename); } bindir = do_lookup_in_section(inis, "Bindir", INI_SECTION, ini_filename,1); rootdir = do_lookup_in_section(inis, "Rootdir", INI_SECTION, ini_filename,1); progname = do_lookup_in_section(inis, "Progname", INI_SECTION, ini_filename,0); free_init_file(inif); } emu = EMULATOR_EXECUTABLE; start_emulator_program = strsave(argv[0]); free(ini_filename); } static void get_home(void) { wchar_t *profile; char* homedrive; char* homepath; homedrive = get_env("HOMEDRIVE"); homepath = get_env("HOMEPATH"); if (!homedrive || !homepath) { if (SHGetKnownFolderPath(&FOLDERID_Profile, 0, NULL, &profile) == S_OK) { home = utf16_to_utf8(profile); /* CoTaskMemFree(profile); */ } else error("HOMEDRIVE or HOMEPATH not set and getting USERPROFILE failed"); } else { home = emalloc(strlen(homedrive)+strlen(homepath)+1); strcpy(home, homedrive); strcat(home, homepath); } free_env_val(homedrive); free_env_val(homepath); } #else static void get_parameters(int argc, char** argv) { progname = get_env("PROGNAME"); if (!progname) { progname = strsave(DEFAULT_PROGNAME); } emu = get_env("EMU"); if (!emu) { emu = strsave(EMULATOR_EXECUTABLE); } bindir = get_env("BINDIR"); if (!bindir) { /* Determine bindir from absolute path to executable */ char *p; char buffer[PATH_MAX]; strncpy(buffer, argv[0], sizeof(buffer)); buffer[sizeof(buffer)-1] = '\0'; for (p = buffer+strlen(buffer)-1 ; p >= buffer && *p != '/'; --p) ; *p ='\0'; bindir = strsave(buffer); } rootdir = get_env("ROOTDIR"); if (!rootdir) { /* Determine rootdir from absolute path to bindir */ char *p; char buffer[PATH_MAX]; strncpy(buffer, bindir, sizeof(buffer)); buffer[sizeof(buffer)-1] = '\0'; for (p = buffer+strlen(buffer)-1; p >= buffer && *p != '/'; --p) ; p--; for (; p >= buffer && *p != '/'; --p) ; *p ='\0'; rootdir = strsave(buffer); } if (!progname || !emu || !rootdir || !bindir) { error("PROGNAME, EMU, ROOTDIR and BINDIR must be set"); } } static void get_home(void) { home = get_env("HOME"); } #endif static void add_epmd_port(void) { char* port = get_env("ERL_EPMD_PORT"); if (port != NULL) { add_args("-epmd_port", port, NULL); } } static char **build_args_from_env(char *env_var) { char *value = get_env(env_var); char **res = build_args_from_string(value, 0); free_env_val(value); return res; } static char **build_args_from_string(char *string, int allow_comments) { int argc = 0; char **argv = NULL; int alloced = 0; char **cur_s = NULL; /* Initialized to avoid warning. */ int s_alloced = 0; int s_pos = 0; char *p = string; enum {Start, Build, Build0, BuildSQuoted, BuildDQuoted, AcceptNext, BuildComment} state; #define ENSURE() \ if (s_pos >= s_alloced) { \ if (!*cur_s) { \ *cur_s = emalloc(s_alloced = 20); \ } else { \ *cur_s = erealloc(*cur_s, s_alloced += 20); \ } \ } if (!p) return NULL; argv = emalloc(sizeof(char *) * (alloced = 10)); state = Start; for(;;) { switch (state) { case Start: if (!*p) goto done; if (argc >= alloced - 2) { /* Make room for extra NULL and "--" */ argv = erealloc(argv, (alloced += 10) * sizeof(char *)); } cur_s = argc + argv; *cur_s = NULL; s_pos = 0; s_alloced = 0; state = Build0; break; case Build0: switch (*p) { case '\n': case '\f': case '\r': case '\t': case '\v': case ' ': ++p; break; case '\0': state = Start; break; case '#': if (allow_comments) { ++p; state = BuildComment; break; } /* fall-through */ default: state = Build; break; } break; case Build: switch (*p) { case '#': if (!allow_comments) goto build_default; /* fall-through */ case '\n': case '\f': case '\r': case '\t': case '\v': case ' ': case '\0': ENSURE(); (*cur_s)[s_pos] = '\0'; ++argc; state = Start; break; case '"': ++p; state = BuildDQuoted; break; case '\'': ++p; state = BuildSQuoted; break; case '\\': ++p; state = AcceptNext; break; default: build_default: ENSURE(); (*cur_s)[s_pos++] = *p++; break; } break; case BuildDQuoted: switch (*p) { case '"': ++p; /* fall through */ case '\0': state = Build; break; default: ENSURE(); (*cur_s)[s_pos++] = *p++; break; } break; case BuildSQuoted: switch (*p) { case '\'': ++p; /* fall through */ case '\0': state = Build; break; default: ENSURE(); (*cur_s)[s_pos++] = *p++; break; } break; case AcceptNext: if (*p) { ENSURE(); (*cur_s)[s_pos++] = *p++; } state = Build; break; case BuildComment: if (*p == '\n' || *p == '\0') { state = Build0; } else { p++; } break; } } done: if (!argc) { efree(argv); return NULL; } argv[argc++] = "--"; /* Add a -- separator in order for flags from different environments to not effect each other */ argv[argc++] = NULL; /* Sure to be large enough */ return argv; #undef ENSURE } static const char * errno_string(void) { const char *str = strerror(errno); if (!str) return "unknown error"; return str; } #define FILE_BUFF_SIZE 1024 static char ** read_args_file(char *filename) { FILE *file; char buff[FILE_BUFF_SIZE+1]; size_t astr_sz = 0, sz; char *astr = buff; char **res; do { errno = 0; file = fopen(filename, "r"); } while (!file && errno == EINTR); if (!file) { #ifdef __WIN32__ char cwd[MAX_PATH]; if (_getcwd(cwd, sizeof(cwd)) == NULL) { #else char cwd[PATH_MAX]; if (getcwd(cwd, sizeof(cwd)) == NULL) { #endif cwd[0] = '\0'; } usage_format("Failed to open arguments file \"%s\" at \"%s\": %s\n", filename, cwd, errno_string()); } sz = fread(astr, 1, FILE_BUFF_SIZE, file); while (!feof(file) && sz == FILE_BUFF_SIZE) { if (astr == buff) { astr = emalloc(FILE_BUFF_SIZE*2+1); astr_sz = FILE_BUFF_SIZE; memcpy(astr, buff, sizeof(buff)); } else { astr_sz += FILE_BUFF_SIZE; astr = erealloc(astr,astr_sz+FILE_BUFF_SIZE+1); } sz = fread(astr+astr_sz, 1, FILE_BUFF_SIZE, file); } if (ferror(file)) { usage_format("Failed to read arguments file \"%s\": %s\n", filename, errno_string()); } astr[astr_sz + sz] = '\0'; fclose(file); if (astr[0] == '\0') res = NULL; else res = build_args_from_string(astr, !0); if (astr != buff) efree(astr); return res; #undef FILE_BUFF_SIZE } typedef struct { char **argv; int argc; int size; } argv_buf; static void trim_argv_buf(argv_buf *abp) { abp->argv = erealloc(abp->argv, sizeof(char *)*(abp->size = abp->argc)); } static void save_arg(argv_buf *abp, char *arg) { if (abp->size <= abp->argc) { if (!abp->argv) abp->argv = emalloc(sizeof(char *)*(abp->size = 100)); else abp->argv = erealloc(abp->argv, sizeof(char *)*(abp->size += 100)); } abp->argv[abp->argc++] = arg; } #define DEF_ARGV_STACK_SIZE 10 #define ARGV_STACK_SIZE_INCR 50 typedef struct { char **argv; int ix; } argv_stack_element; typedef struct { int top_ix; int size; argv_stack_element *base; argv_stack_element def_buf[DEF_ARGV_STACK_SIZE]; } argv_stack; #define ARGV_STACK_INIT(S) \ do { \ (S)->top_ix = 0; \ (S)->size = DEF_ARGV_STACK_SIZE; \ (S)->base = &(S)->def_buf[0]; \ } while (0) static void push_argv(argv_stack *stck, char **argv, int ix) { if (stck->top_ix == stck->size) { if (stck->base != &stck->def_buf[0]) { stck->size += ARGV_STACK_SIZE_INCR; stck->base = erealloc(stck->base, sizeof(argv_stack_element)*stck->size); } else { argv_stack_element *base; base = emalloc(sizeof(argv_stack_element) *(stck->size + ARGV_STACK_SIZE_INCR)); memcpy((void *) base, (void *) stck->base, sizeof(argv_stack_element)*stck->size); stck->base = base; stck->size += ARGV_STACK_SIZE_INCR; } } stck->base[stck->top_ix].argv = argv; stck->base[stck->top_ix++].ix = ix; } static void pop_argv(argv_stack *stck, char ***argvp, int *ixp) { if (stck->top_ix == 0) { *argvp = NULL; *ixp = 0; } else { *argvp = stck->base[--stck->top_ix].argv; *ixp = stck->base[stck->top_ix].ix; if (stck->top_ix == 0 && stck->base != &stck->def_buf[0]) { efree(stck->base); stck->base = &stck->def_buf[0]; stck->size = DEF_ARGV_STACK_SIZE; } } } static void get_file_args(char *filename, argv_buf *abp, argv_buf *xabp) { argv_stack stck; int i; char **argv; ARGV_STACK_INIT(&stck); i = 0; argv = read_args_file(filename); while (argv) { while (argv[i]) { if (strcmp(argv[i], "-args_file") == 0) { char **new_argv; char *fname; if (!argv[++i]) usage("-args_file"); fname = argv[i++]; new_argv = read_args_file(fname); if (new_argv) { if (argv[i]) push_argv(&stck, argv, i); else efree(argv); i = 0; argv = new_argv; } } else { if (strcmp(argv[i], "-extra") == 0) { i++; while (argv[i]) save_arg(xabp, argv[i++]); break; } save_arg(abp, argv[i++]); } } efree(argv); pop_argv(&stck, &argv, &i); } } static void initial_argv_massage(int *argc, char ***argv) { argv_buf ab = {0}, xab = {0}; int ix, vix, ac; char **av; char *sep = "--"; struct { int argc; char **argv; } avv[] = {{INT_MAX, NULL}, {INT_MAX, NULL}, {INT_MAX, NULL}, {INT_MAX, NULL}, {INT_MAX, NULL}, {INT_MAX, NULL}, {INT_MAX, NULL}}; /* * The environment flag containing OTP release is intentionally * undocumented and intended for OTP internal use only. */ vix = 0; av = build_args_from_env("ERL_OTP" OTP_SYSTEM_VERSION "_FLAGS"); if (av) avv[vix++].argv = av; av = build_args_from_env("ERL_AFLAGS"); if (av) avv[vix++].argv = av; /* command line */ if (*argc > 1) { avv[vix].argc = *argc - 1; avv[vix++].argv = &(*argv)[1]; avv[vix].argc = 1; avv[vix++].argv = &sep; } av = build_args_from_env("ERL_FLAGS"); if (av) avv[vix++].argv = av; av = build_args_from_env("ERL_ZFLAGS"); if (av) avv[vix++].argv = av; if (vix == (*argc > 1 ? 2 : 0)) { /* Only command line argv; check if we can use argv as it is... */ ac = *argc; av = *argv; for (ix = 1; ix < ac; ix++) { if (strcmp(av[ix], "-args_file") == 0) { /* ... no; we need to expand arguments from file into argument list */ goto build_new_argv; } if (strcmp(av[ix], "-extra") == 0) { break; } } /* ... yes; we can use argv as it is. */ return; } build_new_argv: save_arg(&ab, (*argv)[0]); vix = 0; while (avv[vix].argv) { ac = avv[vix].argc; av = avv[vix].argv; ix = 0; while (ix < ac && av[ix]) { if (strcmp(av[ix], "-args_file") == 0) { if (++ix == ac) usage("-args_file"); get_file_args(av[ix++], &ab, &xab); } else { if (strcmp(av[ix], "-extra") == 0) { ix++; while (ix < ac && av[ix]) save_arg(&xab, av[ix++]); break; } save_arg(&ab, av[ix++]); } } vix++; } vix = 0; while (avv[vix].argv) { if (avv[vix].argc == INT_MAX) /* not command line */ efree(avv[vix].argv); vix++; } if (xab.argc) { save_arg(&ab, "-extra"); for (ix = 0; ix < xab.argc; ix++) save_arg(&ab, xab.argv[ix]); efree(xab.argv); } save_arg(&ab, NULL); trim_argv_buf(&ab); *argv = ab.argv; *argc = ab.argc - 1; } #ifdef __WIN32__ static char* possibly_quote(char* arg) { int mustQuote = NO; int n = 0; char* s; char* narg; /* * Scan the string to find out if it needs quoting and return * the original argument if not. */ for (s = arg; *s; s++, n++) { if (*s == ' ' || *s == '"') { mustQuote = YES; n++; } } if (!mustQuote) { return arg; } /* * Insert the quotes and put a backslash in front of every quote * inside the string. */ s = narg = emalloc(n+2+1); for (*s++ = '"'; *arg; arg++, s++) { if (*s == '"') { *s++ = '\\'; } *s = *arg; } *s++ = '"'; *s = '\0'; return narg; } /* * Unicode helpers to handle environment and command line parameters on * Windows. We internally handle all environment variables in UTF8, * but put and get the environment using the WCHAR (limited UTF16) interface * * These are simplified to only handle Unicode characters that can fit in * Windows simplified UTF16, i.e. characters that fit in 16 bits. */ static int utf8_len(unsigned char first) { if ((first & ((unsigned char) 0x80)) == 0) { return 1; } else if ((first & ((unsigned char) 0xE0)) == 0xC0) { return 2; } else if ((first & ((unsigned char) 0xF0)) == 0xE0) { return 3; } else if ((first & ((unsigned char) 0xF8)) == 0xF0) { return 4; } return 1; /* will be a '?' */ } static WCHAR *utf8_to_utf16(unsigned char *bytes) { unsigned int unipoint; unsigned char *tmp = bytes; WCHAR *target, *res; int num = 0; while (*tmp) { num++; tmp += utf8_len(*tmp); } res = target = emalloc((num + 1) * sizeof(WCHAR)); while (*bytes) { if (((*bytes) & ((unsigned char) 0x80)) == 0) { unipoint = (unsigned int) *bytes; ++bytes; } else if (((*bytes) & ((unsigned char) 0xE0)) == 0xC0) { unipoint = (((unsigned int) ((*bytes) & ((unsigned char) 0x1F))) << 6) | ((unsigned int) (bytes[1] & ((unsigned char) 0x3F))); bytes += 2; } else if (((*bytes) & ((unsigned char) 0xF0)) == 0xE0) { unipoint = (((unsigned int) ((*bytes) & ((unsigned char) 0xF))) << 12) | (((unsigned int) (bytes[1] & ((unsigned char) 0x3F))) << 6) | ((unsigned int) (bytes[2] & ((unsigned char) 0x3F))); if (unipoint > 0xFFFF) { unipoint = (unsigned int) '?'; } bytes +=3; } else if (((*bytes) & ((unsigned char) 0xF8)) == 0xF0) { unipoint = (unsigned int) '?'; /* Cannot put in a wchar */ bytes += 4; } else { unipoint = (unsigned int) '?'; } *target++ = (WCHAR) unipoint; } *target = L'\0'; return res; } static int put_utf8(WCHAR ch, unsigned char *target, int sz, int *pos) { unsigned int x = (unsigned int) ch; if (x < 0x80) { if (*pos >= sz) { return -1; } target[(*pos)++] = (unsigned char) x; } else if (x < 0x800) { if (((*pos) + 1) >= sz) { return -1; } target[(*pos)++] = (((unsigned char) (x >> 6)) | ((unsigned char) 0xC0)); target[(*pos)++] = (((unsigned char) (x & 0x3F)) | ((unsigned char) 0x80)); } else { if ((x >= 0xD800 && x <= 0xDFFF) || (x == 0xFFFE) || (x == 0xFFFF)) { /* Invalid unicode range */ return -1; } if (((*pos) + 2) >= sz) { return -1; } target[(*pos)++] = (((unsigned char) (x >> 12)) | ((unsigned char) 0xE0)); target[(*pos)++] = ((((unsigned char) (x >> 6)) & 0x3F) | ((unsigned char) 0x80)); target[(*pos)++] = (((unsigned char) (x & 0x3F)) | ((unsigned char) 0x80)); } return 0; } static int need_bytes_for_utf8(WCHAR x) { if (x < 0x80) return 1; else if (x < 0x800) return 2; else return 3; } static WCHAR *latin1_to_utf16(char *str) { int len = strlen(str); int i; WCHAR *wstr = emalloc((len+1) * sizeof(WCHAR)); for(i=0;i