diff options
Diffstat (limited to 'erts/etc/common/erlexec.c')
-rw-r--r-- | erts/etc/common/erlexec.c | 2038 |
1 files changed, 2038 insertions, 0 deletions
diff --git a/erts/etc/common/erlexec.c b/erts/etc/common/erlexec.c new file mode 100644 index 0000000000..4325418e7c --- /dev/null +++ b/erts/etc/common/erlexec.c @@ -0,0 +1,2038 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 1996-2009. All Rights Reserved. + * + * The contents of this file are subject to the Erlang Public License, + * Version 1.1, (the "License"); you may not use this file except in + * compliance with the License. You should have received a copy of the + * Erlang Public License along with this software. If not, it can be + * retrieved online at http://www.erlang.org/. + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * %CopyrightEnd% + */ + +/* + * This is a C version of the erl.exec Bourne shell script, including + * additions required for Windows NT. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "sys.h" +#include "erl_driver.h" +#include <stdlib.h> +#include <stdarg.h> +#include "erl_misc_utils.h" + +#ifdef __WIN32__ +# include "erl_version.h" +# include "init_file.h" +#endif + +#define NO 0 +#define YES 1 +#define DEFAULT_PROGNAME "erl" + +#ifdef __WIN32__ +#define INI_FILENAME "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 */ + 'D', /* std_alloc */ + 'E', /* ets_alloc */ + 'H', /* eheap_alloc */ + 'L', /* ll_alloc */ + 'R', /* driver_alloc */ + 'S', /* sl_alloc */ + 'T', /* temp_alloc */ + '\0' +}; + +/* +M alloc_util allocator specific arguments */ +static char *plusM_au_alloc_switches[] = { + "as", + "asbcst", + "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", + "im", + "is", + "it", + "Mamcbf", + "Mrmcbf", + "Mmcs", + "Mcci", + "Fe", + "Ye", + "Ym", + "Ytp", + "Ytt", + NULL +}; + +/* +s arguments with values */ +static char *pluss_val_switches[] = { + "bt", + "ct", + "ss", + NULL +}; + +/* + * Define sleep(seconds) in terms of Sleep() on Windows. + */ + +#ifdef __WIN32__ +#define sleep(seconds) Sleep(seconds*1000) +#endif + +#define SMP_SUFFIX ".smp" +#define HYBRID_SUFFIX ".hybrid" + +#ifdef __WIN32__ +#define DEBUG_SUFFIX ".debug" +#define EMU_TYPE_SUFFIX_LENGTH (strlen(HYBRID_SUFFIX)+(strlen(DEBUG_SUFFIX))) +#else +/* The length of the longest memory architecture suffix. */ +#define EMU_TYPE_SUFFIX_LENGTH strlen(HYBRID_SUFFIX) +#endif +/* + * Define flags for different memory architectures. + */ +#define EMU_TYPE_SMP 0x0001 +#define EMU_TYPE_HYBRID 0x0002 + +#ifdef __WIN32__ +#define EMU_TYPE_DEBUG 0x0004 +#endif + +void usage(const char *switchname); +void start_epmd(char *epmd); +void error(char* format, ...); + +/* + * Local functions. + */ + +#if !defined(ERTS_HAVE_SMP_EMU) || !defined(ERTS_HAVE_HYBRID_EMU) +static void usage_notsup(const char *switchname); +#endif +static void usage_msg(const char *msg); +static char **build_args_from_env(char *env_var); +static char **build_args_from_string(char *env_var); +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, 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 int emu_type = 0; /* If non-zero, start beam.ARCH or beam.ARCH.exe + * instead of beam or beam.exe, where ARCH is defined by flags. */ +static int emu_type_passed = 0; /* Types explicitly set */ + +#ifdef __WIN32__ +static char *start_emulator_program = NULL; /* For detachec 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_script = NULL; /* used by option -start_erl and -config */ + +static HANDLE this_module_handle; +static int run_werl; + +#endif + +/* + * Needed 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. */ + +static void +set_env(char *key, char *value) +{ +#ifdef __WIN32__ + if (!SetEnvironmentVariable((LPCTSTR) key, (LPCTSTR) value)) + error("SetEnvironmentVariable(\"%s\", \"%s\") failed!", key, value); +#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; + char *value = NULL; + while (1) { + DWORD nsz; + if (value) + efree(value); + value = emalloc(size); + SetLastError(0); + nsz = GetEnvironmentVariable((LPCTSTR) key, (LPTSTR) value, size); + if (nsz == 0 && GetLastError() == ERROR_ENVVAR_NOT_FOUND) { + efree(value); + return NULL; + } + if (nsz <= size) + return value; + size = nsz; + } +#else + return getenv(key); +#endif +} + +static void +free_env_val(char *value) +{ +#ifdef __WIN32__ + if (value) + free(value); +#endif +} + +/* + * Add the arcitecture suffix to the program name if needed, + * except on Windows, where we insert it just before ".DLL". + */ +static char* +add_extra_suffixes(char *prog, int type) +{ + char *res; + char *p; + int len; +#ifdef __WIN32__ + char *dll_p; + int dll = 0; +#endif + + if (!type) { + return prog; + } + + len = strlen(prog); + + /* Worst-case allocation */ + p = emalloc(len + + EMU_TYPE_SUFFIX_LENGTH + + + 1); + 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 + +#ifdef __WIN32__ + if (type & EMU_TYPE_DEBUG) { + p = write_str(p, DEBUG_SUFFIX); + type &= ~(EMU_TYPE_DEBUG); + } +#endif + if (type == EMU_TYPE_SMP) { + p = write_str(p, SMP_SUFFIX); + } + else if (type == EMU_TYPE_HYBRID) { + p = write_str(p, HYBRID_SUFFIX); + } +#ifdef __WIN32__ + if (dll) { + p = write_str(p, DLL_EXT); + } +#endif + + return res; +} + +#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 i; + char* s; + char *epmd_prog = NULL; + char *malloc_lib; + int process_args = 1; + int print_args_exit = 0; + int print_qouted_cmd_exit = 0; + erts_cpu_info_t *cpuinfo = NULL; + +#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. + */ + cpuinfo = erts_cpu_info_create(); + /* '-smp auto' is default */ +#ifdef ERTS_HAVE_SMP_EMU + if (erts_get_cpu_configured(cpuinfo) > 1) + emu_type |= EMU_TYPE_SMP; +#endif + +#if defined(__WIN32__) && defined(WIN32_ALWAYS_DEBUG) + emu_type_passed |= EMU_TYPE_DEBUG; + emu_type |= EMU_TYPE_DEBUG; +#endif + + /* We need to do this before the ordinary processing. */ + malloc_lib = get_env("ERL_MALLOC_LIB"); + while (i < argc) { + if (argv[i][0] == '+') { + if (argv[i][1] == 'M' && argv[i][2] == 'Y' && argv[i][3] == 'm') { + if (argv[i][4] == '\0') { + if (++i < argc) + malloc_lib = argv[i]; + else + usage("+MYm"); + } + else + malloc_lib = &argv[i][4]; + } + } + else if (argv[i][0] == '-') { + if (strcmp(argv[i], "-smp") == 0) { + if (i + 1 >= argc) + goto smp; + + if (strcmp(argv[i+1], "auto") == 0) { + i++; + smp_auto: + emu_type_passed |= EMU_TYPE_SMP; +#ifdef ERTS_HAVE_SMP_EMU + if (erts_get_cpu_configured(cpuinfo) > 1) + emu_type |= EMU_TYPE_SMP; + else +#endif + emu_type &= ~EMU_TYPE_SMP; + } + else if (strcmp(argv[i+1], "enable") == 0) { + i++; + smp_enable: + emu_type_passed |= EMU_TYPE_SMP; +#ifdef ERTS_HAVE_SMP_EMU + emu_type |= EMU_TYPE_SMP; +#else + usage_notsup("-smp enable"); +#endif + } + else if (strcmp(argv[i+1], "disable") == 0) { + i++; + smp_disable: + emu_type_passed |= EMU_TYPE_SMP; + emu_type &= ~EMU_TYPE_SMP; + } + else { + smp: + + emu_type_passed |= EMU_TYPE_SMP; +#ifdef ERTS_HAVE_SMP_EMU + emu_type |= EMU_TYPE_SMP; +#else + usage_notsup("-smp"); +#endif + } + } else if (strcmp(argv[i], "-smpenable") == 0) { + goto smp_enable; + } else if (strcmp(argv[i], "-smpauto") == 0) { + goto smp_auto; + } else if (strcmp(argv[i], "-smpdisable") == 0) { + goto smp_disable; +#ifdef __WIN32__ + } else if (strcmp(argv[i], "-debug") == 0) { + emu_type_passed |= EMU_TYPE_DEBUG; + emu_type |= EMU_TYPE_DEBUG; +#endif + } else if (strcmp(argv[i], "-hybrid") == 0) { + emu_type_passed |= EMU_TYPE_HYBRID; +#ifdef ERTS_HAVE_HYBRID_EMU + emu_type |= EMU_TYPE_HYBRID; +#else + usage_notsup("-hybrid"); +#endif + } else if (strcmp(argv[i], "-extra") == 0) { + break; + } + } + i++; + } + + erts_cpu_info_destroy(cpuinfo); + cpuinfo = NULL; + + if ((emu_type & EMU_TYPE_HYBRID) && (emu_type & EMU_TYPE_SMP)) { + /* + * We have a conflict. Only using explicitly passed arguments + * may solve it... + */ + emu_type &= emu_type_passed; + if ((emu_type & EMU_TYPE_HYBRID) && (emu_type & EMU_TYPE_SMP)) { + usage_msg("Hybrid heap emulator with SMP support selected. The " + "combination hybrid heap and SMP support is currently " + "not supported."); + } + } + + if (malloc_lib) { + if (strcmp(malloc_lib, "libc") != 0) + usage("+MYm"); + } + emu = add_extra_suffixes(emu, emu_type); + sprintf(tmpStr, "%s" DIRSEP "%s" BINARY_EXT, bindir, emu); + emu = strsave(tmpStr); + + add_Eargs(emu); /* Will be argv[0] -- necessary! */ + + /* + * Add the bindir to the path (unless it is there already). + */ + + s = get_env("PATH"); + if (!s) { + sprintf(tmpStr, "%s" PATHSEP "%s" DIRSEP "bin", bindir, rootdir); + } else if (strstr(s, bindir) == NULL) { + sprintf(tmpStr, "%s" PATHSEP "%s" DIRSEP "bin" PATHSEP "%s", bindir, + rootdir, s); + } else { + sprintf(tmpStr, "%s", s); + } + free_env_val(s); + set_env("PATH", tmpStr); + + i = 1; + +#ifdef __WIN32__ +#define ADD_BOOT_CONFIG \ + if (boot_script) \ + add_args("-boot", boot_script, NULL); \ + if (config_script) \ + add_args("-config", config_script, NULL); +#else +#define ADD_BOOT_CONFIG +#endif + + get_home(); + 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 -start_erl and -boot options"); + if (i+1 >= argc) + usage("-boot"); + 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 an 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 (config_script) + error("Conflicting -start_erl and -config options"); + if (i+1 >= argc) + usage("-config"); + config_script = strsave(argv[i+1]); + i++; + } +#endif + 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 'i': + if (strcmp(argv[i], "-instr") == 0) { + add_Eargs("-Mim"); + add_Eargs("true"); + } + else + add_arg(argv[i]); + 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_qouted_cmd_exit") == 0) { + print_qouted_cmd_exit = 1; + } else if (strcmp(argv[i], "-env") == 0) { /* -env VARNAME VARVALUE */ + if (i+2 >= argc) + usage("-env"); + set_env(argv[i+1], argv[i+2]); + i += 2; + } else if (strcmp(argv[i], "-epmd") == 0) { + if (i+1 >= argc) + usage("-epmd"); + 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 an recursive call + * on itself here. We'll avoid doing that. + */ + if (strcmp(argv[i], "-make") == 0) { + add_args("-noshell", "-noinput", "-s", "make", "all", 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"; + sprintf(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 */ + if (i+1 >= argc) + usage("-name"); + + /* + * 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; + } else { + add_arg(argv[i]); + } + break; + + case 's': /* -sname NAME */ + if (strcmp(argv[i], "-sname") == 0) { + if (i+1 >= argc) + usage("-sname"); + add_arg(argv[i]); + add_arg(argv[i+1]); + isdistributed = 1; + i++; + } +#ifdef __WIN32__ + else if (strcmp(argv[i], "-service_event") == 0) { + 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 + 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 '#': + case 'a': + case 'A': + case 'b': + case 'h': + case 'i': + case 'P': + case 'S': + case 'T': + case 'R': + case 'W': + case 'K': + if (argv[i][2] != '\0') + goto the_default; + if (i+1 >= argc) + usage(argv[i]); + 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][0] != '-') && + (argv[i+1][0] != '+')) { + if (argv[i+1][0] == 'i') { + add_Eargs(argv[i]); + add_Eargs(argv[i+1]); + i++; + break; + } else { + usage(argv[i]); + } + } + } + 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)) { + if (i+1 >= argc + || argv[i+1][0] == '-' + || argv[i+1][0] == '+') + usage(argv[i]); + argv[i][0] = '-'; + add_Eargs(argv[i]); + add_Eargs(argv[i+1]); + i++; + } + else + goto the_default; + break; + } + case 's': + if (!is_one_of_strings(&argv[i][2], + pluss_val_switches)) + goto the_default; + else { + if (i+1 >= argc + || argv[i+1][0] == '-' + || argv[i+1][0] == '+') + usage(argv[i]); + 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++; + } + } + + if (process_args) { + ADD_BOOT_CONFIG; + } +#undef ADD_BOOT_CONFIG + + /* Doesn't conflict with -extra, since -make skips all the rest of + the arguments. */ + if (haltAfterwards) { + add_args("-s", "erlang", "halt", NULL); + } + + if (isdistributed && !no_epmd) + start_epmd(epmd_prog); + +#if (! defined(__WIN32__)) && defined(DEBUG) + if (start_detached) { + /* 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 ", Eargsp[i]); + printf("\n"); + 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); + } + 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 + "[-smp " +#ifdef ERTS_HAVE_SMP_EMU + "[enable|" +#endif + "auto|disable" +#ifdef ERTS_HAVE_SMP_EMU + "]" +#endif + "] " +#ifdef ERTS_HAVE_HYBRID_EMU + "[-hybrid] " +#endif + "[-make] [-man [manopts] MANPAGE] [-x] [-emu_args] " + "[-args_file FILENAME] " + "[+A THREADS] [+a SIZE] [+B[c|d|i]] [+c] [+h HEAP_SIZE] [+K BOOLEAN] " + "[+l] [+M<SUBSWITCH> <ARGUMENT>] [+P MAX_PROCS] [+R COMPAT_REL] " + "[+r] [+s SCHEDULER_OPTION] [+S NO_SCHEDULERS:NO_SCHEDULERS_ONLINE] [+T LEVEL] [+V] [+v] [+W<i|w>] " + "[args ...]\n"); + exit(1); +} + +void +usage(const char *switchname) +{ + fprintf(stderr, "Missing argument(s) for \'%s\'.\n", switchname); + usage_aux(); +} + +#if !defined(ERTS_HAVE_SMP_EMU) || !defined(ERTS_HAVE_HYBRID_EMU) +static void +usage_notsup(const char *switchname) +{ + fprintf(stderr, "Argument \'%s\' not supported.\n", switchname); + usage_aux(); +} +#endif + +static void +usage_msg(const char *msg) +{ + fprintf(stderr, "%s\n", msg); + 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(char *epmd) +{ + char epmd_cmd[MAXPATHLEN+100]; +#ifdef __WIN32__ + char* arg1 = NULL; +#endif + int result; + + if (!epmd) { + epmd = epmd_cmd; +#ifdef __WIN32__ + sprintf(epmd_cmd, "%s" DIRSEP "epmd", bindir); + arg1 = "-daemon"; +#else + sprintf(epmd_cmd, "%s" DIRSEP "epmd -daemon", bindir); +#endif + } +#ifdef __WIN32__ + if (arg1 != NULL) { + strcat(epmd, " "); + strcat(epmd, arg1); + } + { + STARTUPINFO start; + PROCESS_INFORMATION pi; + memset(&start, 0, sizeof (start)); + start.cb = sizeof (start); + if (!CreateProcess(NULL, epmd, 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)); +} + +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); + vsprintf(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, 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) +{ + 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_script) + error("Conflicting -start_erl and -config options"); + env = get_env("RELDIR"); + if (env) + reldir = strsave(env); + else { + sprintf(tmpbuffer, "%s/releases", rootdir); + reldir = strsave(tmpbuffer); + } + free_env_val(env); + if (file == NULL) + sprintf(start_erl_data, "%s/start_erl.data", reldir); + else + sprintf(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); + sprintf(bindir,"%s/erts-%s/bin",rootdir,tmpbuffer); + /* BINDIR=$ROOTDIR/erts-$ERTS_VSN/bin */ + tprogname = progname; + progname = emalloc(strlen(tprogname) + 20); + sprintf(progname,"%s -start_erl",tprogname); + + boot_script = emalloc(512); + config_script = emalloc(512); + sprintf(boot_script, "%s/%s/start", reldir, otpstring); + sprintf(config_script, "%s/%s/sys", reldir, otpstring); + +} + + +static char *replace_filename(char *path, char *new_base) +{ + int plen = strlen(path); + char *res = malloc((plen+strlen(new_base)+1)*sizeof(char)); + char *p; + + strcpy(res,path); + for (p = res+plen-1 ;p >= res && *p != '\\'; --p) + ; + *(p+1) ='\0'; + strcat(res,new_base); + return res; +} + +static char *path_massage(char *long_path) +{ + char *p; + + p = malloc(MAX_PATH+1); + strcpy(p, long_path); + GetShortPathName(p, p, MAX_PATH); + return p; +} + +static char *do_lookup_in_section(InitSection *inis, char *name, + char *section, char *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); + } + + if (is_path) { + return path_massage(p); + } else { + return strsave(p); + } +} + + +static void get_parameters(int argc, char** argv) +{ + char *p; + char buffer[MAX_PATH]; + char *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 (GetModuleFileName(module,buffer,MAX_PATH) == 0) { + error("Could not GetModuleFileName"); + } + + ini_filename = replace_filename(buffer,INI_FILENAME); + + if ((inif = load_init_file(ini_filename)) == NULL) { + /* Assume that the path is absolute and that + it does not contain any symbolic link */ + + char buffer[MAX_PATH]; + + /* Determine bindir */ + if (GetEnvironmentVariable("ERLEXEC_DIR", buffer, MAX_PATH) == 0) { + strcpy(buffer, ini_filename); + for (p = buffer+strlen(buffer)-1; p >= buffer && *p != '\\'; --p) + ; + *p ='\0'; + } + bindir = path_massage(buffer); + + /* Determine rootdir */ + for (p = buffer+strlen(buffer)-1; p >= buffer && *p != '\\'; --p) + ; + p--; + for (;p >= buffer && *p != '\\'; --p) + ; + *p ='\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) +{ + int len; + char tmpstr[MAX_PATH+1]; + char* homedrive; + char* homepath; + + homedrive = get_env("HOMEDRIVE"); + homepath = get_env("HOMEPATH"); + if (!homedrive || !homepath) { + if (len = GetWindowsDirectory(tmpstr,MAX_PATH)) { + home = emalloc(len+1); + strcpy(home,tmpstr); + } else + error("HOMEDRIVE or HOMEPATH is not set and GetWindowsDir 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]; + strcpy(buffer, argv[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]; + strcpy(buffer, bindir); + + 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"); + if (home == NULL) + error("HOME must be set"); +} + +#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); + free_env_val(value); + return res; +} + +static char **build_args_from_string(char *string) +{ + 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} 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 - 1) { /* Make room for extra NULL */ + 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 ' ': + ++p; + break; + case '\0': + state = Start; + break; + default: + state = Build; + break; + } + break; + case Build: + switch (*p) { + 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: + 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) { + state = Build; + } else { + ENSURE(); + (*cur_s)[s_pos++] = *p++; + } + state = Build; + break; + } + } +done: + argv[argc] = NULL; /* Sure to be large enough */ + if (!argc) { + efree(argv); + return NULL; + } + return argv; +#undef ENSURE +} + +static char * +errno_string(void) +{ + char *str = strerror(errno); + if (!str) + return "unknown error"; + return str; +} + +static char ** +read_args_file(char *filename) +{ + int c, aix = 0, quote = 0, cmnt = 0, asize = 0; + char **res, *astr = NULL; + FILE *file; + +#undef EAF_CMNT +#undef EAF_QUOTE +#undef SAVE_CHAR + +#define EAF_CMNT (1 << 8) +#define EAF_QUOTE (1 << 9) +#define SAVE_CHAR(C) \ + do { \ + if (!astr) \ + astr = emalloc(sizeof(char)*(asize = 20)); \ + if (aix == asize) \ + astr = erealloc(astr, sizeof(char)*(asize += 20)); \ + if (' ' != (char) (C)) \ + astr[aix++] = (char) (C); \ + else if (aix > 0 && astr[aix-1] != ' ') \ + astr[aix++] = ' '; \ + } while (0) + + do { + errno = 0; + file = fopen(filename, "r"); + } while (!file && errno == EINTR); + if (!file) { + usage_format("Failed to open arguments file \"%s\": %s\n", + filename, + errno_string()); + } + + while (1) { + c = getc(file); + if (c == EOF) { + if (ferror(file)) { + if (errno == EINTR) { + clearerr(file); + continue; + } + usage_format("Failed to read arguments file \"%s\": %s\n", + filename, + errno_string()); + } + break; + } + + switch (quote | cmnt | c) { + case '\\': + quote = EAF_QUOTE; + break; + case '#': + cmnt = EAF_CMNT; + break; + case EAF_CMNT|'\n': + cmnt = 0; + /* Fall through... */ + case '\n': + case '\f': + case '\r': + case '\t': + case '\v': + if (!quote) + c = ' '; + /* Fall through... */ + default: + if (!cmnt) + SAVE_CHAR(c); + quote = 0; + break; + } + } + + SAVE_CHAR('\0'); + + fclose(file); + + if (astr[0] == '\0') + res = NULL; + else + res = build_args_from_string(astr); + + efree(astr); + + return res; + +#undef EAF_CMNT +#undef EAF_QUOTE +#undef SAVE_CHAR +} + +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; + struct { + int argc; + char **argv; + } avv[] = {{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_AFLAGS"); + if (av) + avv[vix++].argv = av; + + /* command line */ + if (*argc > 1) { + avv[vix].argc = *argc - 1; + avv[vix++].argv = &(*argv)[1]; + } + + av = build_args_from_env("ERL_FLAGS"); + if (av) + avv[vix++].argv = av; + + av = build_args_from_env("ERL_" OTP_SYSTEM_VERSION "_FLAGS"); + if (av) + avv[vix++].argv = av; + + av = build_args_from_env("ERL_ZFLAGS"); + if (av) + avv[vix++].argv = av; + + if (vix == (*argc > 1 ? 1 : 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; +} + +#endif |