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