1 /*
2  * %CopyrightBegin%
3  *
4  * Copyright Ericsson AB 1996-2021. All Rights Reserved.
5  *
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  *     http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  *
18  * %CopyrightEnd%
19  */
20 
21 #include "etc_common.h"
22 
23 #include "erl_driver.h"
24 #include "erl_misc_utils.h"
25 
26 #ifdef __WIN32__
27 #  include "erl_version.h"
28 #  include "init_file.h"
29 #  include <Shlobj.h>
30 #endif
31 
32 #define NO 0
33 #define YES 1
34 #define DEFAULT_PROGNAME "erl"
35 
36 #ifdef __WIN32__
37 #define INI_FILENAME L"erl.ini"
38 #define INI_SECTION "erlang"
39 #define DIRSEP "\\"
40 #define PATHSEP ";"
41 #define NULL_DEVICE "nul"
42 #define BINARY_EXT ""
43 #define DLL_EXT ".dll"
44 #define EMULATOR_EXECUTABLE "beam.dll"
45 #else
46 #define PATHSEP ":"
47 #define DIRSEP "/"
48 #define NULL_DEVICE "/dev/null"
49 #define BINARY_EXT ""
50 #define EMULATOR_EXECUTABLE "beam"
51 
52 #endif
53 #define QUOTE(s) s
54 
55 /* +M alloc_util allocators */
56 static const char plusM_au_allocs[]= {
57     'u',	/* all alloc_util allocators */
58     'B',	/* binary_alloc		*/
59     'I',	/* literal_alloc	*/
60     'D',	/* std_alloc		*/
61     'E',	/* ets_alloc		*/
62     'F',	/* fix_alloc		*/
63     'H',	/* eheap_alloc		*/
64     'L',	/* ll_alloc		*/
65     'R',	/* driver_alloc		*/
66     'S',	/* sl_alloc		*/
67     'T',	/* temp_alloc		*/
68     'Z',        /* test_alloc           */
69     '\0'
70 };
71 
72 /* +M alloc_util allocator specific arguments */
73 static char *plusM_au_alloc_switches[] = {
74     "as",
75     "asbcst",
76     "atags",
77     "acul",
78     "acnl",
79     "acfml",
80     "cp",
81     "e",
82     "t",
83     "lmbcs",
84     "mbcgs",
85     "mbsd",
86     "mmbcs",
87     "mmmbc",
88     "mmsbc",
89     "msbclt",
90     "ramv",
91     "rmbcmt",
92     "rsbcmt",
93     "rsbcst",
94     "sbct",
95     "smbcs",
96     NULL
97 };
98 
99 /* +M other arguments */
100 static char *plusM_other_switches[] = {
101     "ea",
102     "ummc",
103     "uycs",
104     "usac",
105     "im",
106     "is",
107     "it",
108     "lpm",
109     "Mamcbf",
110     "Mrmcbf",
111     "Mmcs",
112     "Mscs",
113     "Mscrfsd",
114     "Msco",
115     "Mscrpm",
116     "Ye",
117     "Ym",
118     "Ytp",
119     "Ytt",
120     "Iscs",
121     "Xscs",
122     NULL
123 };
124 
125 /* +s arguments with values */
126 static char *pluss_val_switches[] = {
127     "bt",
128     "bwtdcpu",
129     "bwtdio",
130     "bwt",
131     "cl",
132     "ct",
133     "ecio",
134     "fwi",
135     "tbt",
136     "wct",
137     "wtdcpu",
138     "wtdio",
139     "wt",
140     "ws",
141     "ss",
142     "ssdcpu",
143     "ssdio",
144     "pp",
145     "ub",
146     NULL
147 };
148 /* +h arguments with values */
149 static char *plush_val_switches[] = {
150     "ms",
151     "mbs",
152     "pds",
153     "max",
154     "maxk",
155     "maxel",
156     "mqd",
157     "",
158     NULL
159 };
160 
161 /* +r arguments with values */
162 static char *plusr_val_switches[] = {
163     "g",
164     NULL
165 };
166 
167 /* +z arguments with values */
168 static char *plusz_val_switches[] = {
169     "dbbl",
170     "dntgc",
171     "ebwt",
172     NULL
173 };
174 
175 
176 /*
177  * Define sleep(seconds) in terms of Sleep() on Windows.
178  */
179 
180 #ifdef __WIN32__
181 #define sleep(seconds) Sleep(seconds*1000)
182 #endif
183 
184 #define DEFAULT_SUFFIX	  "smp"
185 
186 void usage(const char *switchname);
187 static void usage_format(char *format, ...);
188 void start_epmd_daemon(char *epmd);
189 void error(char* format, ...);
190 
191 /*
192  * Local functions.
193  */
194 
195 static void usage_notsup(const char *switchname, const char *alt);
196 static char **build_args_from_env(char *env_var);
197 static char **build_args_from_string(char *env_var, int allow_comments);
198 static void initial_argv_massage(int *argc, char ***argv);
199 static void get_parameters(int argc, char** argv);
200 static void add_arg(char *new_arg);
201 static void add_args(char *first_arg, ...);
202 static void ensure_EargsSz(int sz);
203 static void add_Eargs(char *new_arg);
204 static void *emalloc(size_t size);
205 static void *erealloc(void *p, size_t size);
206 static void efree(void *p);
207 static char* strsave(char* string);
208 static int is_one_of_strings(char *str, char *strs[]);
209 static char *write_str(char *to, const char *from);
210 static void get_home(void);
211 static void add_epmd_port(void);
212 #ifdef __WIN32__
213 static void get_start_erl_data(char *);
214 static char* get_value(HKEY key, char* value_name, BOOL mustExit);
215 static char* possibly_quote(char* arg);
216 
217 /*
218  * Functions from win_erlexec.c
219  */
220 int start_win_emulator(char* emu, char *startprog,char** argv, int start_detached);
221 int start_emulator(char* emu, char*start_prog, char** argv, int start_detached);
222 #endif
223 
224 
225 
226 /*
227  * Variables.
228  */
229 int nohup = 0;
230 int keep_window = 0;
231 
232 static char **Eargsp = NULL;	/* Emulator arguments (to appear first). */
233 static int EargsSz = 0;		/* Size of Eargsp */
234 static int EargsCnt = 0;	/* Number of emulator arguments. */
235 static char **argsp = NULL;	/* Common arguments. */
236 static int argsCnt = 0;		/* Number of common arguments */
237 static int argsSz = 0;		/* Size of argsp */
238 static char tmpStr[10240];	/* Temporary string buffer. */
239 static int verbose = 0;		/* If non-zero, print some extra information. */
240 static int start_detached = 0;	/* If non-zero, the emulator should be
241 				 * started detached (in the background).
242 				 */
243 static const char* emu_type = NULL; /* Type of emulator (lcnt, valgrind, etc) */
244 static const char* emu_flavor = DEFAULT_SUFFIX; /* Flavor of emulator (smp, jit or emu) */
245 
246 #ifdef __WIN32__
247 static char *start_emulator_program = NULL; /* For detached mode -
248 					       erl.exe/werl.exe */
249 static char* key_val_name = ERLANG_VERSION; /* Used by the registry
250 					   * access functions.
251 					   */
252 static char* boot_script = NULL; /* used by option -start_erl and -boot */
253 static char** config_scripts = NULL; /* used by option -start_erl and -config */
254 static int config_script_cnt = 0;
255 static int got_start_erl = 0;
256 
257 static HANDLE this_module_handle;
258 static int run_werl;
259 static WCHAR *utf8_to_utf16(unsigned char *bytes);
260 static char *utf16_to_utf8(WCHAR *wstr);
261 static WCHAR *latin1_to_utf16(char *str);
262 #endif
263 
264 /*
265  * Parameters to be fetched from the environment (Unix)
266  * or the ini file (Win32).
267  */
268 
269 static char* bindir;		/* Location of executables. */
270 static char* rootdir;		/* Root location of Erlang installation. */
271 static char* emu;		/* Emulator to run. */
272 static char* progname;		/* Name of this program. */
273 static char* home;		/* Path of user's home directory, if any. */
274 
275 static void
set_env(char * key,char * value)276 set_env(char *key, char *value)
277 {
278 #ifdef __WIN32__
279     WCHAR *wkey = latin1_to_utf16(key);
280     WCHAR *wvalue = utf8_to_utf16(value);
281     if (!SetEnvironmentVariableW(wkey, wvalue))
282 	error("SetEnvironmentVariable(\"%s\", \"%s\") failed!", key, value);
283     efree(wkey);
284     efree(wvalue);
285 #else
286     size_t size = strlen(key) + 1 + strlen(value) + 1;
287     char *str = emalloc(size);
288     sprintf(str, "%s=%s", key, value);
289     if (putenv(str) != 0)
290 	error("putenv(\"%s\") failed!", str);
291 #ifdef HAVE_COPYING_PUTENV
292     efree(str);
293 #endif
294 #endif
295 }
296 
297 
298 static char *
get_env(char * key)299 get_env(char *key)
300 {
301 #ifdef __WIN32__
302     DWORD size = 32;
303     WCHAR *value = NULL;
304     WCHAR *wkey = latin1_to_utf16(key);
305     char *res;
306     while (1) {
307 	DWORD nsz;
308 	if (value)
309 	    efree(value);
310 	value = emalloc(size*sizeof(WCHAR));
311 	SetLastError(0);
312 	nsz = GetEnvironmentVariableW(wkey, value, size);
313 	if (nsz == 0 && GetLastError() == ERROR_ENVVAR_NOT_FOUND) {
314 	    efree(value);
315 	    efree(wkey);
316 	    return NULL;
317 	}
318 	if (nsz <= size) {
319 	    efree(wkey);
320 	    res = utf16_to_utf8(value);
321 	    efree(value);
322 	    return res;
323 	}
324 	size = nsz;
325     }
326 #else
327     return getenv(key);
328 #endif
329 }
330 
331 static void
free_env_val(char * value)332 free_env_val(char *value)
333 {
334 #ifdef __WIN32__
335     if (value)
336 	free(value);
337 #endif
338 }
339 
340 /*
341  * Add the type and architecture suffix to the program name if needed.
342  * On Windows, we insert it just before ".DLL".
343  */
344 static char*
add_extra_suffixes(char * prog)345 add_extra_suffixes(char *prog)
346 {
347    char *res;
348    char *p;
349    int len;
350 #ifdef __WIN32__
351    char *dll_p;
352    int dll = 0;
353 #endif
354 
355    len = strlen(prog);
356 
357    /* Allocate enough extra space for suffixes */
358    p = emalloc(len + 100);
359    res = p;
360    p = write_str(p, prog);
361 
362 #ifdef __WIN32__
363    dll_p = res + len - 4;
364    if (dll_p >= res) {
365       if (dll_p[0] == '.' &&
366 	  (dll_p[1] == 'd' || dll_p[1] == 'D') &&
367 	  (dll_p[2] == 'l' || dll_p[2] == 'L') &&
368 	  (dll_p[3] == 'l' || dll_p[3] == 'L')) {
369 	  p = dll_p;
370 	  dll = 1;
371       }
372    }
373 #endif
374 
375    if (emu_type && strcmp("opt",emu_type) != 0) {
376        p = write_str(p, ".");
377        p = write_str(p, emu_type);
378    }
379 
380    p = write_str(p, ".");
381    p = write_str(p, emu_flavor);
382 
383 #ifdef __WIN32__
384    if (dll) {
385        p = write_str(p, DLL_EXT);
386    }
387 #endif
388 
389    return res;
390 }
391 
392 #ifdef __WIN32__
add_boot_config(void)393 static void add_boot_config(void)
394 {
395     int i;
396     if (boot_script)
397 	add_args("-boot", boot_script, NULL);
398     for (i = 0; i < config_script_cnt; i++) {
399         add_args("-config", config_scripts[i], NULL);
400     }
401 }
402 # define ADD_BOOT_CONFIG add_boot_config()
403 #else
404 # define ADD_BOOT_CONFIG
405 #endif
406 
407 #define NEXT_ARG_CHECK_NAMED(Option) \
408     do {                                                                \
409         if (i+1 >= argc || strncmp(argv[i+1], "--", 3) == 0)            \
410             usage(Option);                                              \
411     } while(0)
412 
413 #define NEXT_ARG_CHECK() NEXT_ARG_CHECK_NAMED(argv[i])
414 
415 #ifdef __WIN32__
win_erlexec(int argc,char ** argv,HANDLE module,int windowed)416 __declspec(dllexport) int win_erlexec(int argc, char **argv, HANDLE module, int windowed)
417 #else
418 int main(int argc, char **argv)
419 #endif
420 {
421     int haltAfterwards = 0;	/* If true, put '-s erlang halt' at the end
422 				 * of the arguments. */
423     int isdistributed = 0;
424     int no_epmd = 0;
425     int proto_dist = 0;
426     int start_epmd = 1;         /* Whether epmd should be started */
427     int i;
428     char* s;
429     char *epmd_prog = NULL;
430     int process_args = 1;
431     int print_args_exit = 0;
432     int print_qouted_cmd_exit = 0;
433     char* emu_name;
434 
435 #ifdef __WIN32__
436     this_module_handle = module;
437     run_werl = windowed;
438     /* if we started this erl just to get a detached emulator,
439      * the arguments are already prepared for beam, so we skip
440      * directly to start_emulator */
441     s = get_env("ERL_CONSOLE_MODE");
442     if (s != NULL && strcmp(s, "detached")==0) {
443 	free_env_val(s);
444 	s = get_env("ERL_EMULATOR_DLL");
445 	if (s != NULL) {
446 	    argv[0] = strsave(s);
447 	} else {
448 	    argv[0] = strsave(EMULATOR_EXECUTABLE);
449 	}
450 	ensure_EargsSz(argc + 1);
451 	memcpy((void *) Eargsp, (void *) argv, argc * sizeof(char *));
452 	Eargsp[argc] = NULL;
453 	emu = argv[0];
454 	start_emulator_program = strsave(argv[0]);
455 	goto skip_arg_massage;
456     }
457     free_env_val(s);
458 #else
459     int reset_cerl_detached = 0;
460 
461     s = get_env("CERL_DETACHED_PROG");
462     if (s && strcmp(s, "") != 0) {
463 	emu = s;
464 	start_detached = 1;
465 	reset_cerl_detached = 1;
466 	ensure_EargsSz(argc + 1);
467 	memcpy((void *) Eargsp, (void *) argv, argc * sizeof(char *));
468 	Eargsp[argc] = emu;
469 	Eargsp[argc] = NULL;
470 	goto skip_arg_massage;
471     }
472     free_env_val(s);
473 #endif
474 
475     initial_argv_massage(&argc, &argv); /* Merge with env; expand -args_file */
476 
477     i = 1;
478 #ifdef __WIN32__
479     /* Not used? /rickard */
480     if ((argc > 2) && (strcmp(argv[i], "-regkey") == 0)) {
481 	key_val_name = strsave(argv[i+1]);
482 	i = 3;
483     }
484 #endif
485 
486     get_parameters(argc, argv);
487 
488     /*
489      * Construct the path of the executable.
490      */
491 #if defined(__WIN32__) && defined(WIN32_ALWAYS_DEBUG)
492     emu_type = "debug";
493 #endif
494 
495     /* We need to do this before the ordinary processing. */
496     while (i < argc) {
497 	if (argv[i][0] == '-') {
498 	    if (strcmp(argv[i], "-smp") == 0) {
499 		if (i + 1 >= argc)
500 		    goto smp;
501 
502 		if (strcmp(argv[i+1], "auto") == 0) {
503 		    i++;
504 		} else if (strcmp(argv[i+1], "enable") == 0) {
505 		    i++;
506 		smp_enable:
507                     ;
508 		} else if (strcmp(argv[i+1], "disable") == 0) {
509 		    i++;
510 		smp_disable:
511                     usage_notsup("-smp disable", " Use \"+S 1\" instead.");
512 		} else {
513 		smp:
514                     ;
515 		}
516 	    } else if (strcmp(argv[i], "-smpenable") == 0) {
517 		goto smp_enable;
518 	    } else if (strcmp(argv[i], "-smpauto") == 0) {
519                 ;
520 	    } else if (strcmp(argv[i], "-smpdisable") == 0) {
521 		goto smp_disable;
522 	    } else if (strcmp(argv[i], "-extra") == 0) {
523 		break;
524 	    } else if (strcmp(argv[i], "-emu_type") == 0) {
525                 NEXT_ARG_CHECK();
526                 emu_type = argv[i+1];
527                 i++;
528 	    } else if (strcmp(argv[i], "-emu_flavor") == 0) {
529                 NEXT_ARG_CHECK();
530                 emu_flavor = argv[i+1];
531                 i++;
532 	    }
533 	}
534 	i++;
535     }
536 
537     emu = add_extra_suffixes(emu);
538     emu_name = strsave(emu);
539     erts_snprintf(tmpStr, sizeof(tmpStr), "%s" DIRSEP "%s" BINARY_EXT, bindir, emu);
540     emu = strsave(tmpStr);
541 
542     s = get_env("ESCRIPT_NAME");
543     if(s) {
544         add_Eargs(s);         /* argv[0] = scriptname*/
545     } else {
546         add_Eargs(emu);       /* argv[0] = erl or cerl */
547     }
548 
549     /* Add the bindir to the front of the PATH, and remove all subsequent
550      * occurrences to avoid ballooning it on repeated up/downgrades. */
551 
552     s = get_env("PATH");
553 
554     if (s == NULL) {
555         erts_snprintf(tmpStr, sizeof(tmpStr),
556             "%s" PATHSEP "%s" DIRSEP "bin" PATHSEP, bindir, rootdir);
557     } else if (strstr(s, rootdir) == NULL) {
558         erts_snprintf(tmpStr, sizeof(tmpStr),
559             "%s" PATHSEP "%s" DIRSEP "bin" PATHSEP "%s", bindir, rootdir, s);
560     } else {
561         const char *bindir_slug, *bindir_slug_index;
562         int bindir_slug_length;
563         const char *in_index;
564         char *out_index;
565 
566         erts_snprintf(tmpStr, sizeof(tmpStr), "%s" PATHSEP, bindir);
567 
568         bindir_slug = strsave(tmpStr);
569         bindir_slug_length = strlen(bindir_slug);
570 
571         out_index = &tmpStr[bindir_slug_length];
572         in_index = s;
573 
574         while ((bindir_slug_index = strstr(in_index, bindir_slug))) {
575             int block_length = (bindir_slug_index - in_index);
576 
577             memcpy(out_index, in_index, block_length);
578 
579             in_index = bindir_slug_index + bindir_slug_length;
580             out_index += block_length;
581         }
582         efree((void*)bindir_slug);
583         strcpy(out_index, in_index);
584     }
585 
586     free_env_val(s);
587     set_env("PATH", tmpStr);
588 
589     i = 1;
590 
591     get_home();
592     /* Add the home parameter when available. This is optional to support
593        systems that don't have the notion of a home directory and setups
594        that don't have the HOME environment variable set (ERL-476). */
595     if (home != NULL) {
596         add_args("-home", home, NULL);
597     }
598 
599     add_epmd_port();
600 
601     add_arg("--");
602 
603     while (i < argc) {
604 	if (!process_args) {	/* Copy arguments after '-extra' */
605 	    add_arg(argv[i]);
606 	    i++;
607 	} else {
608 	    switch (argv[i][0]) {
609 	      case '-':
610 		switch (argv[i][1]) {
611 #ifdef __WIN32__
612 		case 'b':
613 		    if (strcmp(argv[i], "-boot") == 0) {
614 			if (boot_script)
615 			    error("Conflicting -boot options");
616                         if (got_start_erl)
617                             error("Conflicting -start_erl and -boot options");
618                         NEXT_ARG_CHECK();
619 			boot_script = strsave(argv[i+1]);
620 			i++;
621 		    }
622 		    else {
623 			add_arg(argv[i]);
624 		    }
625 		    break;
626 #endif
627 		case 'c':
628 		    if (strcmp(argv[i], "-compile") == 0) {
629 			/*
630 			 * Note that the shell script erl.exec does a recursive call
631 			 * on itself here.  We'll avoid doing that.
632 			 */
633 			add_args("-noshell", "-noinput", "-s", "c", "lc_batch",
634 				 NULL);
635 			add_Eargs("-B");
636 			haltAfterwards = 0;
637 		    }
638 #ifdef __WIN32__
639 		    else if (strcmp(argv[i], "-config") == 0){
640 			if (got_start_erl)
641 			    error("Conflicting -start_erl and -config options");
642                         NEXT_ARG_CHECK();
643                         do {
644                             config_script_cnt++;
645                             config_scripts = erealloc(config_scripts,
646                                                       config_script_cnt * sizeof(char*));
647                             config_scripts[config_script_cnt-1] = strsave(argv[i+1]);
648                             i++;
649                         } while ((i+1) < argc && argv[i+1][0] != '-' && argv[i+1][0] != '+');
650 		    }
651 #endif
652                     else if (strcmp(argv[i], "-configfd") == 0) {
653                         NEXT_ARG_CHECK();
654                         if ( strcmp(argv[i+1], "0") != 0 ) {
655 			    add_arg(argv[i]);
656                         } else {
657                             add_args("-noshell", "-noinput", NULL);
658                             add_arg(argv[i]);
659                         }
660 		    }
661 		    else {
662 			add_arg(argv[i]);
663 		    }
664 		    break;
665 
666 		  case 'd':
667 		    if (strcmp(argv[i], "-detached") != 0) {
668 			add_arg(argv[i]);
669 		    } else {
670 			start_detached = 1;
671 			add_args("-noshell", "-noinput", NULL);
672 		    }
673 		    break;
674 
675 		  case 'e':
676 		    if (strcmp(argv[i], "-extra") == 0) {
677 			process_args = 0;
678 			ADD_BOOT_CONFIG;
679 			add_arg(argv[i]);
680 		    } else if (strcmp(argv[i], "-emu_args") == 0) { /* -emu_args */
681 			verbose = 1;
682 		    } else if (strcmp(argv[i], "-emu_args_exit") == 0) {
683 			print_args_exit = 1;
684 		    } else if (strcmp(argv[i], "-emu_name_exit") == 0) {
685 			printf("%s\n", emu_name);
686 			exit(0);
687 		    } else if (strcmp(argv[i], "-emu_qouted_cmd_exit") == 0) {
688 			print_qouted_cmd_exit = 1;
689 		    } else if (strcmp(argv[i], "-env") == 0) { /* -env VARNAME VARVALUE */
690                         NEXT_ARG_CHECK();
691                         i += 1;
692                         NEXT_ARG_CHECK_NAMED("-env");
693 			set_env(argv[i], argv[i+1]);
694 			i += 1;
695 		    } else if (strcmp(argv[i], "-epmd") == 0) {
696                         NEXT_ARG_CHECK();
697 			epmd_prog = argv[i+1];
698 			++i;
699 		    } else {
700 			add_arg(argv[i]);
701 		    }
702 		    break;
703 		  case 'k':
704 		    if (strcmp(argv[i], "-keep_window") == 0) {
705 			keep_window = 1;
706 		    } else
707 			add_arg(argv[i]);
708 		    break;
709 
710 		  case 'm':
711 		    /*
712 		     * Note that the shell script erl.exec does a recursive call
713 		     * on itself here.  We'll avoid doing that.
714 		     */
715 		    if (strcmp(argv[i], "-make") == 0) {
716 			add_args("-noshell", "-noinput", "-s", "make", "all_or_nothing", NULL);
717 			add_Eargs("-B");
718 			haltAfterwards = 1;
719 			i = argc; /* Skip rest of command line */
720 		    } else if (strcmp(argv[i], "-man") == 0) {
721 #if defined(__WIN32__)
722 			error("-man not supported on Windows");
723 #else
724 			argv[i] = "man";
725 			erts_snprintf(tmpStr, sizeof(tmpStr), "%s/man", rootdir);
726 			set_env("MANPATH", tmpStr);
727 			execvp("man", argv+i);
728 			error("Could not execute the 'man' command.");
729 #endif
730 		    } else
731 			add_arg(argv[i]);
732 		    break;
733 
734 		  case 'n':
735 		    if (strcmp(argv[i], "-name") == 0) { /* -name NAME */
736                         NEXT_ARG_CHECK();
737 
738 			/*
739 			 * Note: Cannot use add_args() here, due to non-defined
740 			 * evaluation order.
741 			 */
742 
743 			add_arg(argv[i]);
744 			add_arg(argv[i+1]);
745 			isdistributed = 1;
746 			i++;
747 		    } else if (strcmp(argv[i], "-noinput") == 0) {
748 			add_args("-noshell", "-noinput", NULL);
749 		    } else if (strcmp(argv[i], "-nohup") == 0) {
750 			add_arg("-nohup");
751 			nohup = 1;
752 		    } else if (strcmp(argv[i], "-no_epmd") == 0) {
753 			add_arg("-no_epmd");
754 			no_epmd = 1;
755 			start_epmd = 0;
756 		    } else {
757 			add_arg(argv[i]);
758 		    }
759 		    break;
760 
761 		  case 'p':
762 		    if (strcmp(argv[i], "-proto_dist") == 0) {
763 			NEXT_ARG_CHECK();
764 			add_arg(argv[i]);
765 			add_arg(argv[i+1]);
766 			proto_dist = 1;
767 			i++;
768 		    } else {
769 			add_arg(argv[i]);
770 		    }
771 		    break;
772 
773 		  case 's':	/* -sname NAME */
774 		    if (strcmp(argv[i], "-sname") == 0) {
775                         NEXT_ARG_CHECK();
776 			add_arg(argv[i]);
777 			add_arg(argv[i+1]);
778 			isdistributed = 1;
779 			i++;
780 		    }
781 #ifdef __WIN32__
782 		    else if (strcmp(argv[i], "-service_event") == 0) {
783                         NEXT_ARG_CHECK();
784 			add_arg(argv[i]);
785 			add_arg(argv[i+1]);
786 			i++;
787 		    }
788 		    else if (strcmp(argv[i], "-start_erl") == 0) {
789 			if (i+1 < argc && argv[i+1][0] != '-') {
790 			    get_start_erl_data(argv[i+1]);
791 			    i++;
792 			} else
793 			    get_start_erl_data((char *) NULL);
794 		    }
795 #endif
796 		    else if (strcmp(argv[i], "-start_epmd") == 0) {
797                         NEXT_ARG_CHECK();
798 
799 			if (strcmp(argv[i+1], "true") == 0) {
800 			    /* The default */
801 			    start_epmd = 1;
802 			}
803 			else if (strcmp(argv[i+1], "false") == 0) {
804 			    start_epmd = 0;
805 			}
806 			else
807 			    usage_format("Expected boolean argument for \'-start_epmd\'.\n");
808 
809 			add_arg(argv[i]);
810 			add_arg(argv[i+1]);
811 			i++;
812 		    }
813 		    else
814 			add_arg(argv[i]);
815 
816 		    break;
817 
818 		  case 'v':	/* -version */
819 		    if (strcmp(argv[i], "-version") == 0) {
820 			add_Eargs("-V");
821 		    } else {
822 			add_arg(argv[i]);
823 		    }
824 		    break;
825 
826 		  default:
827 		    add_arg(argv[i]);
828 		    break;
829 		} /* switch(argv[i][1] */
830 		break;
831 
832 	      case '+':
833 		switch (argv[i][1]) {
834 		  case 'a':
835 		  case 'A':
836 		  case 'C':
837 		  case 'e':
838 		  case 'i':
839 		  case 'n':
840 		  case 'P':
841 		  case 'Q':
842 		  case 't':
843 		  case 'T':
844 		  case 'R':
845 		  case 'W':
846 		  case 'K':
847 		      if (argv[i][2] != '\0')
848 			  goto the_default;
849                       NEXT_ARG_CHECK();
850 		      argv[i][0] = '-';
851 		      add_Eargs(argv[i]);
852 		      add_Eargs(argv[i+1]);
853 		      i++;
854 		      break;
855 		  case 'I':
856                       if (argv[i][2] == 'O' && (argv[i][3] == 't' || argv[i][3] == 'p')) {
857                           if (argv[i][4] != '\0')
858                               goto the_default;
859                           NEXT_ARG_CHECK();
860                           argv[i][0] = '-';
861                           add_Eargs(argv[i]);
862                           add_Eargs(argv[i+1]);
863                           i++;
864                           break;
865                       }
866                       if (argv[i][2] == 'O' && argv[i][3] == 'P' &&
867                           (argv[i][4] == 't' || argv[i][4] == 'p')) {
868                           if (argv[i][5] != '\0')
869                               goto the_default;
870                           NEXT_ARG_CHECK();
871                           argv[i][0] = '-';
872                           add_Eargs(argv[i]);
873                           add_Eargs(argv[i+1]);
874                           i++;
875                           break;
876                       }
877                       usage(argv[i]);
878                       break;
879                   case 'J':
880                       NEXT_ARG_CHECK();
881                       argv[i][0] = '-';
882                       add_Eargs(argv[i]);
883                       add_Eargs(argv[i+1]);
884                       i++;
885                       break;
886 		  case 'S':
887 		      if (argv[i][2] == 'P') {
888 			  if (argv[i][3] != '\0')
889 			      goto the_default;
890 		      }
891 		      else if (argv[i][2] == 'D') {
892 			  char* type = argv[i]+3;
893 			  if (strncmp(type, "cpu", 3) != 0 &&
894 			      strncmp(type, "Pcpu", 4) != 0 &&
895 			      strncmp(type, "io", 2) != 0)
896 			      usage(argv[i]);
897 			  if ((argv[i][3] == 'c' && argv[i][6] != '\0') ||
898 			      (argv[i][3] == 'P' && argv[i][7] != '\0') ||
899 			      (argv[i][3] == 'i' && argv[i][5] != '\0'))
900 			      goto the_default;
901 		      }
902 		      else if (argv[i][2] != '\0')
903 			  goto the_default;
904                       NEXT_ARG_CHECK();
905 		      argv[i][0] = '-';
906 		      add_Eargs(argv[i]);
907 		      add_Eargs(argv[i+1]);
908 		      i++;
909 		      break;
910 		  case 'B':
911 		      argv[i][0] = '-';
912 		      if (argv[i][2] != '\0') {
913 			  if ((argv[i][2] != 'i') &&
914 			      (argv[i][2] != 'c') &&
915 			      (argv[i][2] != 'd')) {
916 			  usage(argv[i]);
917 			} else {
918 			  add_Eargs(argv[i]);
919 			  break;
920 			}
921 		      }
922 		      if (i+1 < argc) {
923                           if (argv[i+1][1] == '\0') {
924                               if ((argv[i+1][0] == 'i') ||
925                                   (argv[i+1][0] == 'c') ||
926                                   (argv[i+1][0] == 'd')
927                                   ) {
928                                   add_Eargs(argv[i]);
929                                   add_Eargs(argv[i+1]);
930                                   i++;
931                                   break;
932                               }
933 			  }
934 		      }
935 		      add_Eargs(argv[i]);
936 		      break;
937 		  case 'c':
938 		      argv[i][0] = '-';
939 		      if (argv[i][2] == '\0' && i+1 < argc) {
940 			  if (strcmp(argv[i+1], "true") == 0
941 			      || strcmp(argv[i+1], "false") == 0) {
942 			      add_Eargs(argv[i]);
943 			      add_Eargs(argv[i+1]);
944 			      i++;
945 			      break;
946 			  }
947 		      }
948 		      add_Eargs(argv[i]);
949 		      break;
950 		  case 'M': {
951 		      int x;
952 		      for (x = 0; plusM_au_allocs[x]; x++)
953 			  if (plusM_au_allocs[x] == argv[i][2])
954 			      break;
955 		      if ((plusM_au_allocs[x]
956 			   && is_one_of_strings(&argv[i][3],
957 						plusM_au_alloc_switches))
958 			  || is_one_of_strings(&argv[i][2],
959 					       plusM_other_switches)) {
960                           NEXT_ARG_CHECK();
961 			  argv[i][0] = '-';
962 			  add_Eargs(argv[i]);
963 			  add_Eargs(argv[i+1]);
964 			  i++;
965 		      }
966 		      else
967 			  goto the_default;
968 		      break;
969 		  }
970 		  case 'h':
971 		      if (!is_one_of_strings(&argv[i][2], plush_val_switches)) {
972 			  goto the_default;
973 		      } else {
974                           NEXT_ARG_CHECK();
975 			  argv[i][0] = '-';
976 			  add_Eargs(argv[i]);
977 			  add_Eargs(argv[i+1]);
978 			  i++;
979 		      }
980 		      break;
981 		  case 'r':
982 		      if (!is_one_of_strings(&argv[i][2],
983 					     plusr_val_switches))
984 			  goto the_default;
985 		      else {
986                           NEXT_ARG_CHECK();
987 			  argv[i][0] = '-';
988 			  add_Eargs(argv[i]);
989 			  add_Eargs(argv[i+1]);
990 			  i++;
991 		      }
992 		      break;
993 		  case 's':
994 		      if (!is_one_of_strings(&argv[i][2],
995 					     pluss_val_switches))
996 			  goto the_default;
997 		      else {
998                           NEXT_ARG_CHECK();
999 			  argv[i][0] = '-';
1000 			  add_Eargs(argv[i]);
1001 			  add_Eargs(argv[i+1]);
1002 			  i++;
1003 		      }
1004 		      break;
1005 		  case 'p':
1006 		      if (argv[i][2] != 'c' || argv[i][3] != '\0')
1007 			  goto the_default;
1008                       NEXT_ARG_CHECK();
1009 		      argv[i][0] = '-';
1010 		      add_Eargs(argv[i]);
1011 		      add_Eargs(argv[i+1]);
1012 		      i++;
1013 		      break;
1014 		  case 'z':
1015 		      if (!is_one_of_strings(&argv[i][2], plusz_val_switches)) {
1016 			  goto the_default;
1017 		      } else {
1018                           NEXT_ARG_CHECK();
1019 			  argv[i][0] = '-';
1020 			  add_Eargs(argv[i]);
1021 			  add_Eargs(argv[i+1]);
1022 			  i++;
1023 		      }
1024 		      break;
1025 		  default:
1026 		  the_default:
1027 		    argv[i][0] = '-'; /* Change +option to -option. */
1028 		    add_Eargs(argv[i]);
1029 		}
1030 		break;
1031 
1032 	      default:
1033 		add_arg(argv[i]);
1034 	    } /* switch(argv[i][0] */
1035 	    i++;
1036 	}
1037     }
1038 
1039     efree(emu_name);
1040 
1041     if (process_args) {
1042 	ADD_BOOT_CONFIG;
1043     }
1044 #undef ADD_BOOT_CONFIG
1045 
1046     /* The default distribution protocol (inet_tcp) relies on epmd,
1047        so the -no_epmd option can only work when using an alternative
1048        protocol for Erlang distribution. */
1049     if (no_epmd && !proto_dist) {
1050         error("Missing -proto_dist option, expected when using -no_epmd.");
1051     }
1052 
1053     /* Doesn't conflict with -extra, since -make skips all the rest of
1054        the arguments. */
1055     if (haltAfterwards) {
1056 	add_args("-s", "erlang", "halt", NULL);
1057     }
1058 
1059     if (isdistributed && start_epmd)
1060 	start_epmd_daemon(epmd_prog);
1061 
1062 #if (! defined(__WIN32__)) && defined(DEBUG)
1063     if (start_detached && get_env("ERL_CONSOLE_MODE")) {
1064 	/* Start the emulator within an xterm.
1065 	 * Move up all arguments and insert
1066 	 * "xterm -e " first.
1067 	 * The path must be searched for this
1068 	 * to work, i.e execvp() must be used.
1069 	 */
1070 	ensure_EargsSz(EargsCnt+2);
1071 	for (i = EargsCnt; i > 0; i--)
1072 	    Eargsp[i+1] = Eargsp[i-1]; /* Two args to insert */
1073 	EargsCnt += 2; /* Two args to insert */
1074 	Eargsp[0] = emu = "xterm";
1075 	Eargsp[1] = "-e";
1076     }
1077 #endif
1078 
1079     add_Eargs("--");
1080     add_Eargs("-root");
1081     add_Eargs(rootdir);
1082     add_Eargs("-progname");
1083     add_Eargs(progname);
1084     add_Eargs("--");
1085     ensure_EargsSz(EargsCnt + argsCnt + 1);
1086     for (i = 0; i < argsCnt; i++)
1087 	Eargsp[EargsCnt++] = argsp[i];
1088     Eargsp[EargsCnt] = NULL;
1089 
1090     if (print_qouted_cmd_exit) {
1091 	printf("\"%s\" ", emu);
1092 	for (i = 1; i < EargsCnt; i++)
1093 	    printf("\"%s\" ", Eargsp[i]);
1094 	printf("\n");
1095 	exit(0);
1096     }
1097 
1098     if (print_args_exit) {
1099 	for (i = 1; i < EargsCnt; i++)
1100 	    printf("%s\n", Eargsp[i]);
1101 	exit(0);
1102     }
1103 
1104     if (verbose) {
1105 	printf("Executing: %s", emu);
1106 	for (i = 0; i < EargsCnt; i++)
1107 	    printf(" %s", Eargsp[i]);
1108 	printf("\n\n");
1109     }
1110 
1111 #ifdef __WIN32__
1112 
1113     if (EargsSz != EargsCnt + 1)
1114 	Eargsp = (char **) erealloc((void *) Eargsp, (EargsCnt + 1) *
1115 				    sizeof(char *));
1116     efree((void *) argsp);
1117 
1118  skip_arg_massage:
1119     /*DebugBreak();*/
1120 
1121     if (run_werl) {
1122 	if (start_detached) {
1123 	    char *p;
1124 	    /* transform werl to erl */
1125 	    p = start_emulator_program+strlen(start_emulator_program);
1126 	    while (--p >= start_emulator_program && *p != '/' && *p != '\\' &&
1127 		   *p != 'W' && *p != 'w')
1128 		;
1129 	    if (p >= start_emulator_program && (*p == 'W' || *p == 'w') &&
1130 		(p[1] == 'E' || p[1] == 'e') && (p[2] == 'R' || p[2] == 'r') &&
1131 		(p[3] == 'L' || p[3] == 'l')) {
1132 		memmove(p,p+1,strlen(p));
1133 	    }
1134 	}
1135       return start_win_emulator(emu, start_emulator_program, Eargsp, start_detached);
1136     } else {
1137       return start_emulator(emu, start_emulator_program, Eargsp, start_detached);
1138     }
1139 
1140 #else
1141 
1142  skip_arg_massage:
1143     if (start_detached) {
1144 	int status = fork();
1145 	if (status != 0)	/* Parent */
1146 	    return 0;
1147 
1148 	if (reset_cerl_detached)
1149 	    putenv("CERL_DETACHED_PROG=");
1150 
1151 	/* Detach from controlling terminal */
1152 #ifdef HAVE_SETSID
1153 	setsid();
1154 #elif defined(TIOCNOTTY)
1155 	{
1156 	  int fd = open("/dev/tty", O_RDWR);
1157 	  if (fd >= 0) {
1158 	    ioctl(fd, TIOCNOTTY, NULL);
1159 	    close(fd);
1160 	  }
1161 	}
1162 #endif
1163 
1164 	status = fork();
1165 	if (status != 0)	/* Parent */
1166 	    return 0;
1167 
1168 	/*
1169 	 * Grandchild.
1170 	 */
1171 	close(0);
1172 	open("/dev/null", O_RDONLY);
1173 	close(1);
1174 	open("/dev/null", O_WRONLY);
1175 	close(2);
1176 	open("/dev/null", O_WRONLY);
1177 #ifdef DEBUG
1178 	execvp(emu, Eargsp); /* "xterm ..." needs to search the path */
1179 #endif
1180     }
1181 #ifdef DEBUG
1182     else
1183 #endif
1184     {
1185 	execv(emu, Eargsp);
1186     }
1187     if (errno == ENOENT) {
1188         if (strcmp(emu_flavor,DEFAULT_SUFFIX) || emu_type) {
1189             /* The executable did not exist and a flavor/type flags was given.
1190              * We collect the possible combinations and print that in the error
1191              * in order to help the user.
1192              */
1193             char buff[255], *currbuff = buff;
1194             DIR *dp = opendir(bindir);
1195             if (dp) {
1196                 struct dirent *ep;
1197                 while ((ep = readdir(dp)) != NULL) {
1198                     if (strncmp("beam",ep->d_name,4) == 0) {
1199                         char *type = strstr(ep->d_name,".") + 1;
1200                         char *flavor = strstr(type,".");
1201                         currbuff += sprintf(currbuff,"\n  ");
1202                         if (flavor == NULL) {
1203                             flavor = type;
1204                         } else {
1205                             char* emu_type = strndup(type,flavor - type);
1206                             currbuff += sprintf(currbuff,"-emu_type %s ", emu_type);
1207                             free(emu_type);
1208                             flavor++;
1209                         }
1210                         currbuff += sprintf(currbuff,"-emu_flavor %s", flavor);
1211                     }
1212                 }
1213                 closedir(dp);
1214             }
1215             error("Invalid emulator type or flavor. Available combinations are: %s\n",buff);
1216         } else {
1217             error("The emulator \'%s\' does not exist.");
1218         }
1219     } else {
1220         error("Error %d executing \'%s\'.", errno, emu);
1221     }
1222     return 1;
1223 #endif
1224 }
1225 
1226 
1227 static void
usage_aux(void)1228 usage_aux(void)
1229 {
1230   fprintf(stderr,
1231 	  "Usage: erl [-version] [-sname NAME | -name NAME] "
1232 	  "[-noshell] [-noinput] [-env VAR VALUE] [-compile file ...] "
1233 #ifdef __WIN32__
1234 	  "[-start_erl [datafile]] "
1235 #endif
1236 	  "[-make] [-man [manopts] MANPAGE] [-x] [-emu_args] [-start_epmd BOOLEAN] "
1237           "[-emu_type TYPE] [-emu_flavor FLAVOR] "
1238 	  "[-args_file FILENAME] [+A THREADS] [+a SIZE] [+B[c|d|i]] [+c [BOOLEAN]] "
1239 	  "[+C MODE] [+dcg DECENTRALIZED_COUNTER_GROUPS_LIMIT] [+h HEAP_SIZE_OPTION] "
1240           "[+J[Pperf] JIT_OPTION] "
1241 	  "[+M<SUBSWITCH> <ARGUMENT>] [+P MAX_PROCS] [+Q MAX_PORTS] "
1242 	  "[+R COMPAT_REL] "
1243 	  "[+r] [+rg READER_GROUPS_LIMIT] [+s<SUBSWITCH> SCHEDULER_OPTION] "
1244 	  "[+S NO_SCHEDULERS:NO_SCHEDULERS_ONLINE] "
1245 	  "[+SP PERCENTAGE_SCHEDULERS:PERCENTAGE_SCHEDULERS_ONLINE] "
1246 	  "[+T LEVEL] [+V] [+v] "
1247 	  "[+W<i|w|e>] [+z MISC_OPTION] [args ...]\n");
1248   exit(1);
1249 }
1250 
1251 void
usage(const char * switchname)1252 usage(const char *switchname)
1253 {
1254     fprintf(stderr, "Missing argument(s) for \'%s\'.\n", switchname);
1255     usage_aux();
1256 }
1257 
1258 static void
usage_notsup(const char * switchname,const char * alt)1259 usage_notsup(const char *switchname, const char *alt)
1260 {
1261     fprintf(stderr, "Argument \'%s\' not supported.%s\n", switchname, alt);
1262     usage_aux();
1263 }
1264 
1265 static void
usage_format(char * format,...)1266 usage_format(char *format, ...)
1267 {
1268     va_list args;
1269     va_start(args, format);
1270     vfprintf(stderr, format, args);
1271     va_end(args);
1272     usage_aux();
1273 }
1274 
1275 void
start_epmd_daemon(char * epmd)1276 start_epmd_daemon(char *epmd)
1277 {
1278     char  epmd_cmd[MAXPATHLEN+100];
1279 #ifdef __WIN32__
1280     char* arg1 = NULL;
1281 #endif
1282     int   result;
1283 
1284     if (!epmd) {
1285 	epmd = epmd_cmd;
1286 #ifdef __WIN32__
1287 	erts_snprintf(epmd_cmd, sizeof(epmd_cmd), "\"%s" DIRSEP "epmd\"", bindir);
1288 	arg1 = "-daemon";
1289 #else
1290 	erts_snprintf(epmd_cmd, sizeof(epmd_cmd), "\"%s" DIRSEP "epmd\" -daemon", bindir);
1291 #endif
1292     }
1293 #ifdef __WIN32__
1294     if (arg1 != NULL) {
1295 	strcat(epmd, " ");
1296 	strcat(epmd, arg1);
1297     }
1298     {
1299 	wchar_t wcepmd[MAXPATHLEN+100];
1300 	STARTUPINFOW start;
1301 	PROCESS_INFORMATION pi;
1302 	memset(&start, 0, sizeof (start));
1303 	start.cb = sizeof (start);
1304 	MultiByteToWideChar(CP_UTF8, 0, epmd, -1, wcepmd, MAXPATHLEN+100);
1305 
1306 	if (!CreateProcessW(NULL, wcepmd, NULL, NULL, FALSE,
1307 			       CREATE_DEFAULT_ERROR_MODE | DETACHED_PROCESS,
1308 			       NULL, NULL, &start, &pi))
1309 	    result = -1;
1310 	else
1311 	    result = 0;
1312     }
1313 #else
1314     result = system(epmd);
1315 #endif
1316     if (result == -1) {
1317       fprintf(stderr, "Error spawning %s (error %d)\n", epmd_cmd,errno);
1318       exit(1);
1319     }
1320 }
1321 
1322 static void
add_arg(char * new_arg)1323 add_arg(char *new_arg)
1324 {
1325     if (argsCnt >= argsSz)
1326 	argsp = (char **) erealloc((void *) argsp,
1327 				   sizeof(char *) * (argsSz += 20));
1328     argsp[argsCnt++] = QUOTE(new_arg);
1329 }
1330 
1331 static void
add_args(char * first_arg,...)1332 add_args(char *first_arg, ...)
1333 {
1334     va_list ap;
1335     char* arg;
1336 
1337     add_arg(first_arg);
1338     va_start(ap, first_arg);
1339     while ((arg = va_arg(ap, char *)) != NULL) {
1340 	add_arg(arg);
1341     }
1342     va_end(ap);
1343 }
1344 
1345 static void
ensure_EargsSz(int sz)1346 ensure_EargsSz(int sz)
1347 {
1348     if (EargsSz < sz)
1349 	Eargsp = (char **) erealloc((void *) Eargsp,
1350 				    sizeof(char *) * (EargsSz = sz));
1351     ASSERT(Eargsp);
1352 }
1353 
1354 static void
add_Eargs(char * new_arg)1355 add_Eargs(char *new_arg)
1356 {
1357     if (EargsCnt >= EargsSz)
1358 	Eargsp = (char **) erealloc((void *) Eargsp,
1359 				    sizeof(char *) * (EargsSz += 20));
1360     Eargsp[EargsCnt++] = QUOTE(new_arg);
1361 }
1362 
1363 #if !defined(__WIN32__)
error(char * format,...)1364 void error(char* format, ...)
1365 {
1366     char sbuf[1024];
1367     va_list ap;
1368 
1369     va_start(ap, format);
1370     erts_vsnprintf(sbuf, sizeof(sbuf), format, ap);
1371     va_end(ap);
1372     fprintf(stderr, "erlexec: %s\n", sbuf);
1373     exit(1);
1374 }
1375 #endif
1376 
1377 static void *
emalloc(size_t size)1378 emalloc(size_t size)
1379 {
1380     void *p = malloc(size);
1381     if (p == NULL)
1382 	error("Insufficient memory");
1383     return p;
1384 }
1385 
1386 static void *
erealloc(void * p,size_t size)1387 erealloc(void *p, size_t size)
1388 {
1389     void *res = realloc(p, size);
1390     if (res == NULL)
1391 	error("Insufficient memory");
1392     return res;
1393 }
1394 
1395 static void
efree(void * p)1396 efree(void *p)
1397 {
1398     free(p);
1399 }
1400 
1401 static int
is_one_of_strings(char * str,char * strs[])1402 is_one_of_strings(char *str, char *strs[])
1403 {
1404     int i, j;
1405     for (i = 0; strs[i]; i++) {
1406 	for (j = 0; str[j] && strs[i][j] && str[j] == strs[i][j]; j++);
1407 	if (!str[j] && !strs[i][j])
1408 	    return 1;
1409     }
1410     return 0;
1411 }
1412 
write_str(char * to,const char * from)1413 static char *write_str(char *to, const char *from)
1414 {
1415     while (*from)
1416 	*(to++) = *(from++);
1417     *to = '\0';
1418     return to;
1419 }
1420 
1421 char*
strsave(char * string)1422 strsave(char* string)
1423 {
1424     char* p = emalloc(strlen(string)+1);
1425     strcpy(p, string);
1426     return p;
1427 }
1428 
1429 
1430 #if defined(__WIN32__)
1431 
get_start_erl_data(char * file)1432 static void get_start_erl_data(char *file)
1433 {
1434     static char* a_config_script;
1435     int fp;
1436     char tmpbuffer[512];
1437     char start_erl_data[512];
1438     int bytesread;
1439     char* env;
1440     char* reldir;
1441     char* otpstring;
1442     char* tprogname;
1443     if (boot_script)
1444 	error("Conflicting -start_erl and -boot options");
1445     if (config_scripts)
1446 	error("Conflicting -start_erl and -config options");
1447     env = get_env("RELDIR");
1448     if (env)
1449 	reldir = strsave(env);
1450     else {
1451 	erts_snprintf(tmpbuffer, sizeof(tmpbuffer), "%s/releases", rootdir);
1452 	reldir = strsave(tmpbuffer);
1453     }
1454     free_env_val(env);
1455     if (file == NULL)
1456        erts_snprintf(start_erl_data, sizeof(start_erl_data), "%s/start_erl.data", reldir);
1457     else
1458        erts_snprintf(start_erl_data, sizeof(start_erl_data), "%s", file);
1459     fp = _open(start_erl_data, _O_RDONLY );
1460     if( fp == -1 )
1461 	error( "open failed on %s",start_erl_data );
1462     else {
1463 	if( ( bytesread = _read( fp, tmpbuffer, 512 ) ) <= 0 )
1464 	    error( "Problem reading file %s", start_erl_data );
1465 	else {
1466 	    tmpbuffer[bytesread]='\0';
1467 	    if ((otpstring = strchr(tmpbuffer,' ')) != NULL) {
1468 		*otpstring = '\0';
1469 		otpstring++;
1470 
1471 /*
1472  *   otpstring is the otpversion
1473  *   tmpbuffer is the emuversion
1474 */
1475 	    }
1476 	}
1477     }
1478     tprogname = otpstring;
1479     while (*tprogname) {
1480 	if (*tprogname <= ' ') {
1481 	    *tprogname='\0';
1482 	    break;
1483 	}
1484 	tprogname++;
1485     }
1486 
1487     bindir = emalloc(512);
1488     erts_snprintf(bindir,512,"%s/erts-%s/bin",rootdir,tmpbuffer);
1489     /* BINDIR=$ROOTDIR/erts-$ERTS_VSN/bin */
1490     tprogname = progname;
1491     progname = emalloc(strlen(tprogname) + 20);
1492     erts_snprintf(progname,strlen(tprogname) + 20,"%s -start_erl",tprogname);
1493 
1494     boot_script = emalloc(512);
1495     a_config_script = emalloc(512);
1496     erts_snprintf(boot_script, 512, "%s/%s/start", reldir, otpstring);
1497     erts_snprintf(a_config_script, 512, "%s/%s/sys", reldir, otpstring);
1498     config_scripts = &a_config_script;
1499     config_script_cnt = 1;
1500 
1501     got_start_erl = 1;
1502 }
1503 
1504 
replace_filename(wchar_t * path,wchar_t * new_base)1505 static wchar_t *replace_filename(wchar_t *path, wchar_t *new_base)
1506 {
1507     int plen = wcslen(path);
1508     wchar_t *res = (wchar_t *) emalloc((plen+wcslen(new_base)+1)*sizeof(wchar_t));
1509     wchar_t *p;
1510 
1511     wcscpy(res,path);
1512     for (p = res+plen-1 ;p >= res && *p != L'\\'; --p)
1513         ;
1514     *(p+1) =L'\0';
1515     wcscat(res,new_base);
1516     return res;
1517 }
1518 
path_massage(wchar_t * long_path)1519 static char *path_massage(wchar_t *long_path)
1520 {
1521      char *p;
1522      int len;
1523      len = WideCharToMultiByte(CP_UTF8, 0, long_path, -1, NULL, 0, NULL, NULL);
1524      p = emalloc(len*sizeof(char));
1525      WideCharToMultiByte(CP_UTF8, 0, long_path, -1, p, len, NULL, NULL);
1526      return p;
1527 }
1528 
do_lookup_in_section(InitSection * inis,char * name,char * section,wchar_t * filename,int is_path)1529 static char *do_lookup_in_section(InitSection *inis, char *name,
1530 				  char *section, wchar_t *filename, int is_path)
1531 {
1532     char *p = lookup_init_entry(inis, name);
1533 
1534     if (p == NULL) {
1535 	error("Could not find key %s in section %s of file %S",
1536 	      name,section,filename);
1537     }
1538 
1539     return strsave(p);
1540 }
1541 
1542 // Setup bindir, rootdir and progname as utf8 buffers
get_parameters(int argc,char ** argv)1543 static void get_parameters(int argc, char** argv)
1544 {
1545     wchar_t *p;
1546     wchar_t buffer[MAX_PATH];
1547     wchar_t *ini_filename;
1548     HANDLE module = GetModuleHandle(NULL); /* This might look strange, but we want the erl.ini
1549 					      that resides in the same dir as erl.exe, not
1550 					      an erl.ini in our directory */
1551     InitFile *inif;
1552     InitSection *inis;
1553 
1554     if (module == NULL) {
1555         error("Cannot GetModuleHandle()");
1556     }
1557 
1558     if (GetModuleFileNameW(module,buffer,MAX_PATH) == 0) {
1559         error("Could not GetModuleFileName");
1560     }
1561 
1562     ini_filename = replace_filename(buffer,INI_FILENAME);
1563 
1564     if ((inif = load_init_file(ini_filename)) == NULL) {
1565 	wchar_t wbindir[MAX_PATH];
1566 	wchar_t wrootdir[MAX_PATH];
1567 
1568 	/* Assume that the path is absolute and that
1569 	   it does not contain any symbolic link */
1570 
1571 	/* Determine bindir */
1572 	if (GetEnvironmentVariableW(L"ERLEXEC_DIR", buffer, MAX_PATH) == 0) {
1573 	    wcscpy(buffer, ini_filename);
1574 	    for (p = buffer+wcslen(buffer)-1; p >= buffer && *p != L'\\'; --p)
1575 		;
1576 	    *p = L'\0';
1577 	}
1578 	bindir = path_massage(buffer);
1579 
1580 	/* Determine rootdir */
1581 	for (p = buffer+wcslen(buffer)-1; p >= buffer && *p != L'\\'; --p)
1582 	    ;
1583 	p--;
1584 	for (;p >= buffer && *p != L'\\'; --p)
1585 	    ;
1586 	*p =L'\0';
1587 	rootdir = path_massage(buffer);
1588 
1589 	/* Hardcoded progname */
1590 	progname = strsave(DEFAULT_PROGNAME);
1591     } else {
1592 	if ((inis = lookup_init_section(inif,INI_SECTION)) == NULL) {
1593 	    error("Could not find section %s in init file %s",
1594 		  INI_SECTION, ini_filename);
1595 	}
1596 
1597 	bindir = do_lookup_in_section(inis, "Bindir", INI_SECTION, ini_filename,1);
1598 	rootdir = do_lookup_in_section(inis, "Rootdir", INI_SECTION,
1599 				       ini_filename,1);
1600 	progname = do_lookup_in_section(inis, "Progname", INI_SECTION,
1601 					ini_filename,0);
1602 	free_init_file(inif);
1603     }
1604 
1605     emu = EMULATOR_EXECUTABLE;
1606     start_emulator_program = strsave(argv[0]);
1607 
1608     free(ini_filename);
1609 }
1610 
1611 static void
get_home(void)1612 get_home(void)
1613 {
1614     wchar_t *profile;
1615     char* homedrive;
1616     char* homepath;
1617 
1618     homedrive = get_env("HOMEDRIVE");
1619     homepath = get_env("HOMEPATH");
1620     if (!homedrive || !homepath) {
1621         if (SHGetKnownFolderPath(&FOLDERID_Profile, 0, NULL, &profile) == S_OK) {
1622             home = utf16_to_utf8(profile);
1623             /* CoTaskMemFree(profile); */
1624 	} else
1625 	    error("HOMEDRIVE or HOMEPATH not set and getting USERPROFILE failed");
1626     } else {
1627 	home = emalloc(strlen(homedrive)+strlen(homepath)+1);
1628 	strcpy(home, homedrive);
1629 	strcat(home, homepath);
1630     }
1631     free_env_val(homedrive);
1632     free_env_val(homepath);
1633 }
1634 
1635 #else
1636 
1637 static void
get_parameters(int argc,char ** argv)1638 get_parameters(int argc, char** argv)
1639 {
1640     progname = get_env("PROGNAME");
1641     if (!progname) {
1642 	progname = strsave(DEFAULT_PROGNAME);
1643     }
1644 
1645     emu = get_env("EMU");
1646     if (!emu) {
1647 	emu = strsave(EMULATOR_EXECUTABLE);
1648     }
1649 
1650     bindir = get_env("BINDIR");
1651     if (!bindir) {
1652 	/* Determine bindir from absolute path to executable */
1653 	char *p;
1654 	char buffer[PATH_MAX];
1655 	strncpy(buffer, argv[0], sizeof(buffer));
1656 	buffer[sizeof(buffer)-1] = '\0';
1657 
1658 	for (p = buffer+strlen(buffer)-1 ; p >= buffer && *p != '/'; --p)
1659 	    ;
1660 	*p ='\0';
1661 	bindir = strsave(buffer);
1662     }
1663 
1664     rootdir = get_env("ROOTDIR");
1665     if (!rootdir) {
1666 	/* Determine rootdir from absolute path to bindir */
1667 	char *p;
1668 	char buffer[PATH_MAX];
1669 	strncpy(buffer, bindir, sizeof(buffer));
1670 	buffer[sizeof(buffer)-1] = '\0';
1671 
1672 	for (p = buffer+strlen(buffer)-1; p >= buffer && *p != '/'; --p)
1673 	    ;
1674 	p--;
1675 	for (; p >= buffer && *p != '/'; --p)
1676 	    ;
1677 	*p ='\0';
1678 	rootdir = strsave(buffer);
1679     }
1680 
1681     if (!progname || !emu || !rootdir || !bindir) {
1682 	error("PROGNAME, EMU, ROOTDIR and BINDIR  must be set");
1683     }
1684 }
1685 
1686 static void
get_home(void)1687 get_home(void)
1688 {
1689     home = get_env("HOME");
1690 }
1691 
1692 #endif
1693 
add_epmd_port(void)1694 static void add_epmd_port(void)
1695 {
1696     char* port = get_env("ERL_EPMD_PORT");
1697     if (port != NULL) {
1698 	add_args("-epmd_port", port, NULL);
1699     }
1700 }
1701 
build_args_from_env(char * env_var)1702 static char **build_args_from_env(char *env_var)
1703 {
1704     char *value = get_env(env_var);
1705     char **res = build_args_from_string(value, 0);
1706     free_env_val(value);
1707     return res;
1708 }
1709 
build_args_from_string(char * string,int allow_comments)1710 static char **build_args_from_string(char *string, int allow_comments)
1711 {
1712     int argc = 0;
1713     char **argv = NULL;
1714     int alloced = 0;
1715     char **cur_s = NULL;	/* Initialized to avoid warning. */
1716     int s_alloced = 0;
1717     int s_pos = 0;
1718     char *p = string;
1719     enum {Start, Build, Build0, BuildSQuoted, BuildDQuoted, AcceptNext, BuildComment} state;
1720 
1721 #define ENSURE()					\
1722     if (s_pos >= s_alloced) {			        \
1723 	if (!*cur_s) {					\
1724 	    *cur_s = emalloc(s_alloced = 20);		\
1725 	} else {					\
1726 	    *cur_s = erealloc(*cur_s, s_alloced += 20);	\
1727 	}						\
1728     }
1729 
1730 
1731     if (!p)
1732 	return NULL;
1733     argv = emalloc(sizeof(char *) * (alloced = 10));
1734     state = Start;
1735     for(;;) {
1736 	switch (state) {
1737 	case Start:
1738 	    if (!*p)
1739 		goto done;
1740 	    if (argc >= alloced - 2) { /* Make room for extra NULL and "--" */
1741 		argv = erealloc(argv, (alloced += 10) * sizeof(char *));
1742 	    }
1743 	    cur_s = argc + argv;
1744 	    *cur_s = NULL;
1745 	    s_pos = 0;
1746 	    s_alloced = 0;
1747 	    state = Build0;
1748 	    break;
1749 	case Build0:
1750 	    switch (*p) {
1751             case '\n':
1752 	    case '\f':
1753 	    case '\r':
1754 	    case '\t':
1755 	    case '\v':
1756 	    case ' ':
1757 		++p;
1758 		break;
1759 	    case '\0':
1760 		state = Start;
1761 		break;
1762             case '#':
1763                 if (allow_comments) {
1764                     ++p;
1765                     state = BuildComment;
1766                     break;
1767                 }
1768                 /* fall-through */
1769 	    default:
1770 		state = Build;
1771 		break;
1772 	    }
1773 	    break;
1774 	case Build:
1775 	    switch (*p) {
1776 	    case '#':
1777                 if (!allow_comments)
1778                     goto build_default;
1779                 /* fall-through */
1780 	    case '\n':
1781 	    case '\f':
1782 	    case '\r':
1783 	    case '\t':
1784 	    case '\v':
1785 	    case ' ':
1786 	    case '\0':
1787 		ENSURE();
1788 		(*cur_s)[s_pos] = '\0';
1789 		++argc;
1790 		state = Start;
1791 		break;
1792 	    case '"':
1793 		++p;
1794 		state = BuildDQuoted;
1795 		break;
1796 	    case '\'':
1797 		++p;
1798 		state = BuildSQuoted;
1799 		break;
1800 	    case '\\':
1801 		++p;
1802 		state = AcceptNext;
1803 		break;
1804 	    default:
1805             build_default:
1806 		ENSURE();
1807 		(*cur_s)[s_pos++] = *p++;
1808 		break;
1809 	    }
1810 	    break;
1811 	case BuildDQuoted:
1812 	    switch (*p) {
1813 	    case '"':
1814 		++p;
1815 		/* fall through */
1816 	    case '\0':
1817 		state = Build;
1818 		break;
1819 	    default:
1820 		ENSURE();
1821 		(*cur_s)[s_pos++] = *p++;
1822 		break;
1823 	    }
1824 	    break;
1825 	case BuildSQuoted:
1826 	    switch (*p) {
1827 	    case '\'':
1828 		++p;
1829 		/* fall through */
1830 	    case '\0':
1831 		state = Build;
1832 		break;
1833 	    default:
1834 		ENSURE();
1835 		(*cur_s)[s_pos++] = *p++;
1836 		break;
1837 	    }
1838 	    break;
1839 	case AcceptNext:
1840 	    if (*p) {
1841 		ENSURE();
1842 		(*cur_s)[s_pos++] = *p++;
1843 	    }
1844 	    state = Build;
1845 	    break;
1846         case BuildComment:
1847             if (*p == '\n' || *p == '\0') {
1848                 state = Build0;
1849             } else {
1850                 p++;
1851             }
1852             break;
1853 	}
1854     }
1855 done:
1856     if (!argc) {
1857 	efree(argv);
1858 	return NULL;
1859     }
1860     argv[argc++] = "--"; /* Add a -- separator in order
1861                             for flags from different environments
1862                             to not effect each other */
1863     argv[argc++] = NULL; /* Sure to be large enough */
1864     return argv;
1865 #undef ENSURE
1866 }
1867 
1868 static const char *
errno_string(void)1869 errno_string(void)
1870 {
1871     const char *str = strerror(errno);
1872     if (!str)
1873 	return "unknown error";
1874     return str;
1875 }
1876 
1877 #define FILE_BUFF_SIZE 1024
1878 
1879 static char **
read_args_file(char * filename)1880 read_args_file(char *filename)
1881 {
1882     FILE *file;
1883     char buff[FILE_BUFF_SIZE+1];
1884     size_t astr_sz = 0, sz;
1885     char *astr = buff;
1886     char **res;
1887 
1888     do {
1889 	errno = 0;
1890 	file = fopen(filename, "r");
1891     } while (!file && errno == EINTR);
1892     if (!file) {
1893 #ifdef __WIN32__
1894         char cwd[MAX_PATH];
1895         if (_getcwd(cwd, sizeof(cwd)) == NULL) {
1896 #else
1897         char cwd[PATH_MAX];
1898         if (getcwd(cwd, sizeof(cwd)) == NULL) {
1899 #endif
1900             cwd[0] = '\0';
1901         }
1902 	usage_format("Failed to open arguments file \"%s\" at \"%s\": %s\n",
1903 		     filename,
1904              cwd,
1905 		     errno_string());
1906     }
1907 
1908     sz = fread(astr, 1, FILE_BUFF_SIZE, file);
1909 
1910     while (!feof(file) && sz == FILE_BUFF_SIZE) {
1911         if (astr == buff) {
1912             astr = emalloc(FILE_BUFF_SIZE*2+1);
1913             astr_sz = FILE_BUFF_SIZE;
1914             memcpy(astr, buff, sizeof(buff));
1915         } else {
1916             astr_sz += FILE_BUFF_SIZE;
1917             astr = erealloc(astr,astr_sz+FILE_BUFF_SIZE+1);
1918         }
1919         sz = fread(astr+astr_sz, 1, FILE_BUFF_SIZE, file);
1920     }
1921 
1922     if (ferror(file)) {
1923         usage_format("Failed to read arguments file \"%s\": %s\n",
1924                      filename,
1925                      errno_string());
1926     }
1927 
1928     astr[astr_sz + sz] = '\0';
1929 
1930     fclose(file);
1931 
1932     if (astr[0] == '\0')
1933 	res = NULL;
1934     else
1935 	res = build_args_from_string(astr, !0);
1936 
1937     if (astr != buff)
1938         efree(astr);
1939 
1940     return res;
1941 
1942 #undef FILE_BUFF_SIZE
1943 }
1944 
1945 
1946 typedef struct {
1947     char **argv;
1948     int argc;
1949     int size;
1950 } argv_buf;
1951 
1952 static void
1953 trim_argv_buf(argv_buf *abp)
1954 {
1955     abp->argv = erealloc(abp->argv, sizeof(char *)*(abp->size = abp->argc));
1956 }
1957 
1958 static void
1959 save_arg(argv_buf *abp, char *arg)
1960 {
1961     if (abp->size <= abp->argc) {
1962 	if (!abp->argv)
1963 	    abp->argv = emalloc(sizeof(char *)*(abp->size = 100));
1964 	else
1965 	    abp->argv = erealloc(abp->argv, sizeof(char *)*(abp->size += 100));
1966     }
1967     abp->argv[abp->argc++] = arg;
1968 }
1969 
1970 #define DEF_ARGV_STACK_SIZE 10
1971 #define ARGV_STACK_SIZE_INCR 50
1972 
1973 typedef struct {
1974     char **argv;
1975     int ix;
1976 } argv_stack_element;
1977 
1978 typedef struct {
1979     int top_ix;
1980     int size;
1981     argv_stack_element *base;
1982     argv_stack_element def_buf[DEF_ARGV_STACK_SIZE];
1983 } argv_stack;
1984 
1985 #define ARGV_STACK_INIT(S)		\
1986 do {					\
1987     (S)->top_ix = 0;			\
1988     (S)->size = DEF_ARGV_STACK_SIZE;	\
1989     (S)->base = &(S)->def_buf[0];	\
1990 } while (0)
1991 
1992 static void
1993 push_argv(argv_stack *stck, char **argv, int ix)
1994 {
1995     if (stck->top_ix == stck->size) {
1996 	if (stck->base != &stck->def_buf[0]) {
1997 	    stck->size += ARGV_STACK_SIZE_INCR;
1998 	    stck->base = erealloc(stck->base,
1999 				  sizeof(argv_stack_element)*stck->size);
2000 	}
2001 	else {
2002 	    argv_stack_element *base;
2003 	    base = emalloc(sizeof(argv_stack_element)
2004 			   *(stck->size + ARGV_STACK_SIZE_INCR));
2005 	    memcpy((void *) base,
2006 		   (void *) stck->base,
2007 		   sizeof(argv_stack_element)*stck->size);
2008 	    stck->base = base;
2009 	    stck->size += ARGV_STACK_SIZE_INCR;
2010 	}
2011     }
2012     stck->base[stck->top_ix].argv = argv;
2013     stck->base[stck->top_ix++].ix = ix;
2014 }
2015 
2016 static void
2017 pop_argv(argv_stack *stck, char ***argvp, int *ixp)
2018 {
2019     if (stck->top_ix == 0) {
2020 	*argvp = NULL;
2021 	*ixp = 0;
2022     }
2023     else {
2024 	*argvp = stck->base[--stck->top_ix].argv;
2025 	*ixp = stck->base[stck->top_ix].ix;
2026 	if (stck->top_ix == 0 && stck->base != &stck->def_buf[0]) {
2027 	    efree(stck->base);
2028 	    stck->base = &stck->def_buf[0];
2029 	    stck->size = DEF_ARGV_STACK_SIZE;
2030 	}
2031     }
2032 }
2033 
2034 static void
2035 get_file_args(char *filename, argv_buf *abp, argv_buf *xabp)
2036 {
2037     argv_stack stck;
2038     int i;
2039     char **argv;
2040 
2041     ARGV_STACK_INIT(&stck);
2042 
2043     i = 0;
2044     argv = read_args_file(filename);
2045 
2046     while (argv) {
2047 
2048 	while (argv[i]) {
2049 	    if (strcmp(argv[i], "-args_file") == 0) {
2050 		char **new_argv;
2051 		char *fname;
2052 		if (!argv[++i])
2053 		    usage("-args_file");
2054 		fname = argv[i++];
2055 		new_argv = read_args_file(fname);
2056 		if (new_argv) {
2057 		    if (argv[i])
2058 			push_argv(&stck, argv, i);
2059 		    else
2060 			efree(argv);
2061 		    i = 0;
2062 		    argv = new_argv;
2063 		}
2064 	    }
2065 	    else {
2066 		if (strcmp(argv[i], "-extra") == 0) {
2067 		    i++;
2068 		    while (argv[i])
2069 			save_arg(xabp, argv[i++]);
2070 		    break;
2071 		}
2072 		save_arg(abp, argv[i++]);
2073 	    }
2074 	}
2075 
2076 	efree(argv);
2077 
2078 	pop_argv(&stck, &argv, &i);
2079     }
2080 }
2081 
2082 static void
2083 initial_argv_massage(int *argc, char ***argv)
2084 {
2085     argv_buf ab = {0}, xab = {0};
2086     int ix, vix, ac;
2087     char **av;
2088     char *sep = "--";
2089     struct {
2090 	int argc;
2091 	char **argv;
2092     } avv[] = {{INT_MAX, NULL}, {INT_MAX, NULL}, {INT_MAX, NULL},
2093 	       {INT_MAX, NULL}, {INT_MAX, NULL},
2094                {INT_MAX, NULL}, {INT_MAX, NULL}};
2095     /*
2096      * The environment flag containing OTP release is intentionally
2097      * undocumented and intended for OTP internal use only.
2098      */
2099 
2100     vix = 0;
2101 
2102     av = build_args_from_env("ERL_OTP" OTP_SYSTEM_VERSION "_FLAGS");
2103     if (av)
2104 	avv[vix++].argv = av;
2105 
2106     av = build_args_from_env("ERL_AFLAGS");
2107     if (av)
2108 	avv[vix++].argv = av;
2109 
2110     /* command line */
2111     if (*argc > 1) {
2112 	avv[vix].argc = *argc - 1;
2113 	avv[vix++].argv = &(*argv)[1];
2114         avv[vix].argc = 1;
2115         avv[vix++].argv = &sep;
2116     }
2117 
2118     av = build_args_from_env("ERL_FLAGS");
2119     if (av)
2120 	avv[vix++].argv = av;
2121 
2122     av = build_args_from_env("ERL_ZFLAGS");
2123     if (av)
2124 	avv[vix++].argv = av;
2125 
2126     if (vix == (*argc > 1 ? 2 : 0)) {
2127 	/* Only command line argv; check if we can use argv as it is... */
2128 	ac = *argc;
2129 	av = *argv;
2130 	for (ix = 1; ix < ac; ix++) {
2131 	    if (strcmp(av[ix], "-args_file") == 0) {
2132 		/* ... no; we need to expand arguments from
2133 		   file into argument list */
2134 		goto build_new_argv;
2135 	    }
2136 	    if (strcmp(av[ix], "-extra") == 0) {
2137 		break;
2138 	    }
2139 	}
2140 
2141 	/* ... yes; we can use argv as it is. */
2142 	return;
2143     }
2144 
2145  build_new_argv:
2146 
2147     save_arg(&ab, (*argv)[0]);
2148 
2149     vix = 0;
2150     while (avv[vix].argv) {
2151 	ac = avv[vix].argc;
2152 	av = avv[vix].argv;
2153 
2154 	ix = 0;
2155 	while (ix < ac && av[ix]) {
2156 	    if (strcmp(av[ix], "-args_file") == 0) {
2157 		if (++ix == ac)
2158 		    usage("-args_file");
2159 		get_file_args(av[ix++], &ab, &xab);
2160 	    }
2161 	    else {
2162 		if (strcmp(av[ix], "-extra") == 0) {
2163 		    ix++;
2164 		    while (ix < ac && av[ix])
2165 			save_arg(&xab, av[ix++]);
2166 		    break;
2167 		}
2168 		save_arg(&ab, av[ix++]);
2169 	    }
2170 	}
2171 
2172 	vix++;
2173     }
2174 
2175     vix = 0;
2176     while (avv[vix].argv) {
2177 	if (avv[vix].argc == INT_MAX) /* not command line */
2178 	    efree(avv[vix].argv);
2179 	vix++;
2180     }
2181 
2182     if (xab.argc) {
2183 	save_arg(&ab, "-extra");
2184 	for (ix = 0; ix < xab.argc; ix++)
2185 	    save_arg(&ab, xab.argv[ix]);
2186 	efree(xab.argv);
2187     }
2188 
2189     save_arg(&ab, NULL);
2190     trim_argv_buf(&ab);
2191     *argv = ab.argv;
2192     *argc = ab.argc - 1;
2193 }
2194 
2195 #ifdef __WIN32__
2196 static char*
2197 possibly_quote(char* arg)
2198 {
2199     int mustQuote = NO;
2200     int n = 0;
2201     char* s;
2202     char* narg;
2203 
2204     /*
2205      * Scan the string to find out if it needs quoting and return
2206      * the original argument if not.
2207      */
2208 
2209     for (s = arg; *s; s++, n++) {
2210 	if (*s == ' ' || *s == '"') {
2211 	    mustQuote = YES;
2212 	    n++;
2213 	}
2214     }
2215     if (!mustQuote) {
2216 	return arg;
2217     }
2218 
2219     /*
2220      * Insert the quotes and put a backslash in front of every quote
2221      * inside the string.
2222      */
2223 
2224     s = narg = emalloc(n+2+1);
2225     for (*s++ = '"'; *arg; arg++, s++) {
2226 	if (*s == '"') {
2227 	    *s++ = '\\';
2228 	}
2229 	*s = *arg;
2230     }
2231     *s++ = '"';
2232     *s = '\0';
2233     return narg;
2234 }
2235 
2236 /*
2237  * Unicode helpers to handle environment and command line parameters on
2238  * Windows. We internally handle all environment variables in UTF8,
2239  * but put and get the environment using the WCHAR (limited UTF16) interface
2240  *
2241  * These are simplified to only handle Unicode characters that can fit in
2242  * Windows simplified UTF16, i.e. characters that fit in 16 bits.
2243  */
2244 
2245 static int utf8_len(unsigned char first)
2246 {
2247     if ((first & ((unsigned char) 0x80)) == 0) {
2248 	return 1;
2249     } else if ((first & ((unsigned char) 0xE0)) == 0xC0) {
2250 	return 2;
2251     } else if ((first & ((unsigned char) 0xF0)) == 0xE0) {
2252 	return 3;
2253     } else if ((first & ((unsigned char) 0xF8)) == 0xF0) {
2254 	return 4;
2255     }
2256     return 1; /* will be a '?' */
2257 }
2258 
2259 static WCHAR *utf8_to_utf16(unsigned char *bytes)
2260 {
2261     unsigned int unipoint;
2262     unsigned char *tmp = bytes;
2263     WCHAR *target, *res;
2264     int num = 0;
2265 
2266     while (*tmp) {
2267 	num++;
2268 	tmp += utf8_len(*tmp);
2269     }
2270     res = target = emalloc((num + 1) * sizeof(WCHAR));
2271     while (*bytes) {
2272 	if (((*bytes) & ((unsigned char) 0x80)) == 0) {
2273 	    unipoint = (unsigned int) *bytes;
2274 	    ++bytes;
2275 	} else if (((*bytes) & ((unsigned char) 0xE0)) == 0xC0) {
2276 	    unipoint =
2277 		(((unsigned int) ((*bytes) & ((unsigned char) 0x1F))) << 6) |
2278 		((unsigned int) (bytes[1] & ((unsigned char) 0x3F)));
2279 	    bytes += 2;
2280 	} else if (((*bytes) & ((unsigned char) 0xF0)) == 0xE0) {
2281 	    unipoint =
2282 		(((unsigned int) ((*bytes) & ((unsigned char) 0xF))) << 12) |
2283 		(((unsigned int) (bytes[1] & ((unsigned char) 0x3F))) << 6) |
2284 		((unsigned int) (bytes[2] & ((unsigned char) 0x3F)));
2285 	    if (unipoint > 0xFFFF) {
2286 		 unipoint = (unsigned int) '?';
2287 	    }
2288 	    bytes +=3;
2289 	} else if (((*bytes) & ((unsigned char) 0xF8)) == 0xF0) {
2290 	    unipoint = (unsigned int) '?'; /* Cannot put in a wchar */
2291 	    bytes += 4;
2292 	} else {
2293 	    unipoint = (unsigned int) '?';
2294 	}
2295 	*target++ = (WCHAR) unipoint;
2296     }
2297     *target = L'\0';
2298     return res;
2299 }
2300 
2301 static int put_utf8(WCHAR ch, unsigned char *target, int sz, int *pos)
2302 {
2303     unsigned int x = (unsigned int) ch;
2304     if (x < 0x80) {
2305     if (*pos >= sz) {
2306 	return -1;
2307     }
2308 	target[(*pos)++] = (unsigned char) x;
2309     }
2310     else if (x < 0x800) {
2311 	if (((*pos) + 1) >= sz) {
2312 	    return -1;
2313 	}
2314 	target[(*pos)++] = (((unsigned char) (x >> 6)) |
2315 			    ((unsigned char) 0xC0));
2316 	target[(*pos)++] = (((unsigned char) (x & 0x3F)) |
2317 			    ((unsigned char) 0x80));
2318     } else {
2319 	if ((x >= 0xD800 && x <= 0xDFFF) ||
2320 	    (x == 0xFFFE) ||
2321 	    (x == 0xFFFF)) { /* Invalid unicode range */
2322 	    return -1;
2323 	}
2324 	if (((*pos) + 2) >= sz) {
2325 	    return -1;
2326 	}
2327 
2328 	target[(*pos)++] = (((unsigned char) (x >> 12)) |
2329 			    ((unsigned char) 0xE0));
2330 	target[(*pos)++] = ((((unsigned char) (x >> 6)) & 0x3F)  |
2331 			    ((unsigned char) 0x80));
2332 	target[(*pos)++] = (((unsigned char) (x & 0x3F)) |
2333 			    ((unsigned char) 0x80));
2334     }
2335     return 0;
2336 }
2337 
2338 static int need_bytes_for_utf8(WCHAR x)
2339 {
2340     if (x < 0x80)
2341 	return 1;
2342     else if (x < 0x800)
2343 	return 2;
2344     else
2345 	return 3;
2346 }
2347 
2348 static WCHAR *latin1_to_utf16(char *str)
2349 {
2350     int len = strlen(str);
2351     int i;
2352     WCHAR *wstr = emalloc((len+1) * sizeof(WCHAR));
2353     for(i=0;i<len;++i)
2354 	wstr[i] = (WCHAR) str[i];
2355     wstr[len] = L'\0';
2356     return wstr;
2357 }
2358 
2359 static char *utf16_to_utf8(WCHAR *wstr)
2360 {
2361     int len = wcslen(wstr);
2362     char *result;
2363     int i,pos;
2364     int reslen = 0;
2365     for(i=0;i<len;++i) {
2366 	reslen += need_bytes_for_utf8(wstr[i]);
2367     }
2368     result = emalloc(reslen+1);
2369     pos = 0;
2370     for(i=0;i<len;++i) {
2371 	if (put_utf8((int) wstr[i], result, reslen, &pos) < 0) {
2372 	    break;
2373 	}
2374     }
2375     result[pos] = '\0';
2376     return result;
2377 }
2378 
2379 #endif
2380