1 /*
2  * %CopyrightBegin%
3  *
4  * Copyright Ericsson AB 2007-2018. 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: escript front-end.
22  */
23 
24 #include "etc_common.h"
25 
26 static int debug = 0;		/* Bit flags for debug printouts. */
27 
28 static char** eargv_base;	/* Base of vector. */
29 static char** eargv;		/* First argument for erl. */
30 
31 static int eargc;		/* Number of arguments in eargv. */
32 
33 #define BOOL int
34 #define FALSE 0
35 #define TRUE 1
36 
37 #ifdef __WIN32__
38 #  define QUOTE(s) possibly_quote(s)
39 #  define IS_DIRSEP(c) ((c) == '/' || (c) == '\\')
40 #  define DIRSEPSTR "\\"
41 #  define LDIRSEPSTR L"\\"
42 #  define LPATHSEPSTR L";"
43 #  define PMAX MAX_PATH
44 #  define ERL_NAME "erl.exe"
45 #else
46 #  define QUOTE(s) s
47 #  define IS_DIRSEP(c) ((c) == '/')
48 #  define DIRSEPSTR "/"
49 #  define PATHSEPSTR ":"
50 #  define PMAX PATH_MAX
51 #  define ERL_NAME "erl"
52 #endif
53 
54 #define UNSHIFT(s) eargc++, eargv--; eargv[0] = QUOTE(s)
55 #define UNSHIFT3(s, t, u) UNSHIFT(u); UNSHIFT(t); UNSHIFT(s)
56 #define PUSH(s) eargv[eargc++] = QUOTE(s)
57 #define PUSH2(s, t) PUSH(s); PUSH(t)
58 #define PUSH3(s, t, u) PUSH2(s, t); PUSH(u)
59 #define LINEBUFSZ 1024
60 
61 /*
62  * Local functions.
63  */
64 
65 static void error(char* format, ...);
66 static void* emalloc(size_t size);
67 static void efree(void *p);
68 static char* strsave(char* string);
69 static int run_erlang(char* name, char** argv);
70 static char* get_default_emulator(char* progname);
71 #ifdef __WIN32__
72 static char* possibly_quote(char* arg);
73 static void* erealloc(void *p, size_t size);
74 #endif
75 
76 /*
77  * Supply a strerror() function if libc doesn't.
78  */
79 #ifndef HAVE_STRERROR
80 
81 extern int sys_nerr;
82 
83 #ifndef SYS_ERRLIST_DECLARED
84 extern const char * const sys_errlist[];
85 #endif /* !SYS_ERRLIST_DECLARED */
86 
strerror(int errnum)87 char *strerror(int errnum)
88 {
89   static char *emsg[1024];
90 
91   if (errnum != 0) {
92     if (errnum > 0 && errnum < sys_nerr)
93       sprintf((char *) &emsg[0], "(%s)", sys_errlist[errnum]);
94     else
95       sprintf((char *) &emsg[0], "errnum = %d ", errnum);
96   }
97   else {
98     emsg[0] = '\0';
99   }
100   return (char *) &emsg[0];
101 }
102 #endif /* !HAVE_STRERROR */
103 
104 static char *
get_env(char * key)105 get_env(char *key)
106 {
107 #ifdef __WIN32__
108     DWORD size = 32;
109     char  *value=NULL;
110     wchar_t *wcvalue = NULL;
111     wchar_t wckey[256];
112     int len;
113 
114     MultiByteToWideChar(CP_UTF8, 0, key, -1, wckey, 256);
115 
116     while (1) {
117 	DWORD nsz;
118 	if (wcvalue)
119 	    efree(wcvalue);
120 	wcvalue = (wchar_t *) emalloc(size*sizeof(wchar_t));
121 	SetLastError(0);
122 	nsz = GetEnvironmentVariableW(wckey, wcvalue, size);
123 	if (nsz == 0 && GetLastError() == ERROR_ENVVAR_NOT_FOUND) {
124 	    efree(wcvalue);
125 	    return NULL;
126 	}
127 	if (nsz <= size) {
128 	    len = WideCharToMultiByte(CP_UTF8, 0, wcvalue, -1, NULL, 0, NULL, NULL);
129 	    value = emalloc(len*sizeof(char));
130 	    WideCharToMultiByte(CP_UTF8, 0, wcvalue, -1, value, len, NULL, NULL);
131 	    efree(wcvalue);
132 	    return value;
133 	}
134 	size = nsz;
135     }
136 #else
137     return getenv(key);
138 #endif
139 }
140 
141 static void
set_env(char * key,char * value)142 set_env(char *key, char *value)
143 {
144 #ifdef __WIN32__
145     WCHAR wkey[MAXPATHLEN];
146     WCHAR wvalue[MAXPATHLEN];
147     MultiByteToWideChar(CP_UTF8, 0, key, -1, wkey, MAXPATHLEN);
148     MultiByteToWideChar(CP_UTF8, 0, value, -1, wvalue, MAXPATHLEN);
149     if (!SetEnvironmentVariableW(wkey, wvalue))
150         error("SetEnvironmentVariable(\"%s\", \"%s\") failed!", key, value);
151 #else
152     size_t size = strlen(key) + 1 + strlen(value) + 1;
153     char *str = emalloc(size);
154     sprintf(str, "%s=%s", key, value);
155     if (putenv(str) != 0)
156         error("putenv(\"%s\") failed!", str);
157 #ifdef HAVE_COPYING_PUTENV
158     efree(str);
159 #endif
160 #endif
161 }
162 
163 /*
164  * Find absolute path to this program
165  */
166 
167 #ifdef __WIN32__
168 static char *
find_prog(char * origpath)169 find_prog(char *origpath)
170 {
171     wchar_t relpath[PMAX];
172     wchar_t abspath[PMAX];
173 
174     if (strlen(origpath) >= PMAX)
175         error("Path too long");
176 
177     MultiByteToWideChar(CP_UTF8, 0, origpath, -1, relpath, PMAX);
178 
179     if (wcsstr(relpath, LDIRSEPSTR) == NULL) {
180         /* Just a base name */
181 	int sz;
182         wchar_t *envpath;
183 	sz = GetEnvironmentVariableW(L"PATH", NULL, 0);
184         if (sz) {
185             /* Try to find the executable in the path */
186             wchar_t dir[PMAX];
187             wchar_t *beg;
188             wchar_t *end;
189 
190 	    HANDLE dir_handle;	        /* Handle to directory. */
191 	    wchar_t wildcard[PMAX];	/* Wildcard to search for. */
192 	    WIN32_FIND_DATAW find_data;	/* Data found by FindFirstFile() or FindNext(). */
193 
194             BOOL look_for_sep = TRUE;
195 
196 	    envpath = (wchar_t *) emalloc(sz * sizeof(wchar_t*));
197 	    GetEnvironmentVariableW(L"PATH", envpath, sz);
198 	    beg = envpath;
199 
200             while (look_for_sep) {
201                 end = wcsstr(beg, LPATHSEPSTR);
202                 if (end != NULL) {
203                     sz = end - beg;
204                 } else {
205                     sz = wcslen(beg);
206                     look_for_sep = FALSE;
207                 }
208                 if (sz >= PMAX) {
209                     beg = end + 1;
210                     continue;
211                 }
212                 wcsncpy(dir, beg, sz);
213                 dir[sz] = L'\0';
214                 beg = end + 1;
215 
216 		swprintf(wildcard, PMAX, L"%s" LDIRSEPSTR L"%s",
217 			      dir, relpath /* basename */);
218 		dir_handle = FindFirstFileW(wildcard, &find_data);
219 		if (dir_handle == INVALID_HANDLE_VALUE) {
220 		    /* Try next directory in path */
221 		    continue;
222 		} else {
223 		    /* Wow we found the executable. */
224 		    wcscpy(relpath, wildcard);
225 		    FindClose(dir_handle);
226 		    look_for_sep = FALSE;
227 		    break;
228 		}
229             }
230 	    efree(envpath);
231         }
232     }
233 
234     {
235 	DWORD size;
236 	wchar_t *absrest;
237 	size = GetFullPathNameW(relpath, PMAX, abspath, &absrest);
238 	if ((size == 0) || (size > PMAX)) {
239 	    /* Cannot determine absolute path to escript. Try the origin.  */
240 	    return strsave(origpath);
241 	} else {
242 	    char utf8abs[PMAX];
243 	    WideCharToMultiByte(CP_UTF8, 0, abspath, -1, utf8abs, PMAX, NULL, NULL);
244 	    return strsave(utf8abs);
245 	}
246     }
247 }
248 #else
249 static char *
find_prog(char * origpath)250 find_prog(char *origpath)
251 {
252     char relpath[PMAX];
253     char abspath[PMAX];
254 
255     if (strlen(origpath) >= sizeof(relpath))
256         error("Path too long");
257 
258     strcpy(relpath, origpath);
259 
260     if (strstr(relpath, DIRSEPSTR) == NULL) {
261         /* Just a base name */
262         char *envpath;
263 
264         envpath = get_env("PATH");
265         if (envpath) {
266             /* Try to find the executable in the path */
267             char dir[PMAX];
268             char *beg = envpath;
269             char *end;
270             int sz;
271 
272             DIR *dp;             /* Pointer to directory structure. */
273             struct dirent* dirp; /* Pointer to directory entry.     */
274 
275             BOOL look_for_sep = TRUE;
276 
277             while (look_for_sep) {
278                 end = strstr(beg, PATHSEPSTR);
279                 if (end != NULL) {
280                     sz = end - beg;
281                 } else {
282                     sz = strlen(beg);
283                     look_for_sep = FALSE;
284                 }
285                 if (sz >= sizeof(dir)) {
286                     beg = end + 1;
287                     continue;
288                 }
289                 strncpy(dir, beg, sz);
290                 dir[sz] = '\0';
291                 beg = end + 1;
292 
293                 dp = opendir(dir);
294                 if (dp != NULL) {
295                     while (TRUE) {
296                         dirp = readdir(dp);
297                         if (dirp == NULL) {
298                             closedir(dp);
299                             /* Try next directory in path */
300                             break;
301                         }
302 
303                         if (strcmp(origpath, dirp->d_name) == 0) {
304                             /* Wow we found the executable. */
305                             erts_snprintf(relpath, sizeof(relpath), "%s" DIRSEPSTR "%s",
306                                 dir, dirp->d_name);
307                             closedir(dp);
308 			    look_for_sep = FALSE;
309                             break;
310                         }
311                     }
312                 }
313             }
314         }
315     }
316 
317     {
318         if (!realpath(relpath, abspath)) {
319 	    /* Cannot determine absolute path to escript. Try the origin.  */
320 	    return strsave(origpath);
321 	} else {
322 	    return strsave(abspath);
323 	}
324     }
325 }
326 #endif
327 
328 static void
append_shebang_args(char * scriptname)329 append_shebang_args(char* scriptname)
330 {
331     /* Open script file */
332     FILE* fd;
333 #ifdef __WIN32__
334     wchar_t wcscriptname[PMAX];
335 
336     MultiByteToWideChar(CP_UTF8, 0, scriptname, -1, wcscriptname, PMAX);
337     fd = _wfopen(wcscriptname, L"r");
338 #else
339     fd = fopen (scriptname,"r");
340 #endif
341 
342     if (fd != NULL)	{
343 	/* Read first line in script file */
344 	static char linebuf[LINEBUFSZ];
345 	char* ptr = fgets(linebuf, LINEBUFSZ, fd);
346 
347 	if (ptr != NULL) {
348 	    /* Try to find args on second or third line */
349 	    ptr = fgets(linebuf, LINEBUFSZ, fd);
350 	    if (ptr != NULL && linebuf[0] == '%' && linebuf[1] == '%' && linebuf[2] == '!') {
351 		/* Use second line */
352 	    } else {
353 		/* Try third line */
354 		ptr = fgets(linebuf, LINEBUFSZ, fd);
355 		if (ptr != NULL && linebuf[0] == '%' && linebuf[1] == '%' && linebuf[2] == '!') {
356 		    /* Use third line */
357 		} else {
358 		    /* Do not use any line */
359 		    ptr = NULL;
360 		}
361 	    }
362 
363 	    if (ptr != NULL) {
364 		/* Use entire line but the leading chars */
365 		char* beg = linebuf + 3;
366 		char* end;
367 		BOOL newline = FALSE;
368 
369 		/* Push all args */
370 		while(beg && !newline) {
371 		    /* Skip leading spaces */
372 		    while (beg && beg[0] == ' ') {
373 			beg++;
374 		    }
375 
376 		    /* Find end of arg */
377 		    end = beg;
378 		    while (end && end < (linebuf+LINEBUFSZ-1) && end[0] != ' ') {
379 			if (end[0] == '\n') {
380 			    newline = TRUE;
381 			    end[0]= '\0';
382 			    break;
383 			} else {
384 			    end++;
385 			}
386 		    }
387 
388 		    /* Empty arg */
389 		    if (beg == end) {
390 			break;
391 		    }
392 		    end[0]= '\0';
393 		    PUSH(beg);
394 		    beg = end + 1;
395 		}
396 	    }
397 	}
398 	fclose(fd);
399     } else {
400 	error("Failed to open file: %s", scriptname);
401     }
402 }
403 
404 #ifdef __WIN32__
wmain(int argc,wchar_t ** wcargv)405 int wmain(int argc, wchar_t **wcargv)
406 {
407     char** argv;
408 #else
409 int
410 main(int argc, char** argv)
411 {
412 #endif
413     int eargv_size;
414     int eargc_base;		/* How many arguments in the base of eargv. */
415     char* emulator;
416     char* basename;
417     char* def_emu_lookup_path;
418     char scriptname[PMAX];
419     char** last_opt;
420     char** first_opt;
421 
422 #ifdef __WIN32__
423     int i;
424     int len;
425     /* Convert argv to utf8 */
426     argv = emalloc((argc+1) * sizeof(char*));
427     for (i=0; i<argc; i++) {
428 	len = WideCharToMultiByte(CP_UTF8, 0, wcargv[i], -1, NULL, 0, NULL, NULL);
429 	argv[i] = emalloc(len*sizeof(char));
430 	WideCharToMultiByte(CP_UTF8, 0, wcargv[i], -1, argv[i], len, NULL, NULL);
431     }
432     argv[argc] = NULL;
433 #endif
434 
435     /*
436      * Allocate the argv vector to be used for arguments to Erlang.
437      * Arrange for starting to pushing information in the middle of
438      * the array, to allow easy addition of commands in the beginning.
439      */
440 
441     eargv_size = argc*4+1000+LINEBUFSZ/2;
442     eargv_base = (char **) emalloc(eargv_size*sizeof(char*));
443     eargv = eargv_base;
444     eargc = 0;
445     eargc_base = eargc;
446     eargv = eargv + eargv_size/2;
447     eargc = 0;
448 
449     /* Determine basename of the executable */
450     for (basename = argv[0]+strlen(argv[0]);
451 	 basename > argv[0] && !(IS_DIRSEP(basename[-1]));
452 	 --basename)
453 	;
454 
455     first_opt = argv;
456     last_opt = argv;
457 
458 #ifdef __WIN32__
459     if ( (_stricmp(basename, "escript.exe") == 0)
460        ||(_stricmp(basename, "escript") == 0)) {
461 #else
462     if (strcmp(basename, "escript") == 0) {
463 #endif
464         def_emu_lookup_path = argv[0];
465 	/*
466 	 * Locate all options before the script name.
467 	 */
468 
469 	while (argc > 1 && argv[1][0] == '-') {
470 	    argc--;
471 	    argv++;
472 	    last_opt = argv;
473 	}
474 
475 	if (argc <= 1) {
476 	    error("Missing filename\n");
477 	}
478 	strncpy(scriptname, argv[1], sizeof(scriptname));
479 	scriptname[sizeof(scriptname)-1] = '\0';
480 	argc--;
481 	argv++;
482     } else {
483         char *absname = find_prog(argv[0]);
484 #ifdef __WIN32__
485 	int len = strlen(absname);
486 	if (len >= 4 && _stricmp(absname+len-4, ".exe") == 0) {
487 	    absname[len-4] = '\0';
488 	}
489 #endif
490 	erts_snprintf(scriptname, sizeof(scriptname), "%s.escript",
491 		      absname);
492         efree(absname);
493         def_emu_lookup_path = scriptname;
494     }
495 
496     /* Determine path to emulator */
497     emulator = get_env("ESCRIPT_EMULATOR");
498 
499     if (emulator == NULL) {
500 	emulator = get_default_emulator(def_emu_lookup_path);
501     }
502 
503     if (strlen(emulator) >= PMAX)
504         error("Value of environment variable ESCRIPT_EMULATOR is too large");
505 
506     /*
507      * Push initial arguments.
508      */
509 
510     PUSH(emulator);
511 
512     PUSH("+B");
513     PUSH2("-boot", "no_dot_erlang");
514     PUSH("-noshell");
515 
516     /*
517      * Read options from the %%! row in the script and add them as args
518      */
519 
520     append_shebang_args(scriptname);
521 
522     /*
523      * Push the script name and everything following it as extra arguments.
524      */
525 
526     PUSH3("-run", "escript", "start");
527 
528     /*
529      * Push all options before the script name. But omit the leading hyphens.
530      */
531 
532     while (first_opt != last_opt) {
533 	PUSH(first_opt[1]+1);
534 	first_opt++;
535     }
536 
537     PUSH("-extra");
538     PUSH(scriptname);
539     while (argc > 1) {
540 	PUSH(argv[1]);
541 	argc--, argv++;
542     }
543 
544     /*
545      * Move up the commands for invoking the emulator and adjust eargv
546      * accordingly.
547      */
548 
549     while (--eargc_base >= 0) {
550 	UNSHIFT(eargv_base[eargc_base]);
551     }
552 
553     /*
554      * Add scriptname to env
555      */
556     set_env("ESCRIPT_NAME", scriptname);
557 
558     /*
559      * Invoke Erlang with the collected options.
560      */
561 
562     PUSH(NULL);
563     return run_erlang(eargv[0], eargv);
564 }
565 
566 #ifdef __WIN32__
567 wchar_t *make_commandline(char **argv)
568 {
569     static wchar_t *buff = NULL;
570     static int siz = 0;
571     int num = 0, len;
572     char **arg;
573     wchar_t *p;
574 
575     if (*argv == NULL) {
576 	return L"";
577     }
578     for (arg = argv; *arg != NULL; ++arg) {
579 	num += strlen(*arg)+1;
580     }
581     if (!siz) {
582 	siz = num;
583 	buff = (wchar_t *) emalloc(siz*sizeof(wchar_t));
584     } else if (siz < num) {
585 	siz = num;
586 	buff = (wchar_t *) erealloc(buff,siz*sizeof(wchar_t));
587     }
588     p = buff;
589     num=0;
590     for (arg = argv; *arg != NULL; ++arg) {
591 	len = MultiByteToWideChar(CP_UTF8, 0, *arg, -1, p, siz);
592 	p+=(len-1);
593 	*p++=L' ';
594     }
595     *(--p) = L'\0';
596 
597     if (debug) {
598 	printf("Processed command line:%S\n",buff);
599     }
600     return buff;
601 }
602 
603 int my_spawnvp(char **argv)
604 {
605     STARTUPINFOW siStartInfo;
606     PROCESS_INFORMATION piProcInfo;
607     DWORD ec;
608 
609     memset(&siStartInfo,0,sizeof(STARTUPINFOW));
610     siStartInfo.cb = sizeof(STARTUPINFOW);
611     siStartInfo.dwFlags = STARTF_USESTDHANDLES;
612     siStartInfo.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
613     siStartInfo.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
614     siStartInfo.hStdError = GetStdHandle(STD_ERROR_HANDLE);
615 
616     if (!CreateProcessW(NULL,
617 			make_commandline(argv),
618 			NULL,
619 			NULL,
620 			TRUE,
621 			0,
622 			NULL,
623 			NULL,
624 			&siStartInfo,
625 			&piProcInfo)) {
626 	return -1;
627     }
628     CloseHandle(piProcInfo.hThread);
629 
630     WaitForSingleObject(piProcInfo.hProcess,INFINITE);
631     if (!GetExitCodeProcess(piProcInfo.hProcess,&ec)) {
632 	return 0;
633     }
634     return (int) ec;
635 }
636 #endif /* __WIN32__ */
637 
638 
639 static int
640 run_erlang(char* progname, char** argv)
641 {
642 #ifdef __WIN32__
643     int status;
644 #endif
645 
646     if (debug) {
647 	int i = 0;
648 	while (argv[i] != NULL)
649 	    printf(" %s", argv[i++]);
650 	printf("\n");
651     }
652 
653 #ifdef __WIN32__
654     /*
655      * Alas, we must wait here for the program to finish.
656      * Otherwise, the shell from which we was executed will think
657      * we are finished and print a prompt and read keyboard input.
658      */
659 
660     status = my_spawnvp(argv)/*_spawnvp(_P_WAIT,progname,argv)*/;
661     if (status == -1) {
662 	fprintf(stderr, "escript: Error executing '%s': %d", progname,
663 		GetLastError());
664     }
665     return status;
666 #else
667     execvp(progname, argv);
668     error("Error %d executing \'%s\'.", errno, progname);
669     return 2;
670 #endif
671 }
672 
673 static void
674 error(char* format, ...)
675 {
676     char sbuf[1024];
677     va_list ap;
678 
679     va_start(ap, format);
680     erts_vsnprintf(sbuf, sizeof(sbuf), format, ap);
681     va_end(ap);
682     fprintf(stderr, "escript: %s\n", sbuf);
683     exit(1);
684 }
685 
686 static void*
687 emalloc(size_t size)
688 {
689   void *p = malloc(size);
690   if (p == NULL)
691     error("Insufficient memory");
692   return p;
693 }
694 
695 #ifdef __WIN32__
696 static void *
697 erealloc(void *p, size_t size)
698 {
699     void *res = realloc(p, size);
700     if (res == NULL)
701     error("Insufficient memory");
702     return res;
703 }
704 #endif
705 
706 static void
707 efree(void *p)
708 {
709     free(p);
710 }
711 
712 static char*
713 strsave(char* string)
714 {
715     char* p = emalloc(strlen(string)+1);
716     strcpy(p, string);
717     return p;
718 }
719 
720 static int
721 file_exists(char *progname)
722 {
723 #ifdef __WIN32__
724     wchar_t wcsbuf[MAXPATHLEN];
725     MultiByteToWideChar(CP_UTF8, 0, progname, -1, wcsbuf, MAXPATHLEN);
726     return (_waccess(wcsbuf, 0) != -1);
727 #else
728     return (access(progname, 1) != -1);
729 #endif
730 }
731 
732 static char*
733 get_default_emulator(char* progname)
734 {
735     char sbuf[MAXPATHLEN];
736     char* s;
737 
738     if (strlen(progname) >= sizeof(sbuf))
739         return ERL_NAME;
740 
741     strcpy(sbuf, progname);
742     for (s = sbuf+strlen(sbuf); s >= sbuf; s--) {
743 	if (IS_DIRSEP(*s)) {
744 	    strcpy(s+1, ERL_NAME);
745 	    if(file_exists(sbuf))
746 		return strsave(sbuf);
747 	    strcpy(s+1, "bin" DIRSEPSTR ERL_NAME);
748 	    if(file_exists(sbuf))
749 		return strsave(sbuf);
750 	    break;
751 	}
752     }
753     return ERL_NAME;
754 }
755 
756 #ifdef __WIN32__
757 static char*
758 possibly_quote(char* arg)
759 {
760     int mustQuote = FALSE;
761     int n = 0;
762     char* s;
763     char* narg;
764 
765     if (arg == NULL) {
766 	return arg;
767     }
768 
769     /*
770      * Scan the string to find out if it needs quoting and return
771      * the original argument if not.
772      */
773 
774     for (s = arg; *s; s++, n++) {
775 	switch(*s) {
776 	case ' ':
777 	    mustQuote = TRUE;
778 	    continue;
779 	case '"':
780 	    mustQuote = TRUE;
781 	    n++;
782 	    continue;
783 	case '\\':
784 	    if(s[1] == '"')
785 		n++;
786 	    continue;
787 	default:
788 	    continue;
789 	}
790     }
791     if (!mustQuote) {
792 	return arg;
793     }
794 
795     /*
796      * Insert the quotes and put a backslash in front of every quote
797      * inside the string.
798      */
799 
800     s = narg = emalloc(n+2+1);
801     for (*s++ = '"'; *arg; arg++, s++) {
802 	if (*arg == '"' || (*arg == '\\' && arg[1] == '"')) {
803 	    *s++ = '\\';
804 	}
805 	*s = *arg;
806     }
807     if (s[-1] == '\\') {
808 	*s++ ='\\';
809     }
810     *s++ = '"';
811     *s = '\0';
812     return narg;
813 }
814 #endif /* __WIN32__ */
815