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