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