1 /*
2 * %CopyrightBegin%
3 *
4 * Copyright Ericsson AB 2006-2017. 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 * Purpose: Dialyzer front-end.
22 */
23
24 #include "etc_common.h"
25
26 #define NO 0
27 #define YES 1
28
29 #define ASIZE(a) (sizeof(a)/sizeof(a[0]))
30
31 static int debug = 0; /* Bit flags for debug printouts. */
32
33 static char** eargv_base; /* Base of vector. */
34 static char** eargv; /* First argument for erl. */
35
36 static int eargc; /* Number of arguments in eargv. */
37
38 #ifdef __WIN32__
39 # define QUOTE(s) possibly_quote(s)
40 # define IS_DIRSEP(c) ((c) == '/' || (c) == '\\')
41 # define ERL_NAME "erl.exe"
42 #else
43 # define QUOTE(s) s
44 # define IS_DIRSEP(c) ((c) == '/')
45 # define ERL_NAME "erl"
46 #endif
47
48 #define UNSHIFT(s) eargc++, eargv--; eargv[0] = QUOTE(s)
49 #define PUSH(s) eargv[eargc++] = QUOTE(s)
50 #define PUSH2(s, t) PUSH(s); PUSH(t)
51 #define PUSH3(s, t, u) PUSH2(s, t); PUSH(u)
52
53 /*
54 * Local functions.
55 */
56
57 static void error(char* format, ...);
58 static void* emalloc(size_t size);
59 #ifdef HAVE_COPYING_PUTENV
60 static void efree(void *p);
61 #endif
62 static char* strsave(char* string);
63 static void push_words(char* src);
64 static int run_erlang(char* name, char** argv);
65 static char* get_default_emulator(char* progname);
66 #ifdef __WIN32__
67 static char* possibly_quote(char* arg);
68 static void* erealloc(void *p, size_t size);
69 #endif
70
71 /*
72 * Supply a strerror() function if libc doesn't.
73 */
74 #ifndef HAVE_STRERROR
75
76 extern int sys_nerr;
77
78 #ifndef SYS_ERRLIST_DECLARED
79 extern const char * const sys_errlist[];
80 #endif /* !SYS_ERRLIST_DECLARED */
81
strerror(int errnum)82 char *strerror(int errnum)
83 {
84 static char *emsg[1024];
85
86 if (errnum != 0) {
87 if (errnum > 0 && errnum < sys_nerr)
88 sprintf((char *) &emsg[0], "(%s)", sys_errlist[errnum]);
89 else
90 sprintf((char *) &emsg[0], "errnum = %d ", errnum);
91 }
92 else {
93 emsg[0] = '\0';
94 }
95 return (char *) &emsg[0];
96 }
97 #endif /* !HAVE_STRERROR */
98
99 static char *
get_env(char * key)100 get_env(char *key)
101 {
102 #ifdef __WIN32__
103 DWORD size = 32;
104 char *value=NULL;
105 wchar_t *wcvalue = NULL;
106 wchar_t wckey[256];
107 int len;
108
109 MultiByteToWideChar(CP_UTF8, 0, key, -1, wckey, 256);
110
111 while (1) {
112 DWORD nsz;
113 if (wcvalue)
114 free(wcvalue);
115 wcvalue = (wchar_t*) emalloc(size*sizeof(wchar_t));
116 SetLastError(0);
117 nsz = GetEnvironmentVariableW(wckey, wcvalue, size);
118 if (nsz == 0 && GetLastError() == ERROR_ENVVAR_NOT_FOUND) {
119 free(wcvalue);
120 return NULL;
121 }
122 if (nsz <= size) {
123 len = WideCharToMultiByte(CP_UTF8, 0, wcvalue, -1, NULL, 0, NULL, NULL);
124 value = emalloc(len*sizeof(char));
125 WideCharToMultiByte(CP_UTF8, 0, wcvalue, -1, value, len, NULL, NULL);
126 free(wcvalue);
127 return value;
128 }
129 size = nsz;
130 }
131 #else
132 return getenv(key);
133 #endif
134 }
135
136 static void
free_env_val(char * value)137 free_env_val(char *value)
138 {
139 #ifdef __WIN32__
140 if (value)
141 free(value);
142 #endif
143 }
144
145 static void
set_env(char * key,char * value)146 set_env(char *key, char *value)
147 {
148 #ifdef __WIN32__
149 WCHAR wkey[MAXPATHLEN];
150 WCHAR wvalue[MAXPATHLEN];
151 MultiByteToWideChar(CP_UTF8, 0, key, -1, wkey, MAXPATHLEN);
152 MultiByteToWideChar(CP_UTF8, 0, value, -1, wvalue, MAXPATHLEN);
153 if (!SetEnvironmentVariableW(wkey, wvalue))
154 error("SetEnvironmentVariable(\"%s\", \"%s\") failed!", key, value);
155 #else
156 size_t size = strlen(key) + 1 + strlen(value) + 1;
157 char *str = emalloc(size);
158 sprintf(str, "%s=%s", key, value);
159 if (putenv(str) != 0)
160 error("putenv(\"%s\") failed!", str);
161 #ifdef HAVE_COPYING_PUTENV
162 efree(str);
163 #endif
164 #endif
165 }
166
167
168 #ifdef __WIN32__
wmain(int argc,wchar_t ** wcargv)169 int wmain(int argc, wchar_t **wcargv)
170 {
171 char** argv;
172 #else
173 int main(int argc, char** argv)
174 {
175 #endif
176 int eargv_size;
177 int eargc_base; /* How many arguments in the base of eargv. */
178 char* emulator;
179 char *env;
180 int need_shell = 0;
181
182 #ifdef __WIN32__
183 int len;
184 int i;
185 /* Convert argv to utf8 */
186 argv = emalloc((argc+1) * sizeof(char*));
187 for (i=0; i<argc; i++) {
188 len = WideCharToMultiByte(CP_UTF8, 0, wcargv[i], -1, NULL, 0, NULL, NULL);
189 argv[i] = emalloc(len*sizeof(char));
190 WideCharToMultiByte(CP_UTF8, 0, wcargv[i], -1, argv[i], len, NULL, NULL);
191 }
192 argv[argc] = NULL;
193 #endif
194
195 env = get_env("DIALYZER_EMULATOR");
196 emulator = env ? env : get_default_emulator(argv[0]);
197
198 if (strlen(emulator) >= MAXPATHLEN)
199 error("Value of environment variable DIALYZER_EMULATOR is too large");
200
201 /*
202 * Add scriptname to env
203 */
204 set_env("ESCRIPT_NAME", argv[0]);
205
206 /*
207 * Allocate the argv vector to be used for arguments to Erlang.
208 * Arrange for starting to pushing information in the middle of
209 * the array, to allow easy addition of commands in the beginning.
210 */
211
212 eargv_size = argc*4+100;
213 eargv_base = (char **) emalloc(eargv_size*sizeof(char*));
214 eargv = eargv_base;
215 eargc = 0;
216 push_words(emulator);
217 if (emulator != env) {
218 free(emulator);
219 }
220 eargc_base = eargc;
221 eargv = eargv + eargv_size/2;
222 eargc = 0;
223
224 free_env_val(env);
225
226 /*
227 * Push initial arguments.
228 */
229
230 if (argc > 1 && strcmp(argv[1], "-smp") == 0) {
231 PUSH("-smpauto");
232 argc--, argv++;
233 }
234
235 if (argc > 2 && strcmp(argv[1], "+S") == 0) {
236 PUSH3("-smp", "+S", argv[2]);
237 argc--, argv++;
238 argc--, argv++;
239 }
240
241 if (argc > 2 && strcmp(argv[1], "+P") == 0) {
242 PUSH2("+P", argv[2]);
243 argc--, argv++;
244 argc--, argv++;
245 } else PUSH2("+P", "1000000");
246
247 if (argc > 2 && strcmp(argv[1], "+sbt") == 0) {
248 PUSH2("+sbt", argv[2]);
249 argc--, argv++;
250 argc--, argv++;
251 }
252
253 PUSH("+B");
254 PUSH2("-boot", "no_dot_erlang");
255 PUSH3("-run", "dialyzer", "plain_cl");
256 PUSH("-extra");
257
258 /*
259 * Push everything except --shell.
260 */
261
262 while (argc > 1) {
263 if (strcmp(argv[1], "--shell") == 0) {
264 need_shell = 1;
265 } else {
266 PUSH(argv[1]);
267 }
268 argc--, argv++;
269 }
270
271 if (!need_shell) {
272 UNSHIFT("-noinput");
273 }
274
275 /*
276 * Move up the commands for invoking the emulator and adjust eargv
277 * accordingly.
278 */
279
280 while (--eargc_base >= 0) {
281 UNSHIFT(eargv_base[eargc_base]);
282 }
283
284 /*
285 * Invoke Erlang with the collected options.
286 */
287
288 PUSH(NULL);
289 return run_erlang(eargv[0], eargv);
290 }
291
292 static void
293 push_words(char* src)
294 {
295 char sbuf[MAXPATHLEN];
296 char* dst;
297
298 dst = sbuf;
299 while ((*dst++ = *src++) != '\0') {
300 if (isspace((int)*src)) {
301 *dst = '\0';
302 PUSH(strsave(sbuf));
303 dst = sbuf;
304 do {
305 src++;
306 } while (isspace((int)*src));
307 }
308 }
309 if (sbuf[0])
310 PUSH(strsave(sbuf));
311 }
312
313 #ifdef __WIN32__
314 wchar_t *make_commandline(char **argv)
315 {
316 static wchar_t *buff = NULL;
317 static int siz = 0;
318 int num = 0, len;
319 char **arg;
320 wchar_t *p;
321
322 if (*argv == NULL) {
323 return L"";
324 }
325 for (arg = argv; *arg != NULL; ++arg) {
326 num += strlen(*arg)+1;
327 }
328 if (!siz) {
329 siz = num;
330 buff = (wchar_t *) emalloc(siz*sizeof(wchar_t));
331 } else if (siz < num) {
332 siz = num;
333 buff = (wchar_t *) erealloc(buff,siz*sizeof(wchar_t));
334 }
335 p = buff;
336 num=0;
337 for (arg = argv; *arg != NULL; ++arg) {
338 len = MultiByteToWideChar(CP_UTF8, 0, *arg, -1, p, siz);
339 p+=(len-1);
340 *p++=L' ';
341 }
342 *(--p) = L'\0';
343
344 if (debug) {
345 printf("Processed command line:%S\n",buff);
346 }
347 return buff;
348 }
349
350 int my_spawnvp(char **argv)
351 {
352 STARTUPINFOW siStartInfo;
353 PROCESS_INFORMATION piProcInfo;
354 DWORD ec;
355
356 memset(&siStartInfo,0,sizeof(STARTUPINFOW));
357 siStartInfo.cb = sizeof(STARTUPINFOW);
358 siStartInfo.dwFlags = STARTF_USESTDHANDLES;
359 siStartInfo.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
360 siStartInfo.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
361 siStartInfo.hStdError = GetStdHandle(STD_ERROR_HANDLE);
362 siStartInfo.wShowWindow = SW_HIDE;
363 siStartInfo.dwFlags |= STARTF_USESHOWWINDOW;
364
365
366 if (!CreateProcessW(NULL,
367 make_commandline(argv),
368 NULL,
369 NULL,
370 TRUE,
371 0,
372 NULL,
373 NULL,
374 &siStartInfo,
375 &piProcInfo)) {
376 return -1;
377 }
378 CloseHandle(piProcInfo.hThread);
379
380 WaitForSingleObject(piProcInfo.hProcess,INFINITE);
381 if (!GetExitCodeProcess(piProcInfo.hProcess,&ec)) {
382 return 0;
383 }
384 return (int) ec;
385 }
386 #endif /* __WIN32__ */
387
388
389 static int
390 run_erlang(char* progname, char** argv)
391 {
392 #ifdef __WIN32__
393 int status;
394 #endif
395
396 if (debug) {
397 int i = 0;
398 while (argv[i] != NULL)
399 printf(" %s", argv[i++]);
400 printf("\n");
401 }
402
403 #ifdef __WIN32__
404 /*
405 * Alas, we must wait here for the program to finish.
406 * Otherwise, the shell from which we was executed will think
407 * we are finished and print a prompt and read keyboard input.
408 */
409
410 status = my_spawnvp(argv)/*_spawnvp(_P_WAIT,progname,argv)*/;
411 if (status == -1) {
412 fprintf(stderr, "dialyzer: Error executing '%s': %d", progname,
413 GetLastError());
414 }
415 return status;
416 #else
417 execvp(progname, argv);
418 error("Error %d executing \'%s\'.", errno, progname);
419 return 2;
420 #endif
421 }
422
423 static void
424 error(char* format, ...)
425 {
426 char sbuf[1024];
427 va_list ap;
428
429 va_start(ap, format);
430 erts_vsnprintf(sbuf, sizeof(sbuf), format, ap);
431 va_end(ap);
432 fprintf(stderr, "dialyzer: %s\n", sbuf);
433 exit(1);
434 }
435
436 static void*
437 emalloc(size_t size)
438 {
439 void *p = malloc(size);
440 if (p == NULL)
441 error("Insufficient memory");
442 return p;
443 }
444
445 #ifdef __WIN32__
446 static void *
447 erealloc(void *p, size_t size)
448 {
449 void *res = realloc(p, size);
450 if (res == NULL)
451 error("Insufficient memory");
452 return res;
453 }
454 #endif
455
456 #ifdef HAVE_COPYING_PUTENV
457 static void
458 efree(void *p)
459 {
460 free(p);
461 }
462 #endif
463
464 static char*
465 strsave(char* string)
466 {
467 char* p = emalloc(strlen(string)+1);
468 strcpy(p, string);
469 return p;
470 }
471
472 static int
473 file_exists(char *progname)
474 {
475 #ifdef __WIN32__
476 wchar_t wcsbuf[MAXPATHLEN];
477 MultiByteToWideChar(CP_UTF8, 0, progname, -1, wcsbuf, MAXPATHLEN);
478 return (_waccess(wcsbuf, 0) != -1);
479 #else
480 return (access(progname, 1) != -1);
481 #endif
482 }
483
484 static char*
485 get_default_emulator(char* progname)
486 {
487 char sbuf[MAXPATHLEN];
488 char* s;
489
490 if (strlen(progname) >= sizeof(sbuf))
491 return strsave(ERL_NAME);
492
493 strcpy(sbuf, progname);
494 for (s = sbuf+strlen(sbuf); s >= sbuf; s--) {
495 if (IS_DIRSEP(*s)) {
496 strcpy(s+1, ERL_NAME);
497 if(file_exists(sbuf))
498 return strsave(sbuf);
499 break;
500 }
501 }
502 return strsave(ERL_NAME);
503 }
504
505 #ifdef __WIN32__
506 static char*
507 possibly_quote(char* arg)
508 {
509 int mustQuote = NO;
510 int n = 0;
511 char* s;
512 char* narg;
513
514 if (arg == NULL) {
515 return arg;
516 }
517
518 /*
519 * Scan the string to find out if it needs quoting and return
520 * the original argument if not.
521 */
522
523 for (s = arg; *s; s++, n++) {
524 switch(*s) {
525 case ' ':
526 mustQuote = YES;
527 continue;
528 case '"':
529 mustQuote = YES;
530 n++;
531 continue;
532 case '\\':
533 if(s[1] == '"')
534 n++;
535 continue;
536 default:
537 continue;
538 }
539 }
540 if (!mustQuote) {
541 return arg;
542 }
543
544 /*
545 * Insert the quotes and put a backslash in front of every quote
546 * inside the string.
547 */
548
549 s = narg = emalloc(n+2+1);
550 for (*s++ = '"'; *arg; arg++, s++) {
551 if (*arg == '"' || (*arg == '\\' && arg[1] == '"')) {
552 *s++ = '\\';
553 }
554 *s = *arg;
555 }
556 if (s[-1] == '\\') {
557 *s++ ='\\';
558 }
559 *s++ = '"';
560 *s = '\0';
561 return narg;
562 }
563 #endif /* __WIN32__ */
564