1 /* Proxy shell designed for use with Emacs on Windows 95 and NT.
2 Copyright (C) 1997, 2001-2015 Free Software Foundation, Inc.
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.
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.
15 This file is part of GNU Emacs.
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
20 (at your option) any later version.
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.
27 You should have received a copy of the GNU General Public License
28 along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */
32 #include <stdarg.h> /* va_args */
33 #include <malloc.h> /* alloca */
34 #include <stdlib.h> /* getenv */
35 #include <string.h> /* strlen */
36 #include <ctype.h> /* isspace, isalpha */
38 /* We don't want to include stdio.h because we are already duplicating
40 extern int _snprintf (char *buffer
, size_t count
, const char *format
, ...);
42 /******* Mock C library routines *********************************/
44 /* These routines are used primarily to minimize the executable size. */
46 #define stdout GetStdHandle (STD_OUTPUT_HANDLE)
47 #define stderr GetStdHandle (STD_ERROR_HANDLE)
50 vfprintf (HANDLE hnd
, const char * msg
, va_list args
)
55 wvsprintf (buf
, msg
, args
);
56 return WriteFile (hnd
, buf
, strlen (buf
), &bytes_written
, NULL
);
60 fprintf (HANDLE hnd
, const char * msg
, ...)
66 rc
= vfprintf (hnd
, msg
, args
);
73 printf (const char * msg
, ...)
79 rc
= vfprintf (stdout
, msg
, args
);
86 fail (const char * msg
, ...)
91 vfprintf (stderr
, msg
, args
);
98 warn (const char * msg
, ...)
102 va_start (args
, msg
);
103 vfprintf (stderr
, msg
, args
);
107 /******************************************************************/
110 canon_filename (char *fname
)
125 skip_space (const char *str
)
127 while (isspace (*str
)) str
++;
132 skip_nonspace (const char *str
)
134 while (*str
&& !isspace (*str
)) str
++;
138 /* This value is never changed by the code. We keep the code that
139 supports also the value of '"', but let's allow the compiler to
140 optimize it out, until someone actually uses that. */
141 const int escape_char
= '\\';
143 /* Get next token from input, advancing pointer. */
145 get_next_token (char * buf
, const char ** pSrc
)
147 const char * p
= *pSrc
;
153 int escape_char_run
= 0;
155 /* Go through src until an ending quote is found, unescaping
156 quotes along the way. If the escape char is not quote, then do
157 special handling of multiple escape chars preceding a quote
158 char (ie. the reverse of what Emacs does to escape quotes). */
162 if (p
[0] == escape_char
&& escape_char
!= '"')
168 else if (p
[0] == '"')
170 while (escape_char_run
> 1)
173 escape_char_run
-= 2;
176 if (escape_char_run
> 0)
182 else if (p
[1] == escape_char
&& escape_char
== '"')
184 /* quote escaped by doubling */
190 /* The ending quote. */
192 /* Leave input pointer after token. */
197 else if (p
[0] == '\0')
199 /* End of string, but no ending quote found. We might want to
200 flag this as an error, but for now will consider the end as
201 the end of the token. */
202 if (escape_char
== '\\')
204 /* Output literal backslashes. Note that if the
205 token ends with an unpaired backslash, we eat it
206 up here. But since this case invokes undefined
207 behavior anyway, it's okay. */
208 while (escape_char_run
> 1)
211 escape_char_run
-= 2;
219 if (escape_char
== '\\')
221 /* Output literal backslashes. Note that we don't
222 treat a backslash as an escape character here,
223 since it doesn't precede a quote. */
224 for ( ; escape_char_run
> 0; escape_char_run
--)
233 /* Next token is delimited by whitespace. */
234 const char * p1
= skip_nonspace (p
);
235 memcpy (o
, p
, p1
- p
);
246 /* Return TRUE if PROGNAME is a batch file. */
248 batch_file_p (const char *progname
)
250 const char *exts
[] = {".bat", ".cmd"};
251 int n_exts
= sizeof (exts
) / sizeof (char *);
254 const char *ext
= strrchr (progname
, '.');
258 for (i
= 0; i
< n_exts
; i
++)
260 if (stricmp (ext
, exts
[i
]) == 0)
268 /* Search for EXEC file in DIR. If EXEC does not have an extension,
269 DIR is searched for EXEC with the standard extensions appended. */
271 search_dir (const char *dir
, const char *exec
, int bufsize
, char *buffer
)
273 const char *exts
[] = {".bat", ".cmd", ".exe", ".com"};
274 int n_exts
= sizeof (exts
) / sizeof (char *);
277 const char *pext
= strrchr (exec
, '\\');
279 /* Does EXEC already include an extension? */
282 pext
= strchr (pext
, '.');
284 /* Search the directory for the program. */
287 /* SearchPath will not append an extension if the file already
288 has an extension, so we must append it ourselves. */
289 char exec_ext
[MAX_PATH
], *p
;
291 p
= strcpy (exec_ext
, exec
) + strlen (exec
);
293 /* Search first without any extension; if found, we are done. */
294 rc
= SearchPath (dir
, exec_ext
, NULL
, bufsize
, buffer
, &dummy
);
298 /* Try the known extensions. */
299 for (i
= 0; i
< n_exts
; i
++)
302 rc
= SearchPath (dir
, exec_ext
, NULL
, bufsize
, buffer
, &dummy
);
309 for (i
= 0; i
< n_exts
; i
++)
311 rc
= SearchPath (dir
, exec
, exts
[i
], bufsize
, buffer
, &dummy
);
320 /* Return the absolute name of executable file PROG, including
321 any file extensions. If an absolute name for PROG cannot be found,
324 make_absolute (const char *prog
)
326 char absname
[MAX_PATH
];
328 char curdir
[MAX_PATH
];
332 /* At least partial absolute path specified; search there. */
333 if ((isalpha (prog
[0]) && prog
[1] == ':') ||
336 /* Split the directory from the filename. */
337 fname
= strrchr (prog
, '\\');
339 /* Only a drive specifier is given. */
341 strncpy (dir
, prog
, fname
- prog
);
342 dir
[fname
- prog
] = '\0';
344 /* Search the directory for the program. */
345 if (search_dir (dir
, prog
, MAX_PATH
, absname
) > 0)
346 return strdup (absname
);
351 if (GetCurrentDirectory (MAX_PATH
, curdir
) <= 0)
354 /* Relative path; search in current dir. */
355 if (strpbrk (prog
, "\\"))
357 if (search_dir (curdir
, prog
, MAX_PATH
, absname
) > 0)
358 return strdup (absname
);
363 /* Just filename; search current directory then PATH. */
364 path
= alloca (strlen (getenv ("PATH")) + strlen (curdir
) + 2);
365 strcpy (path
, curdir
);
367 strcat (path
, getenv ("PATH"));
373 /* Get next directory from path. */
375 while (*p
&& *p
!= ';') p
++;
376 /* A broken PATH could have too long directory names in it. */
377 len
= min (p
- path
, sizeof (dir
) - 1);
378 strncpy (dir
, path
, len
);
381 /* Search the directory for the program. */
382 if (search_dir (dir
, prog
, MAX_PATH
, absname
) > 0)
383 return strdup (absname
);
385 /* Move to the next directory. */
392 /* Try to decode the given command line the way cmd would do it. On
393 success, return 1 with cmdline dequoted. Otherwise, when we've
394 found constructs only cmd can properly interpret, return 0 and
395 leave cmdline unchanged. */
397 try_dequote_cmdline (char* cmdline
)
399 /* Dequoting can only subtract characters, so the length of the
400 original command line is a bound on the amount of scratch space
401 we need. This length, in turn, is bounded by the 32k
402 CreateProcess limit. */
403 char * old_pos
= cmdline
;
404 char * new_cmdline
= alloca (strlen(cmdline
));
405 char * new_pos
= new_cmdline
;
414 while ((c
= *old_pos
++))
423 state
= INSIDE_QUOTE
;
432 /* We saw an unquoted shell metacharacter and we don't
433 understand it. Bail out. */
453 /* Variable substitution inside quote. Bail out. */
463 /* We were able to dequote the entire string. Copy our scratch
464 buffer on top of the original buffer and return success. */
465 memcpy (cmdline
, new_cmdline
, new_pos
- new_cmdline
);
466 cmdline
[new_pos
- new_cmdline
] = '\0';
470 /*****************************************************************/
476 /* Parse commandline into argv array, allowing proper quoting of args. */
480 char * cmdline
= GetCommandLine ();
487 /* Information about child proc is global, to allow for automatic
488 termination when interrupted. At the moment, only one child process
489 can be running at any one time. */
491 PROCESS_INFORMATION child
;
492 int interactive
= TRUE
;
495 console_event_handler (DWORD event
)
500 case CTRL_BREAK_EVENT
:
503 /* Both command.com and cmd.exe have the annoying behavior of
504 prompting "Terminate batch job (y/n)?" when interrupted
505 while running a batch file, even if running in
506 non-interactive (-c) mode. Try to make up for this
507 deficiency by forcibly terminating the subprocess if
508 running non-interactively. */
509 if (child
.hProcess
&&
510 WaitForSingleObject (child
.hProcess
, 500) != WAIT_OBJECT_0
)
511 TerminateProcess (child
.hProcess
, 0);
512 exit (STATUS_CONTROL_C_EXIT
);
518 /* CLOSE, LOGOFF and SHUTDOWN events - actually we don't get these
520 fail ("cmdproxy: received %d event\n", event
);
522 TerminateProcess (child
.hProcess
, 0);
528 /* Change from normal usage; return value indicates whether spawn
529 succeeded or failed - program return code is returned separately. */
531 spawn (const char *progname
, char *cmdline
, const char *dir
, int *retcode
)
533 BOOL success
= FALSE
;
534 SECURITY_ATTRIBUTES sec_attrs
;
536 /* In theory, passing NULL for the environment block to CreateProcess
537 is the same as passing the value of GetEnvironmentStrings, but
538 doing this explicitly seems to cure problems running DOS programs
540 char * envblock
= GetEnvironmentStrings ();
542 sec_attrs
.nLength
= sizeof (sec_attrs
);
543 sec_attrs
.lpSecurityDescriptor
= NULL
;
544 sec_attrs
.bInheritHandle
= FALSE
;
546 memset (&start
, 0, sizeof (start
));
547 start
.cb
= sizeof (start
);
549 /* CreateProcess handles batch files as progname specially. This
550 special handling fails when both the batch file and arguments are
551 quoted. We pass NULL as progname to avoid the special
553 if (progname
!= NULL
&& cmdline
[0] == '"' && batch_file_p (progname
))
556 if (CreateProcess (progname
, cmdline
, &sec_attrs
, NULL
, TRUE
,
557 0, envblock
, dir
, &start
, &child
))
560 /* wait for completion and pass on return code */
561 WaitForSingleObject (child
.hProcess
, INFINITE
);
563 GetExitCodeProcess (child
.hProcess
, (DWORD
*)retcode
);
564 CloseHandle (child
.hThread
);
565 CloseHandle (child
.hProcess
);
566 child
.hProcess
= NULL
;
569 FreeEnvironmentStrings (envblock
);
574 /* Return size of current environment block. */
578 char * start
= GetEnvironmentStrings ();
581 while (tmp
[0] || tmp
[1])
583 FreeEnvironmentStrings (start
);
584 return tmp
+ 2 - start
;
587 /******* Main program ********************************************/
590 main (int argc
, char ** argv
)
597 char **pass_through_args
;
598 int num_pass_through_args
;
599 char modname
[MAX_PATH
];
606 SetConsoleCtrlHandler ((PHANDLER_ROUTINE
) console_event_handler
, TRUE
);
608 if (!GetCurrentDirectory (sizeof (dir
), dir
))
609 fail ("error: GetCurrentDirectory failed\n");
611 /* We serve double duty: we can be called either as a proxy for the
612 real shell (that is, because we are defined to be the user shell),
613 or in our role as a helper application for running DOS programs.
614 In the former case, we interpret the command line options as if we
615 were a Unix shell, but in the latter case we simply pass our
616 command line to CreateProcess. We know which case we are dealing
617 with by whether argv[0] refers to ourself or to some other program.
618 (This relies on an arcane feature of CreateProcess, where we can
619 specify cmdproxy as the module to run, but specify a different
620 program in the command line - the MSVC startup code sets argv[0]
621 from the command line.) */
623 if (!GetModuleFileName (NULL
, modname
, sizeof (modname
)))
624 fail ("error: GetModuleFileName failed\n");
626 /* Change directory to location of .exe so startup directory can be
628 progname
= strrchr (modname
, '\\');
630 SetCurrentDirectory (modname
);
633 /* Due to problems with interaction between API functions that use "OEM"
634 codepage vs API functions that use the "ANSI" codepage, we need to
635 make things consistent by choosing one and sticking with it. */
636 SetConsoleCP (GetACP ());
637 SetConsoleOutputCP (GetACP ());
639 /* Although Emacs always sets argv[0] to an absolute pathname, we
640 might get run in other ways as well, so convert argv[0] to an
641 absolute name before comparing to the module name. */
643 /* The call to SearchPath will find argv[0] in the current
644 directory, append ".exe" to it if needed, and also canonicalize
645 it, to resolve references to ".", "..", etc. */
646 status
= SearchPath (NULL
, argv
[0], ".exe", sizeof (path
), path
,
648 if (!(status
> 0 && stricmp (modname
, path
) == 0))
654 /* Make sure we have argv[0] in path[], as the failed
655 SearchPath might not have copied it there. */
656 strcpy (path
, argv
[0]);
657 /* argv[0] could include forward slashes; convert them all
658 to backslashes, for strrchr calls below to DTRT. */
659 for (s
= path
; *s
; s
++)
663 /* Perhaps MODNAME and PATH use mixed short and long file names. */
664 if (!(GetShortPathName (modname
, modname
, sizeof (modname
))
665 && GetShortPathName (path
, path
, sizeof (path
))
666 && stricmp (modname
, path
) == 0))
668 /* Sometimes GetShortPathName fails because one or more
669 directories leading to argv[0] have issues with access
670 rights. In that case, at least we can compare the
671 basenames. Note: this disregards the improbable case of
672 invoking a program of the same name from another
673 directory, since the chances of that other executable to
674 be both our namesake and a 16-bit DOS application are nil. */
675 char *p
= strrchr (path
, '\\');
676 char *q
= strrchr (modname
, '\\');
680 p
= strchr (path
, ':');
686 q
= strchr (modname
, ':');
692 pdot
= strrchr (p
, '.');
693 if (!pdot
|| stricmp (pdot
, ".exe") != 0)
694 pdot
= p
+ strlen (p
);
695 qdot
= strrchr (q
, '.');
696 if (!qdot
|| stricmp (qdot
, ".exe") != 0)
697 qdot
= q
+ strlen (q
);
698 if (pdot
- p
!= qdot
- q
|| strnicmp (p
, q
, pdot
- p
) != 0)
700 /* We are being used as a helper to run a DOS app; just
701 pass command line to DOS app without change. */
702 /* TODO: fill in progname. */
703 if (spawn (NULL
, GetCommandLine (), dir
, &rc
))
705 fail ("Could not run %s\n", GetCommandLine ());
710 /* Process command line. If running interactively (-c or /c not
711 specified) then spawn a real command shell, passing it the command
714 If not running interactively, then attempt to execute the specified
715 command directly. If necessary, spawn a real shell to execute the
722 /* If no args, spawn real shell for interactive use. */
725 /* Ask command.com to create an environment block with a reasonable
726 amount of free space. */
727 envsize
= get_env_size () + 300;
728 pass_through_args
= (char **) alloca (argc
* sizeof (char *));
729 num_pass_through_args
= 0;
734 /* Act on switches we recognize (mostly single letter switches,
735 except for -e); all unrecognized switches and extra args are
736 passed on to real shell if used (only really of benefit for
737 interactive use, but allow for batch use as well). Accept / as
738 switch char for compatibility with cmd.exe. */
739 if (((*argv
)[0] == '-' || (*argv
)[0] == '/') && (*argv
)[1] != '\0')
741 if (((*argv
)[1] == 'c' || (*argv
)[1] == 'C') && ((*argv
)[2] == '\0'))
744 fail ("error: expecting arg for %s\n", *argv
);
748 else if (((*argv
)[1] == 'i' || (*argv
)[1] == 'I') && ((*argv
)[2] == '\0'))
751 warn ("warning: %s ignored because of -c\n", *argv
);
753 else if (((*argv
)[1] == 'e' || (*argv
)[1] == 'E') && ((*argv
)[2] == ':'))
755 int requested_envsize
= atoi (*argv
+ 3);
756 /* Enforce a reasonable minimum size, as above. */
757 if (requested_envsize
> envsize
)
758 envsize
= requested_envsize
;
759 /* For sanity, enforce a reasonable maximum. */
765 /* warn ("warning: unknown option %s ignored", *argv); */
766 pass_through_args
[num_pass_through_args
++] = *argv
;
774 /* I think this is probably not useful - cmd.exe ignores extra
775 (non-switch) args in interactive mode, and they cannot be passed on
776 when -c was given. */
778 /* Collect any remaining args after (initial) switches. */
781 pass_through_args
[num_pass_through_args
++] = *argv
++;
784 /* Probably a mistake for there to be extra args; not fatal. */
786 warn ("warning: extra args ignored after '%s'\n", argv
[-1]);
789 pass_through_args
[num_pass_through_args
] = NULL
;
791 /* If -c option, determine if we must spawn a real shell, or if we can
792 execute the command directly ourself. */
797 /* The program name is the first token of cmdline. Since
798 filenames cannot legally contain embedded quotes, the value
799 of escape_char doesn't matter. */
801 if (!get_next_token (path
, &args
))
802 fail ("error: no program name specified.\n");
804 canon_filename (path
);
805 progname
= make_absolute (path
);
807 /* If we found the program and the rest of the command line does
808 not contain unquoted shell metacharacters, run the program
809 directly (if not found it might be an internal shell command,
811 if (progname
!= NULL
&& try_dequote_cmdline (cmdline
))
821 int extra_arg_space
= 0;
823 int run_command_dot_com
;
825 progname
= getenv ("COMSPEC");
827 fail ("error: COMSPEC is not set\n");
829 canon_filename (progname
);
830 progname
= make_absolute (progname
);
832 if (progname
== NULL
|| strchr (progname
, '\\') == NULL
)
833 fail ("error: the program %s could not be found.\n", getenv ("COMSPEC"));
835 /* Need to set environment size when running command.com. */
836 run_command_dot_com
=
837 (stricmp (strrchr (progname
, '\\'), "command.com") == 0);
839 /* Work out how much extra space is required for
840 pass_through_args. */
841 for (argv
= pass_through_args
; *argv
!= NULL
; ++argv
)
842 /* We don't expect to have to quote switches. */
843 extra_arg_space
+= strlen (*argv
) + 2;
849 /* Convert to syntax expected by cmd.exe/command.com for
850 running non-interactively. Always quote program name in
851 case path contains spaces (fortunately it can't contain
852 quotes, since they are illegal in path names). */
855 strlen (progname
) + extra_arg_space
+ strlen (cmdline
) + 16 + 2;
856 buf
= p
= alloca (maxlen
+ 1);
858 /* Quote progname in case it contains spaces. */
859 p
+= _snprintf (p
, remlen
, "\"%s\"", progname
);
860 remlen
= maxlen
- (p
- buf
);
862 /* Include pass_through_args verbatim; these are just switches
863 so should not need quoting. */
864 for (argv
= pass_through_args
; *argv
!= NULL
; ++argv
)
866 p
+= _snprintf (p
, remlen
, " %s", *argv
);
867 remlen
= maxlen
- (p
- buf
);
870 /* Now that we know we will be invoking the shell, quote the
871 command line after the "/c" switch as the shell expects:
872 a single pair of quotes enclosing the entire command
873 tail, no matter whether quotes are used in the command
874 line, and how many of them are there. See the output of
875 "cmd /?" for how cmd.exe treats quotes. */
876 if (run_command_dot_com
)
877 _snprintf (p
, remlen
, " /e:%d /c \"%s\"", envsize
, cmdline
);
879 _snprintf (p
, remlen
, " /c \"%s\"", cmdline
);
884 if (run_command_dot_com
)
886 /* Provide dir arg expected by command.com when first
887 started interactively (the "command search path"). To
888 avoid potential problems with spaces in command dir
889 (which cannot be quoted - command.com doesn't like it),
890 we always use the 8.3 form. */
891 GetShortPathName (progname
, path
, sizeof (path
));
892 p
= strrchr (path
, '\\');
893 /* Trailing slash is acceptable, so always leave it. */
900 strlen (progname
) + extra_arg_space
+ strlen (path
) + 13;
901 cmdline
= p
= alloca (maxlen
+ 1);
903 /* Quote progname in case it contains spaces. */
904 p
+= _snprintf (p
, remlen
, "\"%s\" %s", progname
, path
);
905 remlen
= maxlen
- (p
- cmdline
);
907 /* Include pass_through_args verbatim; these are just switches
908 so should not need quoting. */
909 for (argv
= pass_through_args
; *argv
!= NULL
; ++argv
)
911 p
+= _snprintf (p
, remlen
, " %s", *argv
);
912 remlen
= maxlen
- (p
- cmdline
);
915 if (run_command_dot_com
)
916 _snprintf (p
, remlen
, " /e:%d", envsize
);
921 fail ("Internal error: program name not defined\n");
926 if (spawn (progname
, cmdline
, dir
, &rc
))
935 fail ("Could not run %s\n", progname
);