PageRenderTime 209ms CodeModel.GetById 31ms app.highlight 127ms RepoModel.GetById 34ms app.codeStats 0ms

/erts/etc/common/erlexec.c

https://github.com/system/erlang-otp
C | 1887 lines | 1855 code | 7 blank | 25 comment | 4 complexity | 5d792ae5ea83f7a45c158fca885ecb89 MD5 | raw file
   1/* ``The contents of this file are subject to the Erlang Public License,
   2 * Version 1.1, (the "License"); you may not use this file except in
   3 * compliance with the License. You should have received a copy of the
   4 * Erlang Public License along with this software. If not, it can be
   5 * retrieved via the world wide web at http://www.erlang.org/.
   6 * 
   7 * Software distributed under the License is distributed on an "AS IS"
   8 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
   9 * the License for the specific language governing rights and limitations
  10 * under the License.
  11 * 
  12 * The Initial Developer of the Original Code is Ericsson Utvecklings AB.
  13 * Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
  14 * AB. All Rights Reserved.''
  15 * 
  16 *     $Id$
  17 */
  18/*
  19 * This is a C version of the erl.exec Bourne shell script, including
  20 * additions required for Windows NT.
  21 */
  22
  23#ifdef HAVE_CONFIG_H
  24#  include "config.h"
  25#endif
  26
  27#include "sys.h"
  28#include "erl_driver.h"
  29#include <stdlib.h>
  30#include <stdarg.h>
  31#include "erl_misc_utils.h"
  32
  33#ifdef __WIN32__
  34#  include "erl_version.h"
  35#  include "init_file.h"
  36#endif
  37
  38#define NO 0
  39#define YES 1
  40
  41#ifdef __WIN32__
  42#define INI_FILENAME "erl.ini"
  43#define INI_SECTION "erlang"
  44#define DIRSEP "\\"
  45#define PATHSEP ";"
  46#define NULL_DEVICE "nul"
  47#define BINARY_EXT ""
  48#define DLL_EXT ".dll"
  49#define EMULATOR_EXECUTABLE "beam.dll"
  50#else
  51#define PATHSEP ":"
  52#define DIRSEP "/"
  53#define NULL_DEVICE "/dev/null"
  54#define BINARY_EXT ""
  55#endif
  56#define QUOTE(s) s
  57
  58/* +M alloc_util allocators */
  59static const char plusM_au_allocs[]= {
  60    'u',	/* all alloc_util allocators */
  61    'B',	/* binary_alloc		*/
  62    'D',	/* std_alloc		*/
  63    'E',	/* ets_alloc		*/
  64    'H',	/* eheap_alloc		*/
  65    'L',	/* ll_alloc		*/
  66    'R',	/* driver_alloc		*/
  67    'S',	/* sl_alloc		*/
  68    'T',	/* temp_alloc		*/
  69    '\0'
  70};
  71
  72/* +M alloc_util allocator specific arguments */
  73static char *plusM_au_alloc_switches[] = {
  74    "as",
  75    "asbcst",
  76    "e",
  77    "t",
  78    "lmbcs",
  79    "mbcgs",
  80    "mbsd",
  81    "mmbcs",
  82    "mmmbc",
  83    "mmsbc",
  84    "msbclt",
  85    "ramv",
  86    "rsbcmt",
  87    "rsbcst",
  88    "sbct",
  89    "smbcs",
  90    NULL
  91};
  92
  93/* +M other arguments */
  94static char *plusM_other_switches[] = {
  95    "ea",
  96    "ummc",
  97    "uycs",
  98    "im",
  99    "is",
 100    "it",
 101    "Mamcbf",
 102    "Mrmcbf",
 103    "Mmcs",
 104    "Mcci",
 105    "Fe",
 106    "Ye",
 107    "Ym",
 108    "Ytp",
 109    "Ytt",
 110    NULL
 111};
 112
 113
 114/*
 115 * Define sleep(seconds) in terms of Sleep() on Windows.
 116 */
 117
 118#ifdef __WIN32__
 119#define sleep(seconds) Sleep(seconds*1000)
 120#endif
 121
 122#define SMP_SUFFIX	  ".smp"
 123#define HYBRID_SUFFIX	  ".hybrid"
 124
 125#ifdef __WIN32__
 126#define DEBUG_SUFFIX      ".debug"
 127#define EMU_TYPE_SUFFIX_LENGTH  (strlen(HYBRID_SUFFIX)+(strlen(DEBUG_SUFFIX)))
 128#else
 129/* The length of the longest memory architecture suffix. */
 130#define EMU_TYPE_SUFFIX_LENGTH  strlen(HYBRID_SUFFIX)
 131#endif
 132/*
 133 * Define flags for different memory architectures.
 134 */
 135#define EMU_TYPE_SMP		0x0001
 136#define EMU_TYPE_HYBRID		0x0002
 137
 138#ifdef __WIN32__
 139#define EMU_TYPE_DEBUG		0x0004
 140#endif
 141
 142void usage(const char *switchname);
 143void start_epmd(char *epmd);
 144void error(char* format, ...);
 145
 146/*
 147 * Local functions.
 148 */
 149
 150#if !defined(ERTS_HAVE_SMP_EMU) || !defined(ERTS_HAVE_HYBRID_EMU)
 151static void usage_notsup(const char *switchname);
 152#endif
 153static void usage_msg(const char *msg);
 154static char **build_args_from_env(char *env_var);
 155static char **build_args_from_string(char *env_var);
 156static void initial_argv_massage(int *argc, char ***argv);
 157static void get_parameters(int argc, char** argv);
 158static void add_arg(char *new_arg);
 159static void add_args(char *first_arg, ...);
 160static void ensure_EargsSz(int sz);
 161static void add_Eargs(char *new_arg);
 162static void *emalloc(size_t size);
 163static void *erealloc(void *p, size_t size);
 164static void efree(void *p);
 165static char* strsave(char* string);
 166static int is_one_of_strings(char *str, char *strs[]);
 167static char *write_str(char *to, char *from);
 168static void get_home(void);
 169#ifdef __WIN32__
 170static void get_start_erl_data(char *);
 171static char* get_value(HKEY key, char* value_name, BOOL mustExit);
 172static char* possibly_quote(char* arg);
 173
 174/* 
 175 * Functions from win_erlexec.c
 176 */
 177int start_win_emulator(char* emu, char *startprog,char** argv, int start_detached);
 178int start_emulator(char* emu, char*start_prog, char** argv, int start_detached);
 179#endif
 180
 181
 182
 183/*
 184 * Variables.
 185 */
 186int nohup = 0;
 187int keep_window = 0;
 188
 189static char **Eargsp = NULL;	/* Emulator arguments (to appear first). */
 190static int EargsSz = 0;		/* Size of Eargsp */
 191static int EargsCnt = 0;	/* Number of emulator arguments. */
 192static char **argsp = NULL;	/* Common arguments. */
 193static int argsCnt = 0;		/* Number of common arguments */
 194static int argsSz = 0;		/* Size of argsp */
 195static char tmpStr[10240];	/* Temporary string buffer. */
 196static int verbose = 0;		/* If non-zero, print some extra information. */
 197static int start_detached = 0;	/* If non-zero, the emulator should be
 198				 * started detached (in the background).
 199				 */
 200static int emu_type = 0;	/* If non-zero, start beam.ARCH or beam.ARCH.exe
 201				 * instead of beam or beam.exe, where ARCH is defined by flags. */
 202static int emu_type_passed = 0;	/* Types explicitly set */
 203
 204#ifdef __WIN32__
 205static char *start_emulator_program = NULL; /* For detachec mode - 
 206					       erl.exe/werl.exe */
 207static char* key_val_name = ERLANG_VERSION; /* Used by the registry
 208					   * access functions.
 209					   */
 210static char* boot_script = NULL; /* used by option -start_erl and -boot */
 211static char* config_script = NULL; /* used by option -start_erl and -config */
 212
 213static HANDLE this_module_handle;
 214static int run_werl;
 215
 216#endif
 217
 218/*
 219 * Needed parameters to be fetched from the environment (Unix)
 220 * or the ini file (Win32).
 221 */
 222
 223static char* bindir;		/* Location of executables. */
 224static char* rootdir;		/* Root location of Erlang installation. */
 225static char* emu;		/* Emulator to run. */
 226static char* progname;		/* Name of this program. */
 227static char* home;		/* Path of user's home directory. */
 228
 229static void
 230set_env(char *key, char *value)
 231{
 232#ifdef __WIN32__
 233    if (!SetEnvironmentVariable((LPCTSTR) key, (LPCTSTR) value))
 234	error("SetEnvironmentVariable(\"%s\", \"%s\") failed!", key, value);
 235#else
 236    size_t size = strlen(key) + 1 + strlen(value) + 1;
 237    char *str = emalloc(size);
 238    sprintf(str, "%s=%s", key, value);
 239    if (putenv(str) != 0)
 240	error("putenv(\"%s\") failed!", str);
 241#ifdef HAVE_COPYING_PUTENV
 242    efree(str);
 243#endif
 244#endif
 245}
 246
 247static char *
 248get_env(char *key)
 249{
 250#ifdef __WIN32__
 251    DWORD size = 32;
 252    char *value = NULL;
 253    while (1) {
 254	DWORD nsz;
 255	if (value)
 256	    efree(value);
 257	value = emalloc(size);
 258	SetLastError(0);
 259	nsz = GetEnvironmentVariable((LPCTSTR) key, (LPTSTR) value, size);
 260	if (nsz == 0 && GetLastError() == ERROR_ENVVAR_NOT_FOUND) {
 261	    efree(value);
 262	    return NULL;
 263	}
 264	if (nsz <= size)
 265	    return value;
 266	size = nsz;
 267    }
 268#else
 269    return getenv(key);
 270#endif
 271}
 272
 273static void
 274free_env_val(char *value)
 275{
 276#ifdef __WIN32__
 277    if (value)
 278	free(value);
 279#endif
 280}
 281
 282/*
 283 * Add the arcitecture suffix to the program name if needed,
 284 * except on Windows, where we insert it just before ".DLL".
 285 */
 286static char*
 287add_extra_suffixes(char *prog, int type)
 288{
 289   char *res;
 290   char *p;
 291   int len;
 292#ifdef __WIN32__
 293   char *dll_p;
 294   int dll = 0;
 295#endif
 296
 297   if (!type) {
 298       return prog;
 299   }
 300
 301   len = strlen(prog);
 302
 303   /* Worst-case allocation */
 304   p = emalloc(len +
 305	       EMU_TYPE_SUFFIX_LENGTH +
 306	       + 1);
 307   res = p;
 308   p = write_str(p, prog);
 309
 310#ifdef __WIN32__
 311   dll_p = res + len - 4;
 312   if (dll_p >= res) {
 313      if (dll_p[0] == '.' &&
 314	  (dll_p[1] == 'd' || dll_p[1] == 'D') &&
 315	  (dll_p[2] == 'l' || dll_p[2] == 'L') &&
 316	  (dll_p[3] == 'l' || dll_p[3] == 'L')) {
 317	  p = dll_p;
 318	  dll = 1;
 319      }
 320   }
 321#endif
 322
 323#ifdef __WIN32__
 324   if (type & EMU_TYPE_DEBUG) {
 325       p = write_str(p, DEBUG_SUFFIX);
 326       type &= ~(EMU_TYPE_DEBUG);
 327   }
 328#endif
 329   if (type == EMU_TYPE_SMP) {
 330       p = write_str(p, SMP_SUFFIX);
 331   }
 332   else if (type == EMU_TYPE_HYBRID) {
 333       p = write_str(p, HYBRID_SUFFIX);
 334   }
 335#ifdef __WIN32__
 336   if (dll) {
 337       p = write_str(p, DLL_EXT);
 338   }
 339#endif
 340
 341   return res;
 342}
 343
 344#ifdef __WIN32__
 345__declspec(dllexport) int win_erlexec(int argc, char **argv, HANDLE module, int windowed)
 346#else
 347int main(int argc, char **argv)
 348#endif
 349{
 350    int haltAfterwards = 0;	/* If true, put 's erlang halt' at the end
 351				 * of the arguments. */
 352    int isdistributed = 0;
 353    int no_epmd = 0;
 354    int i;
 355    char* s;
 356    char *epmd_prog = NULL;
 357    char *malloc_lib;
 358    int process_args = 1;
 359    int print_args_exit = 0;
 360
 361#ifdef __WIN32__
 362    this_module_handle = module;
 363    run_werl = windowed;
 364    /* if we started this erl just to get a detached emulator, 
 365     * the arguments are already prepared for beam, so we skip
 366     * directly to start_emulator */
 367    s = get_env("ERL_CONSOLE_MODE");
 368    if (s != NULL && strcmp(s, "detached")==0) {
 369	free_env_val(s);
 370	s = get_env("ERL_EMULATOR_DLL");
 371	if (s != NULL) {
 372	    argv[0] = strsave(s);
 373	} else {
 374	    argv[0] = strsave(EMULATOR_EXECUTABLE);
 375	}
 376	ensure_EargsSz(argc + 1);
 377	memcpy((void *) Eargsp, (void *) argv, argc * sizeof(char *));
 378	Eargsp[argc] = NULL;
 379	emu = argv[0];
 380	start_emulator_program = strsave(argv[0]);
 381	goto skip_arg_massage;
 382    }   
 383    free_env_val(s);
 384#endif
 385
 386    initial_argv_massage(&argc, &argv); /* Merge with env; expand -args_file */
 387
 388    i = 1;
 389#ifdef __WIN32__
 390    /* Not used? /rickard */
 391    if ((argc > 2) && (strcmp(argv[i], "-regkey") == 0)) {
 392	key_val_name = strsave(argv[i+1]);
 393	i = 3;
 394    }
 395#endif		
 396
 397    get_parameters(argc, argv);
 398    
 399    /*
 400     * Construct the path of the executable.
 401     */
 402
 403    /* '-smp auto' is default */ 
 404#ifdef ERTS_HAVE_SMP_EMU
 405    if (erts_no_of_cpus() > 1)
 406	emu_type |= EMU_TYPE_SMP;
 407#endif
 408
 409#if defined(__WIN32__) && defined(WIN32_ALWAYS_DEBUG)
 410    emu_type_passed |= EMU_TYPE_DEBUG;
 411    emu_type |= EMU_TYPE_DEBUG;
 412#endif
 413
 414    /* We need to do this before the ordinary processing. */
 415    malloc_lib = get_env("ERL_MALLOC_LIB");
 416    while (i < argc) {
 417	if (argv[i][0] == '+') {
 418	    if (argv[i][1] == 'M' && argv[i][2] == 'Y' && argv[i][3] == 'm') {
 419		if (argv[i][4] == '\0') {
 420		    if (++i < argc)
 421			malloc_lib = argv[i];
 422		    else
 423			usage("+MYm");
 424		}
 425		else
 426		    malloc_lib = &argv[i][4];
 427	    }
 428	}
 429	else if (argv[i][0] == '-') {
 430	    if (strcmp(argv[i], "-smp") == 0) {
 431		if (i + 1 >= argc)
 432		    goto smp;
 433
 434		if (strcmp(argv[i+1], "auto") == 0) {
 435		    i++;
 436		smp_auto:
 437		    emu_type_passed |= EMU_TYPE_SMP;
 438#ifdef ERTS_HAVE_SMP_EMU
 439		    if (erts_no_of_cpus() > 1)
 440			emu_type |= EMU_TYPE_SMP;
 441		    else
 442#endif
 443			emu_type &= ~EMU_TYPE_SMP;
 444		}
 445		else if (strcmp(argv[i+1], "enable") == 0) {
 446		    i++;
 447		smp_enable:
 448		    emu_type_passed |= EMU_TYPE_SMP;
 449#ifdef ERTS_HAVE_SMP_EMU
 450		    emu_type |= EMU_TYPE_SMP;
 451#else
 452		    usage_notsup("-smp enable");
 453#endif
 454		}
 455		else if (strcmp(argv[i+1], "disable") == 0) {
 456		    i++;
 457		smp_disable:
 458		    emu_type_passed |= EMU_TYPE_SMP;
 459		    emu_type &= ~EMU_TYPE_SMP;
 460		}
 461		else {
 462		smp:
 463
 464		    emu_type_passed |= EMU_TYPE_SMP;
 465#ifdef ERTS_HAVE_SMP_EMU
 466		    emu_type |= EMU_TYPE_SMP;
 467#else
 468		    usage_notsup("-smp");
 469#endif
 470		}
 471	    } else if (strcmp(argv[i], "-smpenable") == 0) {
 472		goto smp_enable;
 473	    } else if (strcmp(argv[i], "-smpauto") == 0) {
 474		goto smp_auto;
 475	    } else if (strcmp(argv[i], "-smpdisable") == 0) {
 476		goto smp_disable;
 477#ifdef __WIN32__
 478	    } else if (strcmp(argv[i], "-debug") == 0) {
 479		emu_type_passed |= EMU_TYPE_DEBUG;
 480		emu_type |= EMU_TYPE_DEBUG;
 481#endif
 482	    } else if (strcmp(argv[i], "-hybrid") == 0) {
 483		emu_type_passed |= EMU_TYPE_HYBRID;
 484#ifdef ERTS_HAVE_HYBRID_EMU
 485		emu_type |= EMU_TYPE_HYBRID;
 486#else
 487		usage_notsup("-hybrid");
 488#endif
 489	    } else if (strcmp(argv[i], "-extra") == 0) {
 490		break;
 491	    }
 492	}
 493	i++;
 494    }
 495
 496    if ((emu_type & EMU_TYPE_HYBRID) && (emu_type & EMU_TYPE_SMP)) {
 497	/*
 498	 * We have a conflict. Only using explicitly passed arguments
 499	 * may solve it...
 500	 */
 501	emu_type &= emu_type_passed;
 502	if ((emu_type & EMU_TYPE_HYBRID) && (emu_type & EMU_TYPE_SMP)) {
 503	    usage_msg("Hybrid heap emulator with SMP support selected. The "
 504		      "combination hybrid heap and SMP support is currently "
 505		      "not supported.");
 506	}
 507    }
 508
 509    if (malloc_lib) {
 510	if (strcmp(malloc_lib, "libc") != 0)
 511	    usage("+MYm");
 512    }
 513    emu = add_extra_suffixes(emu, emu_type);
 514    sprintf(tmpStr, "%s" DIRSEP "%s" BINARY_EXT, bindir, emu);
 515    emu = strsave(tmpStr);
 516
 517    add_Eargs(emu);		/* Will be argv[0] -- necessary! */
 518
 519    /*
 520     * Add the bindir to the path (unless it is there already).
 521     */
 522
 523    s = get_env("PATH");
 524    if (!s) {
 525	sprintf(tmpStr, "%s" PATHSEP "%s" DIRSEP "bin", bindir, rootdir);
 526    } else if (strstr(s, bindir) == NULL) {
 527	sprintf(tmpStr, "%s" PATHSEP "%s" DIRSEP "bin" PATHSEP "%s", bindir, 
 528		rootdir, s);
 529    } else {
 530	sprintf(tmpStr, "%s", s);
 531    }
 532    free_env_val(s);
 533    set_env("PATH", tmpStr);
 534    
 535    i = 1;
 536
 537#ifdef __WIN32__
 538#define ADD_BOOT_CONFIG					\
 539    if (boot_script)					\
 540	add_args("-boot", boot_script, NULL);		\
 541    if (config_script)					\
 542	add_args("-config", config_script, NULL);
 543#else
 544#define ADD_BOOT_CONFIG
 545#endif
 546
 547    get_home();
 548    add_args("-home", home, NULL);
 549    while (i < argc) {
 550	if (!process_args) {	/* Copy arguments after '-extra' */
 551	    add_arg(argv[i]);
 552	    i++;
 553	} else {
 554	    switch (argv[i][0]) {
 555	      case '-':
 556		switch (argv[i][1]) {
 557#ifdef __WIN32__
 558		case 'b':
 559		    if (strcmp(argv[i], "-boot") == 0) {
 560			if (boot_script)
 561			    error("Conflicting -start_erl and -boot options");
 562			if (i+1 >= argc)
 563			    usage("-boot");
 564			boot_script = strsave(argv[i+1]);
 565			i++;
 566		    }
 567		    else {
 568			add_arg(argv[i]);
 569		    }
 570		    break;
 571#endif
 572		case 'c':
 573		    if (strcmp(argv[i], "-compile") == 0) {
 574			/*
 575			 * Note that the shell script erl.exec does an recursive call
 576			 * on itself here.  We'll avoid doing that.
 577			 */
 578			add_args("-noshell", "-noinput", "-s", "c", "lc_batch",
 579				 NULL);
 580			add_Eargs("-B");
 581			haltAfterwards = 0;
 582		    }
 583#ifdef __WIN32__
 584		    else if (strcmp(argv[i], "-config") == 0){
 585			if (config_script)
 586			    error("Conflicting -start_erl and -config options");
 587			if (i+1 >= argc)
 588			    usage("-config");
 589			config_script = strsave(argv[i+1]);
 590			i++;
 591		    }
 592#endif
 593		    else {
 594			add_arg(argv[i]);
 595		    }
 596		    break;
 597
 598		  case 'd':
 599		    if (strcmp(argv[i], "-detached") != 0) {
 600			add_arg(argv[i]);
 601		    } else {
 602			start_detached = 1;
 603			add_args("-noshell", "-noinput", NULL);
 604		    }
 605		    break;
 606
 607		  case 'i':
 608		    if (strcmp(argv[i], "-instr") == 0) {
 609			add_Eargs("-Mim");
 610			add_Eargs("true");
 611		    }
 612		    else
 613			add_arg(argv[i]);
 614		    break;
 615
 616		  case 'e':
 617		    if (strcmp(argv[i], "-extra") == 0) {
 618			process_args = 0;
 619			ADD_BOOT_CONFIG;
 620			add_arg(argv[i]);
 621		    } else if (strcmp(argv[i], "-emu_args") == 0) { /* -emu_args */
 622			verbose = 1;
 623		    } else if (strcmp(argv[i], "-emu_args_exit") == 0) {
 624			print_args_exit = 1;
 625		    } else if (strcmp(argv[i], "-env") == 0) { /* -env VARNAME VARVALUE */
 626			if (i+2 >= argc)
 627			    usage("-env");
 628			set_env(argv[i+1], argv[i+2]);
 629			i += 2;
 630		    } else if (strcmp(argv[i], "-epmd") == 0) { 
 631			if (i+1 >= argc)
 632			    usage("-epmd");
 633			epmd_prog = argv[i+1];
 634			++i;
 635		    } else {
 636			add_arg(argv[i]);
 637		    }
 638		    break;
 639		  case 'k':
 640		    if (strcmp(argv[i], "-keep_window") == 0) {
 641			keep_window = 1;
 642		    } else
 643			add_arg(argv[i]);
 644		    break;
 645
 646		  case 'm':
 647		    /*
 648		     * Note that the shell script erl.exec does an recursive call
 649		     * on itself here.  We'll avoid doing that.
 650		     */
 651		    if (strcmp(argv[i], "-make") == 0) {
 652			add_args("-noshell", "-noinput", "-s", "make", "all", NULL);
 653			add_Eargs("-B");
 654			haltAfterwards = 1;
 655			i = argc; /* Skip rest of command line */
 656		    } else if (strcmp(argv[i], "-man") == 0) {
 657#if defined(__WIN32__)
 658			error("-man not supported on Windows");
 659#else
 660			argv[i] = "man";
 661			sprintf(tmpStr, "%s/man", rootdir);
 662			set_env("MANPATH", tmpStr);
 663			execvp("man", argv+i);
 664			error("Could not execute the 'man' command.");
 665#endif
 666		    } else
 667			add_arg(argv[i]);
 668		    break;
 669
 670		  case 'n':
 671		    if (strcmp(argv[i], "-name") == 0) { /* -name NAME */
 672			if (i+1 >= argc)
 673			    usage("-name");
 674		    
 675			/*
 676			 * Note: Cannot use add_args() here, due to non-defined
 677			 * evaluation order.
 678			 */
 679
 680			add_arg(argv[i]);
 681			add_arg(argv[i+1]);
 682			isdistributed = 1;
 683			i++;
 684		    } else if (strcmp(argv[i], "-noinput") == 0) {
 685			add_args("-noshell", "-noinput", NULL);
 686		    } else if (strcmp(argv[i], "-nohup") == 0) {
 687			add_arg("-nohup");
 688			nohup = 1;
 689		    } else if (strcmp(argv[i], "-no_epmd") == 0) {
 690			add_arg("-no_epmd");
 691			no_epmd = 1;
 692		    } else {
 693			add_arg(argv[i]);
 694		    }
 695		    break;
 696
 697		  case 's':	/* -sname NAME */
 698		    if (strcmp(argv[i], "-sname") == 0) {
 699			if (i+1 >= argc)
 700			    usage("-sname");
 701			add_arg(argv[i]);
 702			add_arg(argv[i+1]);
 703			isdistributed = 1;
 704			i++;
 705		    }
 706#ifdef __WIN32__
 707		    else if (strcmp(argv[i], "-service_event") == 0) {
 708			add_arg(argv[i]);
 709			add_arg(argv[i+1]);
 710			i++;
 711		    }		    
 712		    else if (strcmp(argv[i], "-start_erl") == 0) {
 713			if (i+1 < argc && argv[i+1][0] != '-') {
 714			    get_start_erl_data(argv[i+1]);
 715			    i++;
 716			} else
 717			    get_start_erl_data((char *) NULL);
 718		    }
 719#endif
 720		    else
 721			add_arg(argv[i]);
 722		
 723		    break;
 724	
 725		  case 'v':	/* -version */
 726		    if (strcmp(argv[i], "-version") == 0) {
 727			add_Eargs("-V");
 728		    } else {
 729			add_arg(argv[i]);
 730		    }
 731		    break;
 732
 733		  default:
 734		    add_arg(argv[i]);
 735		    break;
 736		} /* switch(argv[i][1] */
 737		break;
 738
 739	      case '+':
 740		switch (argv[i][1]) {
 741		  case '#':
 742		  case 'a':
 743		  case 'A':
 744		  case 'b':
 745		  case 'h':
 746		  case 'i':
 747		  case 'P':
 748		  case 'S':
 749		  case 'T':
 750		  case 'R':
 751		  case 'W':
 752		  case 'K':
 753		      if (argv[i][2] != '\0')
 754			  goto the_default;
 755		      if (i+1 >= argc)
 756			  usage(argv[i]);
 757		      argv[i][0] = '-';
 758		      add_Eargs(argv[i]);
 759		      add_Eargs(argv[i+1]);
 760		      i++;
 761		      break;
 762		  case 'B':
 763		      argv[i][0] = '-';
 764		      if (argv[i][2] != '\0') {
 765			  if ((argv[i][2] != 'i') &&
 766			      (argv[i][2] != 'c') &&
 767			      (argv[i][2] != 'd')) { 
 768			  usage(argv[i]);
 769			} else {
 770			  add_Eargs(argv[i]);
 771			  break;
 772			}
 773		      }
 774		      if (i+1 < argc) {
 775			if ((argv[i+1][0] != '-') &&
 776			    (argv[i+1][0] != '+')) {
 777			  if (argv[i+1][0] == 'i') {
 778			    add_Eargs(argv[i]);
 779			    add_Eargs(argv[i+1]);
 780			    i++;
 781			    break;
 782			  } else {
 783			    usage(argv[i]);
 784			  }
 785			}
 786		      }
 787		      add_Eargs(argv[i]);
 788		      break;
 789		  case 'M': {
 790		      int x;
 791		      for (x = 0; plusM_au_allocs[x]; x++)
 792			  if (plusM_au_allocs[x] == argv[i][2])
 793			      break;
 794		      if ((plusM_au_allocs[x]
 795			   && is_one_of_strings(&argv[i][3],
 796						plusM_au_alloc_switches))
 797			  || is_one_of_strings(&argv[i][2],
 798					       plusM_other_switches)) {
 799			  if (i+1 >= argc
 800			      || argv[i+1][0] == '-'
 801			      || argv[i+1][0] == '+')
 802			      usage(argv[i]);
 803			  argv[i][0] = '-';
 804			  add_Eargs(argv[i]);
 805			  add_Eargs(argv[i+1]);
 806			  i++;
 807		      }
 808		      else
 809			  goto the_default;
 810		      break;
 811		  }
 812		  default:
 813		  the_default:
 814		    argv[i][0] = '-'; /* Change +option to -option. */
 815		    add_Eargs(argv[i]);
 816		}
 817		break;
 818      
 819	      default:
 820		add_arg(argv[i]);
 821	    } /* switch(argv[i][0] */
 822	    i++;
 823	}
 824    }
 825
 826    if (process_args) {
 827	ADD_BOOT_CONFIG;
 828    }
 829#undef ADD_BOOT_CONFIG
 830
 831    /* Doesn't conflict with -extra, since -make skips all the rest of
 832       the arguments. */
 833    if (haltAfterwards) {
 834	add_args("-s", "erlang", "halt", NULL);
 835    }
 836    
 837    if (isdistributed && !no_epmd)
 838	start_epmd(epmd_prog);
 839
 840#if (! defined(__WIN32__)) && defined(DEBUG)
 841    if (start_detached) {
 842	/* Start the emulator within an xterm.
 843	 * Move up all arguments and insert
 844	 * "xterm -e " first.
 845	 * The path must be searched for this 
 846	 * to work, i.e execvp() must be used. 
 847	 */
 848	ensure_EargsSz(EargsCnt+2);
 849	for (i = EargsCnt; i > 0; i--)
 850	    Eargsp[i+1] = Eargsp[i-1]; /* Two args to insert */
 851	EargsCnt += 2; /* Two args to insert */
 852	Eargsp[0] = emu = "xterm";
 853	Eargsp[1] = "-e";
 854    }    
 855#endif
 856    
 857    add_Eargs("--");
 858    add_Eargs("-root");
 859    add_Eargs(rootdir);
 860    add_Eargs("-progname");
 861    add_Eargs(progname);
 862    add_Eargs("--");
 863    ensure_EargsSz(EargsCnt + argsCnt + 1);
 864    for (i = 0; i < argsCnt; i++)
 865	Eargsp[EargsCnt++] = argsp[i];
 866    Eargsp[EargsCnt] = NULL;
 867    
 868    if (print_args_exit) {
 869	for (i = 1; i < EargsCnt; i++)
 870	    printf("%s ", Eargsp[i]);
 871	printf("\n");
 872	exit(0);
 873    }
 874
 875    if (verbose) {
 876	printf("Executing: %s", emu);
 877	for (i = 0; i < EargsCnt; i++)
 878	    printf(" %s", Eargsp[i]);
 879	printf("\n\n");
 880    }
 881
 882#ifdef __WIN32__
 883
 884    if (EargsSz != EargsCnt + 1)
 885	Eargsp = (char **) erealloc((void *) Eargsp, (EargsCnt + 1) * 
 886				    sizeof(char *));
 887    efree((void *) argsp);
 888
 889 skip_arg_massage:
 890    /*DebugBreak();*/
 891
 892    if (run_werl) {
 893	if (start_detached) {
 894	    char *p;
 895	    /* transform werl to erl */
 896	    p = start_emulator_program+strlen(start_emulator_program);
 897	    while (--p >= start_emulator_program && *p != '/' && *p != '\\' &&
 898		   *p != 'W' && *p != 'w')
 899		;
 900	    if (p >= start_emulator_program && (*p == 'W' || *p == 'w') &&
 901		(p[1] == 'E' || p[1] == 'e') && (p[2] == 'R' || p[2] == 'r') &&
 902		(p[3] == 'L' || p[3] == 'l')) {
 903		memmove(p,p+1,strlen(p));
 904	    }
 905	}
 906      return start_win_emulator(emu, start_emulator_program, Eargsp, start_detached);
 907    } else {
 908      return start_emulator(emu, start_emulator_program, Eargsp, start_detached);
 909    }
 910
 911#else
 912    if (start_detached) {
 913	int status = fork();
 914	if (status != 0)	/* Parent */
 915	    return 0;
 916	status = fork();
 917	if (status != 0)	/* Parent */
 918	    return 0;
 919
 920	/*
 921	 * Grandchild.
 922	 */
 923	close(0);
 924	open("/dev/null", O_RDONLY);
 925	close(1);
 926	open("/dev/null", O_WRONLY);
 927	close(2);
 928	open("/dev/null", O_WRONLY);
 929#ifdef DEBUG
 930	execvp(emu, Eargsp); /* "xterm ..." needs to search the path */
 931#endif
 932    } 
 933#ifdef DEBUG
 934    else
 935#endif
 936    {
 937	execv(emu, Eargsp);
 938    }
 939    error("Error %d executing \'%s\'.", errno, emu);
 940    return 1;
 941#endif
 942}
 943
 944
 945static void
 946usage_aux(void)
 947{
 948  fprintf(stderr,
 949	  "Usage: erl [-version] [-sname NAME | -name NAME] "
 950	  "[-noshell] [-noinput] [-env VAR VALUE] [-compile file ...] "
 951#ifdef __WIN32__
 952	  "[-start_erl [datafile]] "
 953#endif
 954	  "[-smp "
 955#ifdef ERTS_HAVE_SMP_EMU
 956	  "[enable|"
 957#endif
 958	  "auto|disable"
 959#ifdef ERTS_HAVE_SMP_EMU
 960	  "]"
 961#endif
 962	  "] "
 963#ifdef ERTS_HAVE_HYBRID_EMU
 964	  "[-hybrid] "
 965#endif
 966	  "[-make] [-man [manopts] MANPAGE] [-x] [-emu_args] "
 967	  "[-args_file FILENAME] "
 968	  "[+A THREADS] [+a SIZE] [+B[c|d|i]] [+c] [+h HEAP_SIZE] [+K BOOLEAN] "
 969	  "[+l] [+M<SUBSWITCH> <ARGUMENT>] [+P MAX_PROCS] [+R COMPAT_REL] "
 970	  "[+r] [+S NO_OF_SCHEDULERS] [+T LEVEL] [+V] [+v] [+W<i|w>] "
 971	  "[args ...]\n");
 972  exit(1);
 973}
 974
 975void
 976usage(const char *switchname)
 977{
 978    fprintf(stderr, "Missing argument(s) for \'%s\'.\n", switchname);
 979    usage_aux();
 980}
 981
 982#if !defined(ERTS_HAVE_SMP_EMU) || !defined(ERTS_HAVE_HYBRID_EMU)
 983static void
 984usage_notsup(const char *switchname)
 985{
 986    fprintf(stderr, "Argument \'%s\' not supported.\n", switchname);
 987    usage_aux();
 988}
 989#endif
 990
 991static void
 992usage_msg(const char *msg)
 993{
 994    fprintf(stderr, "%s\n", msg);
 995    usage_aux();
 996}
 997
 998static void
 999usage_format(char *format, ...)
1000{
1001    va_list args;
1002    va_start(args, format);
1003    vfprintf(stderr, format, args);
1004    va_end(args);
1005    usage_aux();
1006}
1007
1008void
1009start_epmd(char *epmd)
1010{
1011    char  epmd_cmd[MAXPATHLEN+100];
1012#ifdef __WIN32__
1013    char* arg1 = NULL;
1014#endif
1015    int   result;
1016
1017    if (!epmd) {
1018	epmd = epmd_cmd;
1019#ifdef __WIN32__
1020	sprintf(epmd_cmd, "%s" DIRSEP "epmd", bindir);
1021	arg1 = "-daemon";
1022#else
1023	sprintf(epmd_cmd, "%s" DIRSEP "epmd -daemon", bindir);
1024#endif
1025    } 
1026#ifdef __WIN32__
1027    if (arg1 != NULL) {
1028	strcat(epmd, " ");
1029	strcat(epmd, arg1);
1030    }
1031    {
1032	STARTUPINFO start;
1033	PROCESS_INFORMATION pi;
1034	memset(&start, 0, sizeof (start));
1035	start.cb = sizeof (start);
1036	if (!CreateProcess(NULL, epmd, NULL, NULL, FALSE, 
1037			       CREATE_DEFAULT_ERROR_MODE | DETACHED_PROCESS,
1038			       NULL, NULL, &start, &pi))
1039	    result = -1;
1040	else
1041	    result = 0;
1042    }
1043#else
1044    result = system(epmd);
1045#endif
1046    if (result == -1) {
1047      fprintf(stderr, "Error spawning %s (error %d)\n", epmd_cmd,errno);
1048      exit(1);
1049    }
1050}
1051
1052static void
1053add_arg(char *new_arg)
1054{
1055    if (argsCnt >= argsSz)
1056	argsp = (char **) erealloc((void *) argsp,
1057				   sizeof(char *) * (argsSz += 20));
1058    argsp[argsCnt++] = QUOTE(new_arg);
1059}
1060
1061static void
1062add_args(char *first_arg, ...)
1063{
1064    va_list ap;
1065    char* arg;
1066    
1067    add_arg(first_arg);
1068    va_start(ap, first_arg);
1069    while ((arg = va_arg(ap, char *)) != NULL) {
1070	add_arg(arg);
1071    }
1072    va_end(ap);
1073}
1074
1075static void
1076ensure_EargsSz(int sz)
1077{
1078    if (EargsSz < sz)
1079	Eargsp = (char **) erealloc((void *) Eargsp,
1080				    sizeof(char *) * (EargsSz = sz));
1081}
1082
1083static void
1084add_Eargs(char *new_arg)
1085{
1086    if (EargsCnt >= EargsSz)
1087	Eargsp = (char **) erealloc((void *) Eargsp,
1088				    sizeof(char *) * (EargsSz += 20));
1089    Eargsp[EargsCnt++] = QUOTE(new_arg);
1090}
1091
1092#if !defined(__WIN32__)
1093void error(char* format, ...)
1094{
1095    char sbuf[1024];
1096    va_list ap;
1097
1098    va_start(ap, format);
1099    vsprintf(sbuf, format, ap);
1100    va_end(ap);
1101    fprintf(stderr, "erlexec: %s\n", sbuf);
1102    exit(1);
1103}
1104#endif
1105
1106static void *
1107emalloc(size_t size)
1108{
1109    void *p = malloc(size);
1110    if (p == NULL)
1111	error("Insufficient memory");
1112    return p;
1113}
1114
1115static void *
1116erealloc(void *p, size_t size)
1117{
1118    void *res = realloc(p, size);
1119    if (res == NULL)
1120	error("Insufficient memory");
1121    return res;
1122}
1123
1124static void
1125efree(void *p) 
1126{
1127    free(p);
1128}
1129
1130static int
1131is_one_of_strings(char *str, char *strs[])
1132{
1133    int i, j;
1134    for (i = 0; strs[i]; i++) {
1135	for (j = 0; str[j] && strs[i][j] && str[j] == strs[i][j]; j++);
1136	if (!str[j] && !strs[i][j])
1137	    return 1;
1138    }
1139    return 0;
1140}
1141
1142static char *write_str(char *to, char *from)
1143{
1144    while (*from)
1145	*(to++) = *(from++);
1146    *to = '\0';
1147    return to;
1148}
1149
1150char*
1151strsave(char* string)
1152{
1153    char* p = emalloc(strlen(string)+1);
1154    strcpy(p, string);
1155    return p;
1156}
1157
1158
1159#if defined(__WIN32__)
1160
1161static void get_start_erl_data(char *file)
1162{
1163    int fp;
1164    char tmpbuffer[512];
1165    char start_erl_data[512];
1166    int bytesread;
1167    char* env;
1168    char* reldir;
1169    char* otpstring;
1170    char* tprogname;
1171    if (boot_script) 
1172	error("Conflicting -start_erl and -boot options");
1173    if (config_script)
1174	error("Conflicting -start_erl and -config options");
1175    env = get_env("RELDIR");
1176    if (env)
1177	reldir = strsave(env);
1178    else {
1179	sprintf(tmpbuffer, "%s/releases", rootdir);
1180	reldir = strsave(tmpbuffer);
1181    }
1182    free_env_val(env);
1183    if (file == NULL)
1184       sprintf(start_erl_data, "%s/start_erl.data", reldir);
1185    else
1186       sprintf(start_erl_data, "%s", file);
1187    fp = _open(start_erl_data, _O_RDONLY );
1188    if( fp == -1 )
1189	error( "open failed on %s",start_erl_data );
1190    else {
1191	if( ( bytesread = _read( fp, tmpbuffer, 512 ) ) <= 0 )
1192	    error( "Problem reading file %s", start_erl_data );
1193	else {
1194	    tmpbuffer[bytesread]='\0';
1195	    if ((otpstring = strchr(tmpbuffer,' ')) != NULL) {
1196		*otpstring = '\0';
1197		otpstring++;
1198		
1199/*
1200 *   otpstring is the otpversion
1201 *   tmpbuffer is the emuversion
1202*/
1203	    }
1204	}
1205    }
1206    tprogname = otpstring;
1207    while (*tprogname) {
1208	if (*tprogname <= ' ') {
1209	    *tprogname='\0';
1210	    break;
1211	}
1212	tprogname++;
1213    }
1214	
1215    bindir = emalloc(512);
1216    sprintf(bindir,"%s/erts-%s/bin",rootdir,tmpbuffer);
1217    /* BINDIR=$ROOTDIR/erts-$ERTS_VSN/bin */
1218    tprogname = progname;
1219    progname = emalloc(strlen(tprogname) + 20);
1220    sprintf(progname,"%s -start_erl",tprogname);
1221
1222    boot_script = emalloc(512);
1223    config_script = emalloc(512);
1224    sprintf(boot_script, "%s/%s/start", reldir, otpstring);
1225    sprintf(config_script, "%s/%s/sys", reldir, otpstring);
1226       
1227}
1228
1229
1230static char *replace_filename(char *path, char *new_base) 
1231{
1232    int plen = strlen(path);
1233    char *res = malloc((plen+strlen(new_base)+1)*sizeof(char));
1234    char *p;
1235
1236    strcpy(res,path);
1237    for (p = res+plen-1 ;p >= res && *p != '\\'; --p)
1238        ;
1239    *(p+1) ='\0';
1240    strcat(res,new_base);
1241    return res;
1242}
1243
1244static char *path_massage(char *long_path)
1245{
1246     char *p;
1247
1248     p = malloc(MAX_PATH+1);
1249     strcpy(p, long_path);
1250     GetShortPathName(p, p, MAX_PATH);
1251     return p;
1252}
1253    
1254static char *do_lookup_in_section(InitSection *inis, char *name, 
1255				  char *section, char *filename, int is_path)
1256{
1257    char *p = lookup_init_entry(inis, name);
1258
1259    if (p == NULL) {
1260	error("Could not find key %s in section %s of file %s",
1261	      name,section,filename);
1262    }
1263
1264    if (is_path) {
1265	return path_massage(p);
1266    } else {
1267	return strsave(p);
1268    }
1269}
1270
1271
1272static void get_parameters(int argc, char** argv)
1273{
1274    char buffer[MAX_PATH];
1275    char *ini_filename;
1276    HANDLE module = GetModuleHandle(NULL); /* This might look strange, but we want the erl.ini 
1277					      that resides in the same dir as erl.exe, not 
1278					      an erl.ini in our directory */
1279    InitFile *inif;
1280    InitSection *inis;
1281
1282    if (module == NULL) {
1283        error("Cannot GetModuleHandle()");
1284    }
1285
1286    if (GetModuleFileName(module,buffer,MAX_PATH) == 0) {
1287        error("Could not GetModuleFileName");
1288    }
1289
1290    ini_filename = replace_filename(buffer,INI_FILENAME);
1291
1292    if ((inif = load_init_file(ini_filename)) == NULL) {
1293	error("Could not load init file %s",ini_filename);
1294    }
1295
1296    if ((inis = lookup_init_section(inif,INI_SECTION)) == NULL) {
1297	error("Could not find section %s in init file %s",
1298	      INI_SECTION, ini_filename);
1299    }
1300
1301    progname = do_lookup_in_section(inis, "Progname", INI_SECTION, 
1302				    ini_filename,0);
1303    bindir = do_lookup_in_section(inis, "Bindir", INI_SECTION, ini_filename,1);
1304    rootdir = do_lookup_in_section(inis, "Rootdir", INI_SECTION, 
1305				   ini_filename,1);
1306    emu = EMULATOR_EXECUTABLE;
1307    start_emulator_program = strsave(argv[0]);
1308
1309    free_init_file(inif);
1310    free(ini_filename);
1311}
1312
1313static void
1314get_home(void)
1315{
1316    int len;
1317    char tmpstr[MAX_PATH+1];
1318    char* homedrive;
1319    char* homepath;
1320
1321    homedrive = get_env("HOMEDRIVE");
1322    homepath = get_env("HOMEPATH");
1323    if (!homedrive || !homepath) {
1324	if (len = GetWindowsDirectory(tmpstr,MAX_PATH)) {
1325	    home = emalloc(len+1);
1326	    strcpy(home,tmpstr);
1327	} else
1328	    error("HOMEDRIVE or HOMEPATH is not set and GetWindowsDir failed");
1329    } else {
1330	home = emalloc(strlen(homedrive)+strlen(homepath)+1);
1331	strcpy(home, homedrive);
1332	strcat(home, homepath);
1333    }
1334    free_env_val(homedrive);
1335    free_env_val(homepath);
1336}
1337
1338#else
1339
1340static void
1341get_parameters(int argc, char** argv)
1342{
1343    progname = get_env("PROGNAME");
1344    bindir = get_env("BINDIR");
1345    rootdir = get_env("ROOTDIR");
1346    emu = get_env("EMU");
1347    if (!progname || !bindir || !rootdir || !emu ) {
1348	error("BINDIR, ROOTDIR, EMU and PROGNAME  must be set");
1349    }
1350}
1351
1352static void
1353get_home(void)
1354{
1355    home = get_env("HOME");
1356    if (home == NULL)
1357	error("HOME must be set");
1358}
1359
1360#endif
1361
1362
1363static char **build_args_from_env(char *env_var)
1364{
1365    char *value = get_env(env_var);
1366    char **res = build_args_from_string(value);
1367    free_env_val(value);
1368    return res;
1369}
1370
1371static char **build_args_from_string(char *string)
1372{
1373    int argc = 0;
1374    char **argv = NULL;
1375    int alloced = 0;
1376    char **cur_s = NULL;	/* Initialized to avoid warning. */
1377    int s_alloced = 0;
1378    int s_pos = 0;
1379    char *p = string;
1380    enum {Start, Build, Build0, BuildSQuoted, BuildDQuoted, AcceptNext} state;
1381
1382#define ENSURE()					\
1383    if (s_pos >= s_alloced) {			        \
1384	if (!*cur_s) {					\
1385	    *cur_s = emalloc(s_alloced = 20);		\
1386	} else {					\
1387	    *cur_s = erealloc(*cur_s, s_alloced += 20);	\
1388	}						\
1389    }
1390
1391
1392    if (!p)
1393	return NULL;
1394    argv = emalloc(sizeof(char *) * (alloced = 10));
1395    state = Start;
1396    for(;;) {
1397	switch (state) {
1398	case Start:
1399	    if (!*p) 
1400		goto done;
1401	    if (argc >= alloced - 1) { /* Make room for extra NULL */
1402		argv = erealloc(argv, (alloced += 10) * sizeof(char *));
1403	    }
1404	    cur_s = argc + argv;
1405	    *cur_s = NULL;
1406	    s_pos = 0;
1407	    s_alloced = 0;
1408	    state = Build0;
1409	    break;
1410	case Build0:
1411	    switch (*p) {
1412	    case ' ':
1413		++p;
1414		break;
1415	    case '\0':
1416		state = Start;
1417		break;
1418	    default:
1419		state = Build;
1420		break;
1421	    }
1422	    break;
1423	case Build:
1424	    switch (*p) {
1425	    case ' ':
1426	    case '\0':
1427		ENSURE();
1428		(*cur_s)[s_pos] = '\0';
1429		++argc;
1430		state = Start;
1431		break;
1432	    case '"':
1433		++p;
1434		state = BuildDQuoted;
1435		break;
1436	    case '\'':
1437		++p;
1438		state = BuildSQuoted;
1439		break;
1440	    case '\\':
1441		++p;
1442		state = AcceptNext;
1443		break;
1444	    default:
1445		ENSURE();
1446		(*cur_s)[s_pos++] = *p++;
1447		break;
1448	    }
1449	    break;
1450	case BuildDQuoted:
1451	    switch (*p) {
1452	    case '"':
1453		++p;
1454		/* fall through */
1455	    case '\0':
1456		state = Build;
1457		break;
1458	    default:
1459		ENSURE();
1460		(*cur_s)[s_pos++] = *p++;
1461		break;
1462	    }
1463	    break;
1464	case BuildSQuoted:
1465	    switch (*p) {
1466	    case '\'':
1467		++p;
1468		/* fall through */
1469	    case '\0':
1470		state = Build;
1471		break;
1472	    default:
1473		ENSURE();
1474		(*cur_s)[s_pos++] = *p++;
1475		break;
1476	    }
1477	    break;
1478	case AcceptNext:
1479	    if (!*p) {
1480		state = Build;
1481	    } else {
1482		ENSURE();
1483		(*cur_s)[s_pos++] = *p++;
1484	    }
1485	    state = Build;
1486	    break;
1487	}
1488    }
1489done:
1490    argv[argc] = NULL; /* Sure to be large enough */
1491    if (!argc) {
1492	efree(argv);
1493	return NULL;
1494    }
1495    return argv;
1496#undef ENSURE
1497}
1498
1499static char *
1500errno_string(void)
1501{
1502    char *str = strerror(errno);
1503    if (!str)
1504	return "unknown error";
1505    return str;
1506}
1507
1508static char **
1509read_args_file(char *filename)
1510{
1511    int c, aix = 0, quote = 0, cmnt = 0, asize = 0;
1512    char **res, *astr = NULL;
1513    FILE *file;
1514
1515#undef EAF_CMNT
1516#undef EAF_QUOTE
1517#undef SAVE_CHAR
1518
1519#define EAF_CMNT	(1 << 8)
1520#define EAF_QUOTE	(1 << 9)
1521#define SAVE_CHAR(C)						\
1522    do {							\
1523	if (!astr)						\
1524	    astr = emalloc(sizeof(char)*(asize = 20));		\
1525	if (aix == asize)					\
1526	    astr = erealloc(astr, sizeof(char)*(asize += 20));	\
1527	if (' ' != (char) (C))					\
1528	    astr[aix++] = (char) (C);				\
1529	else if (aix > 0 && astr[aix-1] != ' ')			\
1530	    astr[aix++] = ' ';					\
1531   } while (0)
1532
1533    do {
1534	errno = 0;
1535	file = fopen(filename, "r");
1536    } while (!file && errno == EINTR);
1537    if (!file) {
1538	usage_format("Failed to open arguments file \"%s\": %s\n",
1539		     filename,
1540		     errno_string());
1541    }
1542
1543    while (1) {
1544	c = getc(file);
1545	if (c == EOF) {
1546	    if (ferror(file)) {
1547		if (errno == EINTR) {
1548		    clearerr(file);
1549		    continue;
1550		}
1551		usage_format("Failed to read arguments file \"%s\": %s\n",
1552			     filename,
1553			     errno_string());
1554	    }
1555	    break;
1556	}
1557
1558	switch (quote | cmnt | c) {
1559	case '\\':
1560	    quote = EAF_QUOTE;
1561	    break;
1562	case '#':
1563	    cmnt = EAF_CMNT;
1564	    break;
1565	case EAF_CMNT|'\n':
1566	    cmnt = 0;
1567	    /* Fall through... */
1568	case '\n':
1569	case '\f':
1570	case '\r':
1571	case '\t':
1572	case '\v':
1573	    if (!quote)
1574		c = ' ';
1575	    /* Fall through... */
1576	default:
1577	    if (!cmnt)
1578		SAVE_CHAR(c);
1579	    quote = 0;
1580	    break;
1581	}
1582    }
1583
1584    SAVE_CHAR('\0');
1585
1586    fclose(file);
1587
1588    if (astr[0] == '\0')
1589	res = NULL;
1590    else
1591	res = build_args_from_string(astr);
1592
1593    efree(astr);
1594
1595    return res;
1596
1597#undef EAF_CMNT
1598#undef EAF_QUOTE
1599#undef SAVE_CHAR
1600}
1601
1602typedef struct {
1603    char **argv;
1604    int argc;
1605    int size;
1606} argv_buf;
1607
1608static void
1609trim_argv_buf(argv_buf *abp)
1610{
1611    abp->argv = erealloc(abp->argv, sizeof(char *)*(abp->size = abp->argc));
1612}
1613
1614static void
1615save_arg(argv_buf *abp, char *arg)
1616{
1617    if (abp->size <= abp->argc) {
1618	if (!abp->argv)
1619	    abp->argv = emalloc(sizeof(char *)*(abp->size = 100));
1620	else
1621	    abp->argv = erealloc(abp->argv, sizeof(char *)*(abp->size += 100));
1622    }
1623    abp->argv[abp->argc++] = arg;
1624}
1625
1626#define DEF_ARGV_STACK_SIZE 10
1627#define ARGV_STACK_SIZE_INCR 50
1628
1629typedef struct {
1630    char **argv;
1631    int ix;
1632} argv_stack_element;
1633
1634typedef struct {
1635    int top_ix;
1636    int size;
1637    argv_stack_element *base;
1638    argv_stack_element def_buf[DEF_ARGV_STACK_SIZE];
1639} argv_stack;
1640
1641#define ARGV_STACK_INIT(S)		\
1642do {					\
1643    (S)->top_ix = 0;			\
1644    (S)->size = DEF_ARGV_STACK_SIZE;	\
1645    (S)->base = &(S)->def_buf[0];	\
1646} while (0)
1647
1648static void
1649push_argv(argv_stack *stck, char **argv, int ix)
1650{
1651    if (stck->top_ix == stck->size) {
1652	if (stck->base != &stck->def_buf[0]) {
1653	    stck->size += ARGV_STACK_SIZE_INCR;
1654	    stck->base = erealloc(stck->base,
1655				  sizeof(argv_stack_element)*stck->size);
1656	}
1657	else {
1658	    argv_stack_element *base;
1659	    base = emalloc(sizeof(argv_stack_element)
1660			   *(stck->size + ARGV_STACK_SIZE_INCR));
1661	    memcpy((void *) base,
1662		   (void *) stck->base,
1663		   sizeof(argv_stack_element)*stck->size);
1664	    stck->base = base;
1665	    stck->size += ARGV_STACK_SIZE_INCR;
1666	}
1667    }
1668    stck->base[stck->top_ix].argv = argv;
1669    stck->base[stck->top_ix++].ix = ix;
1670}
1671
1672static void
1673pop_argv(argv_stack *stck, char ***argvp, int *ixp)
1674{
1675    if (stck->top_ix == 0) {
1676	*argvp = NULL;
1677	*ixp = 0;
1678    }
1679    else {
1680	*argvp = stck->base[--stck->top_ix].argv;
1681	*ixp = stck->base[stck->top_ix].ix;
1682	if (stck->top_ix == 0 && stck->base != &stck->def_buf[0]) {
1683	    efree(stck->base);
1684	    stck->base = &stck->def_buf[0];
1685	    stck->size = DEF_ARGV_STACK_SIZE;
1686	}
1687    }
1688}
1689
1690static void
1691get_file_args(char *filename, argv_buf *abp, argv_buf *xabp)
1692{
1693    argv_stack stck;
1694    int i;
1695    char **argv;
1696
1697    ARGV_STACK_INIT(&stck);
1698
1699    i = 0;
1700    argv = read_args_file(filename);
1701    
1702    while (argv) {
1703	
1704	while (argv[i]) {
1705	    if (strcmp(argv[i], "-args_file") == 0) {
1706		char **new_argv;
1707		char *fname;
1708		if (!argv[++i])
1709		    usage("-args_file");
1710		fname = argv[i++];
1711		new_argv = read_args_file(fname);
1712		if (new_argv) {
1713		    if (argv[i])
1714			push_argv(&stck, argv, i);
1715		    else
1716			efree(argv);
1717		    i = 0;
1718		    argv = new_argv;
1719		}
1720	    }
1721	    else {
1722		if (strcmp(argv[i], "-extra") == 0) {
1723		    i++;
1724		    while (argv[i])
1725			save_arg(xabp, argv[i++]);
1726		    break;
1727		}
1728		save_arg(abp, argv[i++]);
1729	    }
1730	}
1731
1732	efree(argv);
1733
1734	pop_argv(&stck, &argv, &i);
1735    }
1736}
1737
1738static void
1739initial_argv_massage(int *argc, char ***argv)
1740{
1741    argv_buf ab = {0}, xab = {0};
1742    int ix, vix, ac;
1743    char **av;
1744    struct {
1745	int argc;
1746	char **argv;
1747    } avv[] = {{INT_MAX, NULL}, {INT_MAX, NULL}, {INT_MAX, NULL},
1748	       {INT_MAX, NULL}, {INT_MAX, NULL}, {INT_MAX, NULL}};
1749    /*
1750     * The environment flag containing OTP release is intentionally
1751     * undocumented and intended for OTP internal use only.
1752     */
1753
1754    vix = 0;
1755    av = build_args_from_env("ERL_AFLAGS");
1756    if (av)
1757	avv[vix++].argv = av;
1758
1759    /* command line */
1760    if (*argc > 1) {
1761	avv[vix].argc = *argc - 1;
1762	avv[vix++].argv = &(*argv)[1];
1763    }
1764
1765    av = build_args_from_env("ERL_FLAGS");
1766    if (av)
1767	avv[vix++].argv = av;
1768
1769    av = build_args_from_env("ERL_" OTP_SYSTEM_VERSION "_FLAGS");
1770    if (av)
1771	avv[vix++].argv = av;
1772
1773    av = build_args_from_env("ERL_ZFLAGS");
1774    if (av)
1775	avv[vix++].argv = av;
1776
1777    if (vix == (*argc > 1 ? 1 : 0)) {
1778	/* Only command line argv; check if we can use argv as it is... */
1779	ac = *argc;
1780	av = *argv;
1781	for (ix = 1; ix < ac; ix++) {
1782	    if (strcmp(av[ix], "-args_file") == 0) {
1783		/* ... no; we need to expand arguments from
1784		   file into argument list */
1785		goto build_new_argv;
1786	    }
1787	    if (strcmp(av[ix], "-extra") == 0) {
1788		break;
1789	    }
1790	}
1791
1792	/* ... yes; we can use argv as it is. */
1793	return;
1794    }
1795
1796 build_new_argv:
1797
1798    save_arg(&ab, (*argv)[0]);
1799    
1800    vix = 0;
1801    while (avv[vix].argv) {
1802	ac = avv[vix].argc;
1803	av = avv[vix].argv;
1804
1805	ix = 0;
1806	while (ix < ac && av[ix]) {
1807	    if (strcmp(av[ix], "-args_file") == 0) {
1808		if (++ix == ac)
1809		    usage("-args_file");
1810		get_file_args(av[ix++], &ab, &xab);
1811	    }
1812	    else {
1813		if (strcmp(av[ix], "-extra") == 0) {
1814		    ix++;
1815		    while (ix < ac && av[ix])
1816			save_arg(&xab, av[ix++]);
1817		    break;
1818		}
1819		save_arg(&ab, av[ix++]);
1820	    }
1821	}
1822
1823	vix++;
1824    }
1825
1826    vix = 0;
1827    while (avv[vix].argv) {
1828	if (avv[vix].argc == INT_MAX) /* not command line */
1829	    efree(avv[vix].argv);
1830	vix++;
1831    }
1832
1833    if (xab.argc) {
1834	save_arg(&ab, "-extra");
1835	for (ix = 0; ix < xab.argc; ix++)
1836	    save_arg(&ab, xab.argv[ix]);
1837	efree(xab.argv);
1838    }
1839
1840    save_arg(&ab, NULL);
1841    trim_argv_buf(&ab);
1842    *argv = ab.argv;
1843    *argc = ab.argc - 1;
1844}
1845
1846#ifdef __WIN32__
1847static char*
1848possibly_quote(char* arg)
1849{
1850    int mustQuote = NO;
1851    int n = 0;
1852    char* s;
1853    char* narg;
1854
1855    /*
1856     * Scan the string to find out if it needs quoting and return
1857     * the original argument if not.
1858     */
1859
1860    for (s = arg; *s; s++, n++) {
1861	if (*s == ' ' || *s == '"') {
1862	    mustQuote = YES;
1863	    n++;
1864	}
1865    }
1866    if (!mustQuote) {
1867	return arg;
1868    }
1869
1870    /*
1871     * Insert the quotes and put a backslash in front of every quote
1872     * inside the string.
1873     */
1874
1875    s = narg = emalloc(n+2+1);
1876    for (*s++ = '"'; *arg; arg++, s++) {
1877	if (*s == '"') {
1878	    *s++ = '\\';
1879	}
1880	*s = *arg;
1881    }
1882    *s++ = '"';
1883    *s = '\0';
1884    return narg;
1885}
1886
1887#endif