1 /* Proxy shell designed for use with Emacs on Windows 95 and NT.
2 Copyright (C) 1997, 2001-2023 Free Software Foundation, Inc.
3
4 Accepts subset of Unix sh(1) command-line options, for compatibility
5 with elisp code written for Unix. When possible, executes external
6 programs directly (a common use of /bin/sh by Emacs), otherwise
7 invokes the user-specified command processor to handle built-in shell
8 commands, batch files and interactive mode.
9
10 The main function is simply to process the "-c string" option in the
11 way /bin/sh does, since the standard Windows command shells use the
12 convention that everything after "/c" (the Windows equivalent of
13 "-c") is the input string.
14
15 This file is part of GNU Emacs.
16
17 GNU Emacs is free software: you can redistribute it and/or modify
18 it under the terms of the GNU General Public License as published by
19 the Free Software Foundation, either version 3 of the License, or (at
20 your option) any later version.
21
22 GNU Emacs is distributed in the hope that it will be useful,
23 but WITHOUT ANY WARRANTY; without even the implied warranty of
24 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 GNU General Public License for more details.
26
27 You should have received a copy of the GNU General Public License
28 along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
29
30 #define DEFER_MS_W32_H
31 #include <config.h>
32
33 #include <windows.h>
34
35 #include <stdarg.h> /* va_args */
36 #include <malloc.h> /* alloca */
37 #include <stdlib.h> /* getenv */
38 #include <string.h> /* strlen */
39 #include <ctype.h> /* isspace, isalpha */
40
41 /* We don't want to include stdio.h because we are already duplicating
42 lots of it here */
43 extern int _snprintf (char *buffer, size_t count, const char *format, ...);
44
45 /******* Mock C library routines *********************************/
46
47 /* These routines are used primarily to minimize the executable size. */
48
49 #define stdout GetStdHandle (STD_OUTPUT_HANDLE)
50 #define stderr GetStdHandle (STD_ERROR_HANDLE)
51
52 #if __GNUC__ + (__GNUC_MINOR__ >= 4) >= 5
53 void fail (const char *, ...) __attribute__((noreturn));
54 #else
55 void fail (const char *, ...);
56 #endif
57 int vfprintf (HANDLE, const char *, va_list);
58 int fprintf (HANDLE, const char *, ...);
59 int printf (const char *, ...);
60 void warn (const char *, ...);
61
62 int
63 vfprintf (HANDLE hnd, const char * msg, va_list args)
64 {
65 DWORD bytes_written;
66 char buf[1024];
67
68 wvsprintf (buf, msg, args);
69 return WriteFile (hnd, buf, strlen (buf), &bytes_written, NULL);
70 }
71
72 int
73 fprintf (HANDLE hnd, const char * msg, ...)
74 {
75 va_list args;
76 int rc;
77
78 va_start (args, msg);
79 rc = vfprintf (hnd, msg, args);
80 va_end (args);
81
82 return rc;
83 }
84
85 int
86 printf (const char * msg, ...)
87 {
88 va_list args;
89 int rc;
90
91 va_start (args, msg);
92 rc = vfprintf (stdout, msg, args);
93 va_end (args);
94
95 return rc;
96 }
97
98 void
99 fail (const char * msg, ...)
100 {
101 va_list args;
102
103 va_start (args, msg);
104 vfprintf (stderr, msg, args);
105 va_end (args);
106
107 exit (-1);
108 }
109
110 void
111 warn (const char * msg, ...)
112 {
113 va_list args;
114
115 va_start (args, msg);
116 vfprintf (stderr, msg, args);
117 va_end (args);
118 }
119
120 /******************************************************************/
121
122 static char *
123 canon_filename (char *fname)
124 {
125 char *p = fname;
126
127 while (*p)
128 {
129 if (*p == '/')
130 *p = '\\';
131 p++;
132 }
133
134 return fname;
135 }
136
137 static const char *
138 skip_space (const char *str)
139 {
140 while (isspace (*str)) str++;
141 return str;
142 }
143
144 static const char *
145 skip_nonspace (const char *str)
146 {
147 while (*str && !isspace (*str)) str++;
148 return str;
149 }
150
151 /* This value is never changed by the code. We keep the code that
152 supports also the value of '"', but let's allow the compiler to
153 optimize it out, until someone actually uses that. */
154 const int escape_char = '\\';
155
156 /* Get next token from input, advancing pointer. */
157 static int
158 get_next_token (char * buf, const char ** pSrc)
159 {
160 const char * p = *pSrc;
161 char * o = buf;
162
163 p = skip_space (p);
164 if (*p == '"')
165 {
166 int escape_char_run = 0;
167
168 /* Go through src until an ending quote is found, unescaping
169 quotes along the way. If the escape char is not quote, then do
170 special handling of multiple escape chars preceding a quote
171 char (ie. the reverse of what Emacs does to escape quotes). */
172 p++;
173 while (1)
174 {
175 if (p[0] == escape_char && escape_char != '"')
176 {
177 escape_char_run++;
178 p++;
179 continue;
180 }
181 else if (p[0] == '"')
182 {
183 while (escape_char_run > 1)
184 {
185 *o++ = escape_char;
186 escape_char_run -= 2;
187 }
188
189 if (escape_char_run > 0)
190 {
191 /* escaped quote */
192 *o++ = *p++;
193 escape_char_run = 0;
194 }
195 else if (p[1] == escape_char && escape_char == '"')
196 {
197 /* quote escaped by doubling */
198 *o++ = *p;
199 p += 2;
200 }
201 else
202 {
203 /* The ending quote. */
204 *o = '\0';
205 /* Leave input pointer after token. */
206 p++;
207 break;
208 }
209 }
210 else if (p[0] == '\0')
211 {
212 /* End of string, but no ending quote found. We might want to
213 flag this as an error, but for now will consider the end as
214 the end of the token. */
215 if (escape_char == '\\')
216 {
217 /* Output literal backslashes. Note that if the
218 token ends with an unpaired backslash, we eat it
219 up here. But since this case invokes undefined
220 behavior anyway, it's okay. */
221 while (escape_char_run > 1)
222 {
223 *o++ = escape_char;
224 escape_char_run -= 2;
225 }
226 }
227 *o = '\0';
228 break;
229 }
230 else
231 {
232 if (escape_char == '\\')
233 {
234 /* Output literal backslashes. Note that we don't
235 treat a backslash as an escape character here,
236 since it doesn't precede a quote. */
237 for ( ; escape_char_run > 0; escape_char_run--)
238 *o++ = escape_char;
239 }
240 *o++ = *p++;
241 }
242 }
243 }
244 else
245 {
246 /* Next token is delimited by whitespace. */
247 const char * p1 = skip_nonspace (p);
248 memcpy (o, p, p1 - p);
249 o += (p1 - p);
250 *o = '\0';
251 p = p1;
252 }
253
254 *pSrc = p;
255
256 return o - buf;
257 }
258
259 /* Return TRUE if PROGNAME is a batch file. */
260 static BOOL
261 batch_file_p (const char *progname)
262 {
263 const char *exts[] = {".bat", ".cmd"};
264 int n_exts = sizeof (exts) / sizeof (char *);
265 int i;
266
267 const char *ext = strrchr (progname, '.');
268
269 if (ext)
270 {
271 for (i = 0; i < n_exts; i++)
272 {
273 if (stricmp (ext, exts[i]) == 0)
274 return TRUE;
275 }
276 }
277
278 return FALSE;
279 }
280
281 /* Search for EXEC file in DIR. If EXEC does not have an extension,
282 DIR is searched for EXEC with the standard extensions appended. */
283 static int
284 search_dir (const char *dir, const char *exec, int bufsize, char *buffer)
285 {
286 const char *exts[] = {".bat", ".cmd", ".exe", ".com"};
287 int n_exts = sizeof (exts) / sizeof (char *);
288 char *dummy;
289 int i, rc;
290 const char *pext = strrchr (exec, '\\');
291
292 /* Does EXEC already include an extension? */
293 if (!pext)
294 pext = exec;
295 pext = strchr (pext, '.');
296
297 /* Search the directory for the program. */
298 if (pext)
299 {
300 /* SearchPath will not append an extension if the file already
301 has an extension, so we must append it ourselves. */
302 char exec_ext[MAX_PATH], *p;
303
304 p = strcpy (exec_ext, exec) + strlen (exec);
305
306 /* Search first without any extension; if found, we are done. */
307 rc = SearchPath (dir, exec_ext, NULL, bufsize, buffer, &dummy);
308 if (rc > 0)
309 return rc;
310
311 /* Try the known extensions. */
312 for (i = 0; i < n_exts; i++)
313 {
314 strcpy (p, exts[i]);
315 rc = SearchPath (dir, exec_ext, NULL, bufsize, buffer, &dummy);
316 if (rc > 0)
317 return rc;
318 }
319 }
320 else
321 {
322 for (i = 0; i < n_exts; i++)
323 {
324 rc = SearchPath (dir, exec, exts[i], bufsize, buffer, &dummy);
325 if (rc > 0)
326 return rc;
327 }
328 }
329
330 return 0;
331 }
332
333 /* Return the absolute name of executable file PROG, including
334 any file extensions. If an absolute name for PROG cannot be found,
335 return NULL. */
336 static char *
337 make_absolute (const char *prog)
338 {
339 char absname[MAX_PATH];
340 char dir[MAX_PATH];
341 char curdir[MAX_PATH];
342 char *p, *path;
343 const char *fname;
344
345 /* At least partial absolute path specified; search there. */
346 if ((isalpha (prog[0]) && prog[1] == ':') ||
347 (prog[0] == '\\'))
348 {
349 /* Split the directory from the filename. */
350 fname = strrchr (prog, '\\');
351 if (!fname)
352 /* Only a drive specifier is given. */
353 fname = prog + 2;
354 strncpy (dir, prog, fname - prog);
355 dir[fname - prog] = '\0';
356
357 /* Search the directory for the program. */
358 if (search_dir (dir, prog, MAX_PATH, absname) > 0)
359 return strdup (absname);
360 else
361 return NULL;
362 }
363
364 if (GetCurrentDirectory (MAX_PATH, curdir) <= 0)
365 return NULL;
366
367 /* Relative path; search in current dir. */
368 if (strpbrk (prog, "\\"))
369 {
370 if (search_dir (curdir, prog, MAX_PATH, absname) > 0)
371 return strdup (absname);
372 else
373 return NULL;
374 }
375
376 /* Just filename; search current directory then PATH. */
377 path = alloca (strlen (getenv ("PATH")) + strlen (curdir) + 2);
378 strcpy (path, curdir);
379 strcat (path, ";");
380 strcat (path, getenv ("PATH"));
381
382 while (*path)
383 {
384 size_t len;
385
386 /* Get next directory from path. */
387 p = path;
388 while (*p && *p != ';') p++;
389 /* A broken PATH could have too long directory names in it. */
390 len = min (p - path, sizeof (dir) - 1);
391 strncpy (dir, path, len);
392 dir[len] = '\0';
393
394 /* Search the directory for the program. */
395 if (search_dir (dir, prog, MAX_PATH, absname) > 0)
396 return strdup (absname);
397
398 /* Move to the next directory. */
399 path = p + 1;
400 }
401
402 return NULL;
403 }
404
405 /* Try to decode the given command line the way cmd would do it. On
406 success, return 1 with cmdline dequoted. Otherwise, when we've
407 found constructs only cmd can properly interpret, return 0 and
408 leave cmdline unchanged. */
409 static int
410 try_dequote_cmdline (char* cmdline)
411 {
412 /* Dequoting can only subtract characters, so the length of the
413 original command line is a bound on the amount of scratch space
414 we need. This length, in turn, is bounded by the 32k
415 CreateProcess limit. */
416 char * old_pos = cmdline;
417 char * new_cmdline = alloca (strlen(cmdline));
418 char * new_pos = new_cmdline;
419 char c;
420
421 enum {
422 NORMAL,
423 AFTER_CARET,
424 INSIDE_QUOTE
425 } state = NORMAL;
426
427 while ((c = *old_pos++))
428 {
429 switch (state)
430 {
431 case NORMAL:
432 switch(c)
433 {
434 case '"':
435 *new_pos++ = c;
436 state = INSIDE_QUOTE;
437 break;
438 case '^':
439 state = AFTER_CARET;
440 break;
441 case '<': case '>':
442 case '&': case '|':
443 case '(': case ')':
444 case '%': case '!':
445 /* We saw an unquoted shell metacharacter and we don't
446 understand it. Bail out. */
447 return 0;
448 default:
449 *new_pos++ = c;
450 break;
451 }
452 break;
453 case AFTER_CARET:
454 *new_pos++ = c;
455 state = NORMAL;
456 break;
457 case INSIDE_QUOTE:
458 switch (c)
459 {
460 case '"':
461 *new_pos++ = c;
462 state = NORMAL;
463 break;
464 case '%':
465 case '!':
466 /* Variable substitution inside quote. Bail out. */
467 return 0;
468 default:
469 *new_pos++ = c;
470 break;
471 }
472 break;
473 }
474 }
475
476 /* We were able to dequote the entire string. Copy our scratch
477 buffer on top of the original buffer and return success. */
478 memcpy (cmdline, new_cmdline, new_pos - new_cmdline);
479 cmdline[new_pos - new_cmdline] = '\0';
480 return 1;
481 }
482
483 /*****************************************************************/
484
485 #if 0
486 char ** _argv;
487 int _argc;
488
489 /* Parse commandline into argv array, allowing proper quoting of args. */
490 void
491 setup_argv (void)
492 {
493 char * cmdline = GetCommandLine ();
494 int arg_bytes = 0;
495
496
497 }
498 #endif
499
500 /* Information about child proc is global, to allow for automatic
501 termination when interrupted. At the moment, only one child process
502 can be running at any one time. */
503
504 PROCESS_INFORMATION child;
505 int interactive = TRUE;
506
507 BOOL console_event_handler (DWORD);
508
509 BOOL
510 console_event_handler (DWORD event)
511 {
512 switch (event)
513 {
514 case CTRL_C_EVENT:
515 case CTRL_BREAK_EVENT:
516 if (!interactive)
517 {
518 /* Both command.com and cmd.exe have the annoying behavior of
519 prompting "Terminate batch job (y/n)?" when interrupted
520 while running a batch file, even if running in
521 non-interactive (-c) mode. Try to make up for this
522 deficiency by forcibly terminating the subprocess if
523 running non-interactively. */
524 if (child.hProcess &&
525 WaitForSingleObject (child.hProcess, 500) != WAIT_OBJECT_0)
526 TerminateProcess (child.hProcess, 0);
527 exit (STATUS_CONTROL_C_EXIT);
528 }
529 break;
530
531 #if 0
532 default:
533 /* CLOSE, LOGOFF and SHUTDOWN events - actually we don't get these
534 under Windows 95. */
535 fail ("cmdproxy: received %d event\n", event);
536 if (child.hProcess)
537 TerminateProcess (child.hProcess, 0);
538 #endif
539 }
540 return TRUE;
541 }
542
543 /* Change from normal usage; return value indicates whether spawn
544 succeeded or failed - program return code is returned separately. */
545 static int
546 spawn (const char *progname, char *cmdline, const char *dir, int *retcode)
547 {
548 BOOL success = FALSE;
549 SECURITY_ATTRIBUTES sec_attrs;
550 STARTUPINFO start;
551 /* In theory, passing NULL for the environment block to CreateProcess
552 is the same as passing the value of GetEnvironmentStrings, but
553 doing this explicitly seems to cure problems running DOS programs
554 in some cases. */
555 char * envblock = GetEnvironmentStrings ();
556
557 sec_attrs.nLength = sizeof (sec_attrs);
558 sec_attrs.lpSecurityDescriptor = NULL;
559 sec_attrs.bInheritHandle = FALSE;
560
561 memset (&start, 0, sizeof (start));
562 start.cb = sizeof (start);
563
564 /* CreateProcess handles batch files as progname specially. This
565 special handling fails when both the batch file and arguments are
566 quoted. We pass NULL as progname to avoid the special
567 handling. */
568 if (progname != NULL && cmdline[0] == '"' && batch_file_p (progname))
569 progname = NULL;
570
571 if (CreateProcess (progname, cmdline, &sec_attrs, NULL, TRUE,
572 0, envblock, dir, &start, &child))
573 {
574 success = TRUE;
575 /* wait for completion and pass on return code */
576 WaitForSingleObject (child.hProcess, INFINITE);
577 if (retcode)
578 GetExitCodeProcess (child.hProcess, (DWORD *)retcode);
579 CloseHandle (child.hThread);
580 CloseHandle (child.hProcess);
581 child.hProcess = NULL;
582 }
583
584 FreeEnvironmentStrings (envblock);
585
586 return success;
587 }
588
589 /* Return size of current environment block. */
590 static int
591 get_env_size (void)
592 {
593 char * start = GetEnvironmentStrings ();
594 char * tmp = start;
595
596 while (tmp[0] || tmp[1])
597 ++tmp;
598 FreeEnvironmentStrings (start);
599 return tmp + 2 - start;
600 }
601
602 /******* Main program ********************************************/
603
604 int
605 main (int argc, char ** argv)
606 {
607 int rc;
608 int need_shell;
609 char * cmdline;
610 char * progname;
611 int envsize;
612 char **pass_through_args;
613 int num_pass_through_args;
614 char modname[MAX_PATH];
615 char path[MAX_PATH];
616 char dir[MAX_PATH];
617 int status;
618
619 interactive = TRUE;
620
621 SetConsoleCtrlHandler ((PHANDLER_ROUTINE) console_event_handler, TRUE);
622
623 if (!GetCurrentDirectory (sizeof (dir), dir))
624 fail ("error: GetCurrentDirectory failed\n");
625
626 /* We serve double duty: we can be called either as a proxy for the
627 real shell (that is, because we are defined to be the user shell),
628 or in our role as a helper application for running DOS programs.
629 In the former case, we interpret the command line options as if we
630 were a Unix shell, but in the latter case we simply pass our
631 command line to CreateProcess. We know which case we are dealing
632 with by whether argv[0] refers to ourself or to some other program.
633 (This relies on an arcane feature of CreateProcess, where we can
634 specify cmdproxy as the module to run, but specify a different
635 program in the command line - the MSVC startup code sets argv[0]
636 from the command line.) */
637
638 if (!GetModuleFileName (NULL, modname, sizeof (modname)))
639 fail ("error: GetModuleFileName failed\n");
640
641 /* Change directory to location of .exe so startup directory can be
642 deleted. */
643 progname = strrchr (modname, '\\');
644 *progname = '\0';
645 SetCurrentDirectory (modname);
646 *progname = '\\';
647
648 /* Due to problems with interaction between API functions that use "OEM"
649 codepage vs API functions that use the "ANSI" codepage, we need to
650 make things consistent by choosing one and sticking with it. */
651 SetConsoleCP (GetACP ());
652 SetConsoleOutputCP (GetACP ());
653
654 /* Although Emacs always sets argv[0] to an absolute pathname, we
655 might get run in other ways as well, so convert argv[0] to an
656 absolute name before comparing to the module name. */
657 path[0] = '\0';
658 /* The call to SearchPath will find argv[0] in the current
659 directory, append ".exe" to it if needed, and also canonicalize
660 it, to resolve references to ".", "..", etc. */
661 status = SearchPath (NULL, argv[0], ".exe", sizeof (path), path,
662 &progname);
663 if (!(status > 0 && stricmp (modname, path) == 0))
664 {
665 if (status <= 0)
666 {
667 char *s;
668
669 /* Make sure we have argv[0] in path[], as the failed
670 SearchPath might not have copied it there. */
671 strcpy (path, argv[0]);
672 /* argv[0] could include forward slashes; convert them all
673 to backslashes, for strrchr calls below to DTRT. */
674 for (s = path; *s; s++)
675 if (*s == '/')
676 *s = '\\';
677 }
678 /* Perhaps MODNAME and PATH use mixed short and long file names. */
679 if (!(GetShortPathName (modname, modname, sizeof (modname))
680 && GetShortPathName (path, path, sizeof (path))
681 && stricmp (modname, path) == 0))
682 {
683 /* Sometimes GetShortPathName fails because one or more
684 directories leading to argv[0] have issues with access
685 rights. In that case, at least we can compare the
686 basenames. Note: this disregards the improbable case of
687 invoking a program of the same name from another
688 directory, since the chances of that other executable to
689 be both our namesake and a 16-bit DOS application are nil. */
690 char *p = strrchr (path, '\\');
691 char *q = strrchr (modname, '\\');
692 char *pdot, *qdot;
693
694 if (!p)
695 p = strchr (path, ':');
696 if (!p)
697 p = path;
698 else
699 p++;
700 if (!q)
701 q = strchr (modname, ':');
702 if (!q)
703 q = modname;
704 else
705 q++;
706
707 pdot = strrchr (p, '.');
708 if (!pdot || stricmp (pdot, ".exe") != 0)
709 pdot = p + strlen (p);
710 qdot = strrchr (q, '.');
711 if (!qdot || stricmp (qdot, ".exe") != 0)
712 qdot = q + strlen (q);
713 if (pdot - p != qdot - q || strnicmp (p, q, pdot - p) != 0)
714 {
715 /* We are being used as a helper to run a DOS app; just
716 pass command line to DOS app without change. */
717 /* TODO: fill in progname. */
718 if (spawn (NULL, GetCommandLine (), dir, &rc))
719 return rc;
720 fail ("Could not run %s\n", GetCommandLine ());
721 }
722 }
723 }
724
725 /* Process command line. If running interactively (-c or /c not
726 specified) then spawn a real command shell, passing it the command
727 line arguments.
728
729 If not running interactively, then attempt to execute the specified
730 command directly. If necessary, spawn a real shell to execute the
731 command.
732
733 */
734
735 progname = NULL;
736 cmdline = NULL;
737 /* If no args, spawn real shell for interactive use. */
738 need_shell = TRUE;
739 interactive = TRUE;
740 /* Ask command.com to create an environment block with a reasonable
741 amount of free space. */
742 envsize = get_env_size () + 300;
743 pass_through_args = (char **) alloca (argc * sizeof (char *));
744 num_pass_through_args = 0;
745
746 while (--argc > 0)
747 {
748 ++argv;
749 /* Act on switches we recognize (mostly single letter switches,
750 except for -e); all unrecognized switches and extra args are
751 passed on to real shell if used (only really of benefit for
752 interactive use, but allow for batch use as well). Accept / as
753 switch char for compatibility with cmd.exe. */
754 if (((*argv)[0] == '-' || (*argv)[0] == '/') && (*argv)[1] != '\0')
755 {
756 if (((*argv)[1] == 'c' || (*argv)[1] == 'C') && ((*argv)[2] == '\0'))
757 {
758 if (--argc == 0)
759 fail ("error: expecting arg for %s\n", *argv);
760 cmdline = *(++argv);
761 interactive = FALSE;
762 }
763 else if (((*argv)[1] == 'i' || (*argv)[1] == 'I') && ((*argv)[2] == '\0'))
764 {
765 if (cmdline)
766 warn ("warning: %s ignored because of -c\n", *argv);
767 }
768 else if (((*argv)[1] == 'e' || (*argv)[1] == 'E') && ((*argv)[2] == ':'))
769 {
770 int requested_envsize = atoi (*argv + 3);
771 /* Enforce a reasonable minimum size, as above. */
772 if (requested_envsize > envsize)
773 envsize = requested_envsize;
774 /* For sanity, enforce a reasonable maximum. */
775 if (envsize > 32768)
776 envsize = 32768;
777 }
778 else
779 {
780 /* warn ("warning: unknown option %s ignored", *argv); */
781 pass_through_args[num_pass_through_args++] = *argv;
782 }
783 }
784 else
785 break;
786 }
787
788 #if 0
789 /* I think this is probably not useful - cmd.exe ignores extra
790 (non-switch) args in interactive mode, and they cannot be passed on
791 when -c was given. */
792
793 /* Collect any remaining args after (initial) switches. */
794 while (argc-- > 0)
795 {
796 pass_through_args[num_pass_through_args++] = *argv++;
797 }
798 #else
799 /* Probably a mistake for there to be extra args; not fatal. */
800 if (argc > 0)
801 warn ("warning: extra args ignored after '%s'\n", argv[-1]);
802 #endif
803
804 pass_through_args[num_pass_through_args] = NULL;
805
806 /* If -c option, determine if we must spawn a real shell, or if we can
807 execute the command directly ourself. */
808 if (cmdline)
809 {
810 const char *args;
811
812 /* The program name is the first token of cmdline. Since
813 filenames cannot legally contain embedded quotes, the value
814 of escape_char doesn't matter. */
815 args = cmdline;
816 if (!get_next_token (path, &args))
817 fail ("error: no program name specified.\n");
818
819 canon_filename (path);
820 progname = make_absolute (path);
821
822 /* If we found the program and the rest of the command line does
823 not contain unquoted shell metacharacters, run the program
824 directly (if not found it might be an internal shell command,
825 so don't fail). */
826 if (progname != NULL && try_dequote_cmdline (cmdline))
827 need_shell = FALSE;
828 else
829 progname = NULL;
830 }
831
832 pass_to_shell:
833 if (need_shell)
834 {
835 char * p;
836 int extra_arg_space = 0;
837 int maxlen, remlen;
838 int run_command_dot_com;
839
840 progname = getenv ("COMSPEC");
841 if (!progname)
842 fail ("error: COMSPEC is not set\n");
843
844 canon_filename (progname);
845 progname = make_absolute (progname);
846
847 if (progname == NULL || strchr (progname, '\\') == NULL)
848 fail ("error: the program %s could not be found.\n", getenv ("COMSPEC"));
849
850 /* Need to set environment size when running command.com. */
851 run_command_dot_com =
852 (stricmp (strrchr (progname, '\\'), "command.com") == 0);
853
854 /* Work out how much extra space is required for
855 pass_through_args. */
856 for (argv = pass_through_args; *argv != NULL; ++argv)
857 /* We don't expect to have to quote switches. */
858 extra_arg_space += strlen (*argv) + 2;
859
860 if (cmdline)
861 {
862 char * buf;
863
864 /* Convert to syntax expected by cmd.exe/command.com for
865 running non-interactively. Always quote program name in
866 case path contains spaces (fortunately it can't contain
867 quotes, since they are illegal in path names). */
868
869 remlen = maxlen =
870 strlen (progname) + extra_arg_space + strlen (cmdline) + 16 + 2;
871 buf = p = alloca (maxlen + 1);
872
873 /* Quote progname in case it contains spaces. */
874 p += _snprintf (p, remlen, "\"%s\"", progname);
875 remlen = maxlen - (p - buf);
876
877 /* Include pass_through_args verbatim; these are just switches
878 so should not need quoting. */
879 for (argv = pass_through_args; *argv != NULL; ++argv)
880 {
881 p += _snprintf (p, remlen, " %s", *argv);
882 remlen = maxlen - (p - buf);
883 }
884
885 /* Now that we know we will be invoking the shell, quote the
886 command line after the "/c" switch as the shell expects:
887 a single pair of quotes enclosing the entire command
888 tail, no matter whether quotes are used in the command
889 line, and how many of them are there. See the output of
890 "cmd /?" for how cmd.exe treats quotes. */
891 if (run_command_dot_com)
892 _snprintf (p, remlen, " /e:%d /c \"%s\"", envsize, cmdline);
893 else
894 _snprintf (p, remlen, " /c \"%s\"", cmdline);
895 cmdline = buf;
896 }
897 else
898 {
899 if (run_command_dot_com)
900 {
901 /* Provide dir arg expected by command.com when first
902 started interactively (the "command search path"). To
903 avoid potential problems with spaces in command dir
904 (which cannot be quoted - command.com doesn't like it),
905 we always use the 8.3 form. */
906 GetShortPathName (progname, path, sizeof (path));
907 p = strrchr (path, '\\');
908 /* Trailing slash is acceptable, so always leave it. */
909 *(++p) = '\0';
910 }
911 else
912 path[0] = '\0';
913
914 remlen = maxlen =
915 strlen (progname) + extra_arg_space + strlen (path) + 13;
916 cmdline = p = alloca (maxlen + 1);
917
918 /* Quote progname in case it contains spaces. */
919 p += _snprintf (p, remlen, "\"%s\" %s", progname, path);
920 remlen = maxlen - (p - cmdline);
921
922 /* Include pass_through_args verbatim; these are just switches
923 so should not need quoting. */
924 for (argv = pass_through_args; *argv != NULL; ++argv)
925 {
926 p += _snprintf (p, remlen, " %s", *argv);
927 remlen = maxlen - (p - cmdline);
928 }
929
930 if (run_command_dot_com)
931 _snprintf (p, remlen, " /e:%d", envsize);
932 }
933 }
934
935 if (!progname)
936 fail ("Internal error: program name not defined\n");
937
938 if (!cmdline)
939 cmdline = progname;
940
941 if (spawn (progname, cmdline, dir, &rc))
942 return rc;
943
944 if (!need_shell)
945 {
946 need_shell = TRUE;
947 goto pass_to_shell;
948 }
949
950 fail ("Could not run %s\n", progname);
951
952 return 0;
953 }