]> code.delx.au - gnu-emacs/blob - nt/cmdproxy.c
Remove App Nap setting from Info.plist (bug#22993)
[gnu-emacs] / nt / cmdproxy.c
1 /* Proxy shell designed for use with Emacs on Windows 95 and NT.
2 Copyright (C) 1997, 2001-2016 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 <http://www.gnu.org/licenses/>. */
29
30 #include <windows.h>
31
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 */
37
38 /* We don't want to include stdio.h because we are already duplicating
39 lots of it here */
40 extern int _snprintf (char *buffer, size_t count, const char *format, ...);
41
42 /******* Mock C library routines *********************************/
43
44 /* These routines are used primarily to minimize the executable size. */
45
46 #define stdout GetStdHandle (STD_OUTPUT_HANDLE)
47 #define stderr GetStdHandle (STD_ERROR_HANDLE)
48
49 int
50 vfprintf (HANDLE hnd, const char * msg, va_list args)
51 {
52 DWORD bytes_written;
53 char buf[1024];
54
55 wvsprintf (buf, msg, args);
56 return WriteFile (hnd, buf, strlen (buf), &bytes_written, NULL);
57 }
58
59 int
60 fprintf (HANDLE hnd, const char * msg, ...)
61 {
62 va_list args;
63 int rc;
64
65 va_start (args, msg);
66 rc = vfprintf (hnd, msg, args);
67 va_end (args);
68
69 return rc;
70 }
71
72 int
73 printf (const char * msg, ...)
74 {
75 va_list args;
76 int rc;
77
78 va_start (args, msg);
79 rc = vfprintf (stdout, msg, args);
80 va_end (args);
81
82 return rc;
83 }
84
85 void
86 fail (const char * msg, ...)
87 {
88 va_list args;
89
90 va_start (args, msg);
91 vfprintf (stderr, msg, args);
92 va_end (args);
93
94 exit (-1);
95 }
96
97 void
98 warn (const char * msg, ...)
99 {
100 va_list args;
101
102 va_start (args, msg);
103 vfprintf (stderr, msg, args);
104 va_end (args);
105 }
106
107 /******************************************************************/
108
109 char *
110 canon_filename (char *fname)
111 {
112 char *p = fname;
113
114 while (*p)
115 {
116 if (*p == '/')
117 *p = '\\';
118 p++;
119 }
120
121 return fname;
122 }
123
124 const char *
125 skip_space (const char *str)
126 {
127 while (isspace (*str)) str++;
128 return str;
129 }
130
131 const char *
132 skip_nonspace (const char *str)
133 {
134 while (*str && !isspace (*str)) str++;
135 return str;
136 }
137
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 = '\\';
142
143 /* Get next token from input, advancing pointer. */
144 int
145 get_next_token (char * buf, const char ** pSrc)
146 {
147 const char * p = *pSrc;
148 char * o = buf;
149
150 p = skip_space (p);
151 if (*p == '"')
152 {
153 int escape_char_run = 0;
154
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). */
159 p++;
160 while (1)
161 {
162 if (p[0] == escape_char && escape_char != '"')
163 {
164 escape_char_run++;
165 p++;
166 continue;
167 }
168 else if (p[0] == '"')
169 {
170 while (escape_char_run > 1)
171 {
172 *o++ = escape_char;
173 escape_char_run -= 2;
174 }
175
176 if (escape_char_run > 0)
177 {
178 /* escaped quote */
179 *o++ = *p++;
180 escape_char_run = 0;
181 }
182 else if (p[1] == escape_char && escape_char == '"')
183 {
184 /* quote escaped by doubling */
185 *o++ = *p;
186 p += 2;
187 }
188 else
189 {
190 /* The ending quote. */
191 *o = '\0';
192 /* Leave input pointer after token. */
193 p++;
194 break;
195 }
196 }
197 else if (p[0] == '\0')
198 {
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 == '\\')
203 {
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)
209 {
210 *o++ = escape_char;
211 escape_char_run -= 2;
212 }
213 }
214 *o = '\0';
215 break;
216 }
217 else
218 {
219 if (escape_char == '\\')
220 {
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--)
225 *o++ = escape_char;
226 }
227 *o++ = *p++;
228 }
229 }
230 }
231 else
232 {
233 /* Next token is delimited by whitespace. */
234 const char * p1 = skip_nonspace (p);
235 memcpy (o, p, p1 - p);
236 o += (p1 - p);
237 *o = '\0';
238 p = p1;
239 }
240
241 *pSrc = p;
242
243 return o - buf;
244 }
245
246 /* Return TRUE if PROGNAME is a batch file. */
247 BOOL
248 batch_file_p (const char *progname)
249 {
250 const char *exts[] = {".bat", ".cmd"};
251 int n_exts = sizeof (exts) / sizeof (char *);
252 int i;
253
254 const char *ext = strrchr (progname, '.');
255
256 if (ext)
257 {
258 for (i = 0; i < n_exts; i++)
259 {
260 if (stricmp (ext, exts[i]) == 0)
261 return TRUE;
262 }
263 }
264
265 return FALSE;
266 }
267
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. */
270 int
271 search_dir (const char *dir, const char *exec, int bufsize, char *buffer)
272 {
273 const char *exts[] = {".bat", ".cmd", ".exe", ".com"};
274 int n_exts = sizeof (exts) / sizeof (char *);
275 char *dummy;
276 int i, rc;
277 const char *pext = strrchr (exec, '\\');
278
279 /* Does EXEC already include an extension? */
280 if (!pext)
281 pext = exec;
282 pext = strchr (pext, '.');
283
284 /* Search the directory for the program. */
285 if (pext)
286 {
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;
290
291 p = strcpy (exec_ext, exec) + strlen (exec);
292
293 /* Search first without any extension; if found, we are done. */
294 rc = SearchPath (dir, exec_ext, NULL, bufsize, buffer, &dummy);
295 if (rc > 0)
296 return rc;
297
298 /* Try the known extensions. */
299 for (i = 0; i < n_exts; i++)
300 {
301 strcpy (p, exts[i]);
302 rc = SearchPath (dir, exec_ext, NULL, bufsize, buffer, &dummy);
303 if (rc > 0)
304 return rc;
305 }
306 }
307 else
308 {
309 for (i = 0; i < n_exts; i++)
310 {
311 rc = SearchPath (dir, exec, exts[i], bufsize, buffer, &dummy);
312 if (rc > 0)
313 return rc;
314 }
315 }
316
317 return 0;
318 }
319
320 /* Return the absolute name of executable file PROG, including
321 any file extensions. If an absolute name for PROG cannot be found,
322 return NULL. */
323 char *
324 make_absolute (const char *prog)
325 {
326 char absname[MAX_PATH];
327 char dir[MAX_PATH];
328 char curdir[MAX_PATH];
329 char *p, *path;
330 const char *fname;
331
332 /* At least partial absolute path specified; search there. */
333 if ((isalpha (prog[0]) && prog[1] == ':') ||
334 (prog[0] == '\\'))
335 {
336 /* Split the directory from the filename. */
337 fname = strrchr (prog, '\\');
338 if (!fname)
339 /* Only a drive specifier is given. */
340 fname = prog + 2;
341 strncpy (dir, prog, fname - prog);
342 dir[fname - prog] = '\0';
343
344 /* Search the directory for the program. */
345 if (search_dir (dir, prog, MAX_PATH, absname) > 0)
346 return strdup (absname);
347 else
348 return NULL;
349 }
350
351 if (GetCurrentDirectory (MAX_PATH, curdir) <= 0)
352 return NULL;
353
354 /* Relative path; search in current dir. */
355 if (strpbrk (prog, "\\"))
356 {
357 if (search_dir (curdir, prog, MAX_PATH, absname) > 0)
358 return strdup (absname);
359 else
360 return NULL;
361 }
362
363 /* Just filename; search current directory then PATH. */
364 path = alloca (strlen (getenv ("PATH")) + strlen (curdir) + 2);
365 strcpy (path, curdir);
366 strcat (path, ";");
367 strcat (path, getenv ("PATH"));
368
369 while (*path)
370 {
371 size_t len;
372
373 /* Get next directory from path. */
374 p = 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);
379 dir[len] = '\0';
380
381 /* Search the directory for the program. */
382 if (search_dir (dir, prog, MAX_PATH, absname) > 0)
383 return strdup (absname);
384
385 /* Move to the next directory. */
386 path = p + 1;
387 }
388
389 return NULL;
390 }
391
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. */
396 int
397 try_dequote_cmdline (char* cmdline)
398 {
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;
406 char c;
407
408 enum {
409 NORMAL,
410 AFTER_CARET,
411 INSIDE_QUOTE
412 } state = NORMAL;
413
414 while ((c = *old_pos++))
415 {
416 switch (state)
417 {
418 case NORMAL:
419 switch(c)
420 {
421 case '"':
422 *new_pos++ = c;
423 state = INSIDE_QUOTE;
424 break;
425 case '^':
426 state = AFTER_CARET;
427 break;
428 case '<': case '>':
429 case '&': case '|':
430 case '(': case ')':
431 case '%': case '!':
432 /* We saw an unquoted shell metacharacter and we don't
433 understand it. Bail out. */
434 return 0;
435 default:
436 *new_pos++ = c;
437 break;
438 }
439 break;
440 case AFTER_CARET:
441 *new_pos++ = c;
442 state = NORMAL;
443 break;
444 case INSIDE_QUOTE:
445 switch (c)
446 {
447 case '"':
448 *new_pos++ = c;
449 state = NORMAL;
450 break;
451 case '%':
452 case '!':
453 /* Variable substitution inside quote. Bail out. */
454 return 0;
455 default:
456 *new_pos++ = c;
457 break;
458 }
459 break;
460 }
461 }
462
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';
467 return 1;
468 }
469
470 /*****************************************************************/
471
472 #if 0
473 char ** _argv;
474 int _argc;
475
476 /* Parse commandline into argv array, allowing proper quoting of args. */
477 void
478 setup_argv (void)
479 {
480 char * cmdline = GetCommandLine ();
481 int arg_bytes = 0;
482
483
484 }
485 #endif
486
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. */
490
491 PROCESS_INFORMATION child;
492 int interactive = TRUE;
493
494 BOOL
495 console_event_handler (DWORD event)
496 {
497 switch (event)
498 {
499 case CTRL_C_EVENT:
500 case CTRL_BREAK_EVENT:
501 if (!interactive)
502 {
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);
513 }
514 break;
515
516 #if 0
517 default:
518 /* CLOSE, LOGOFF and SHUTDOWN events - actually we don't get these
519 under Windows 95. */
520 fail ("cmdproxy: received %d event\n", event);
521 if (child.hProcess)
522 TerminateProcess (child.hProcess, 0);
523 #endif
524 }
525 return TRUE;
526 }
527
528 /* Change from normal usage; return value indicates whether spawn
529 succeeded or failed - program return code is returned separately. */
530 int
531 spawn (const char *progname, char *cmdline, const char *dir, int *retcode)
532 {
533 BOOL success = FALSE;
534 SECURITY_ATTRIBUTES sec_attrs;
535 STARTUPINFO start;
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
539 in some cases. */
540 char * envblock = GetEnvironmentStrings ();
541
542 sec_attrs.nLength = sizeof (sec_attrs);
543 sec_attrs.lpSecurityDescriptor = NULL;
544 sec_attrs.bInheritHandle = FALSE;
545
546 memset (&start, 0, sizeof (start));
547 start.cb = sizeof (start);
548
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
552 handling. */
553 if (progname != NULL && cmdline[0] == '"' && batch_file_p (progname))
554 progname = NULL;
555
556 if (CreateProcess (progname, cmdline, &sec_attrs, NULL, TRUE,
557 0, envblock, dir, &start, &child))
558 {
559 success = TRUE;
560 /* wait for completion and pass on return code */
561 WaitForSingleObject (child.hProcess, INFINITE);
562 if (retcode)
563 GetExitCodeProcess (child.hProcess, (DWORD *)retcode);
564 CloseHandle (child.hThread);
565 CloseHandle (child.hProcess);
566 child.hProcess = NULL;
567 }
568
569 FreeEnvironmentStrings (envblock);
570
571 return success;
572 }
573
574 /* Return size of current environment block. */
575 int
576 get_env_size (void)
577 {
578 char * start = GetEnvironmentStrings ();
579 char * tmp = start;
580
581 while (tmp[0] || tmp[1])
582 ++tmp;
583 FreeEnvironmentStrings (start);
584 return tmp + 2 - start;
585 }
586
587 /******* Main program ********************************************/
588
589 int
590 main (int argc, char ** argv)
591 {
592 int rc;
593 int need_shell;
594 char * cmdline;
595 char * progname;
596 int envsize;
597 char **pass_through_args;
598 int num_pass_through_args;
599 char modname[MAX_PATH];
600 char path[MAX_PATH];
601 char dir[MAX_PATH];
602 int status;
603
604 interactive = TRUE;
605
606 SetConsoleCtrlHandler ((PHANDLER_ROUTINE) console_event_handler, TRUE);
607
608 if (!GetCurrentDirectory (sizeof (dir), dir))
609 fail ("error: GetCurrentDirectory failed\n");
610
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.) */
622
623 if (!GetModuleFileName (NULL, modname, sizeof (modname)))
624 fail ("error: GetModuleFileName failed\n");
625
626 /* Change directory to location of .exe so startup directory can be
627 deleted. */
628 progname = strrchr (modname, '\\');
629 *progname = '\0';
630 SetCurrentDirectory (modname);
631 *progname = '\\';
632
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 ());
638
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. */
642 path[0] = '\0';
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,
647 &progname);
648 if (!(status > 0 && stricmp (modname, path) == 0))
649 {
650 if (status <= 0)
651 {
652 char *s;
653
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++)
660 if (*s == '/')
661 *s = '\\';
662 }
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))
667 {
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, '\\');
677 char *pdot, *qdot;
678
679 if (!p)
680 p = strchr (path, ':');
681 if (!p)
682 p = path;
683 else
684 p++;
685 if (!q)
686 q = strchr (modname, ':');
687 if (!q)
688 q = modname;
689 else
690 q++;
691
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)
699 {
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))
704 return rc;
705 fail ("Could not run %s\n", GetCommandLine ());
706 }
707 }
708 }
709
710 /* Process command line. If running interactively (-c or /c not
711 specified) then spawn a real command shell, passing it the command
712 line arguments.
713
714 If not running interactively, then attempt to execute the specified
715 command directly. If necessary, spawn a real shell to execute the
716 command.
717
718 */
719
720 progname = NULL;
721 cmdline = NULL;
722 /* If no args, spawn real shell for interactive use. */
723 need_shell = TRUE;
724 interactive = TRUE;
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;
730
731 while (--argc > 0)
732 {
733 ++argv;
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')
740 {
741 if (((*argv)[1] == 'c' || (*argv)[1] == 'C') && ((*argv)[2] == '\0'))
742 {
743 if (--argc == 0)
744 fail ("error: expecting arg for %s\n", *argv);
745 cmdline = *(++argv);
746 interactive = FALSE;
747 }
748 else if (((*argv)[1] == 'i' || (*argv)[1] == 'I') && ((*argv)[2] == '\0'))
749 {
750 if (cmdline)
751 warn ("warning: %s ignored because of -c\n", *argv);
752 }
753 else if (((*argv)[1] == 'e' || (*argv)[1] == 'E') && ((*argv)[2] == ':'))
754 {
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. */
760 if (envsize > 32768)
761 envsize = 32768;
762 }
763 else
764 {
765 /* warn ("warning: unknown option %s ignored", *argv); */
766 pass_through_args[num_pass_through_args++] = *argv;
767 }
768 }
769 else
770 break;
771 }
772
773 #if 0
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. */
777
778 /* Collect any remaining args after (initial) switches. */
779 while (argc-- > 0)
780 {
781 pass_through_args[num_pass_through_args++] = *argv++;
782 }
783 #else
784 /* Probably a mistake for there to be extra args; not fatal. */
785 if (argc > 0)
786 warn ("warning: extra args ignored after '%s'\n", argv[-1]);
787 #endif
788
789 pass_through_args[num_pass_through_args] = NULL;
790
791 /* If -c option, determine if we must spawn a real shell, or if we can
792 execute the command directly ourself. */
793 if (cmdline)
794 {
795 const char *args;
796
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. */
800 args = cmdline;
801 if (!get_next_token (path, &args))
802 fail ("error: no program name specified.\n");
803
804 canon_filename (path);
805 progname = make_absolute (path);
806
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,
810 so don't fail). */
811 if (progname != NULL && try_dequote_cmdline (cmdline))
812 need_shell = FALSE;
813 else
814 progname = NULL;
815 }
816
817 pass_to_shell:
818 if (need_shell)
819 {
820 char * p;
821 int extra_arg_space = 0;
822 int maxlen, remlen;
823 int run_command_dot_com;
824
825 progname = getenv ("COMSPEC");
826 if (!progname)
827 fail ("error: COMSPEC is not set\n");
828
829 canon_filename (progname);
830 progname = make_absolute (progname);
831
832 if (progname == NULL || strchr (progname, '\\') == NULL)
833 fail ("error: the program %s could not be found.\n", getenv ("COMSPEC"));
834
835 /* Need to set environment size when running command.com. */
836 run_command_dot_com =
837 (stricmp (strrchr (progname, '\\'), "command.com") == 0);
838
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;
844
845 if (cmdline)
846 {
847 char * buf;
848
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). */
853
854 remlen = maxlen =
855 strlen (progname) + extra_arg_space + strlen (cmdline) + 16 + 2;
856 buf = p = alloca (maxlen + 1);
857
858 /* Quote progname in case it contains spaces. */
859 p += _snprintf (p, remlen, "\"%s\"", progname);
860 remlen = maxlen - (p - buf);
861
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)
865 {
866 p += _snprintf (p, remlen, " %s", *argv);
867 remlen = maxlen - (p - buf);
868 }
869
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);
878 else
879 _snprintf (p, remlen, " /c \"%s\"", cmdline);
880 cmdline = buf;
881 }
882 else
883 {
884 if (run_command_dot_com)
885 {
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. */
894 *(++p) = '\0';
895 }
896 else
897 path[0] = '\0';
898
899 remlen = maxlen =
900 strlen (progname) + extra_arg_space + strlen (path) + 13;
901 cmdline = p = alloca (maxlen + 1);
902
903 /* Quote progname in case it contains spaces. */
904 p += _snprintf (p, remlen, "\"%s\" %s", progname, path);
905 remlen = maxlen - (p - cmdline);
906
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)
910 {
911 p += _snprintf (p, remlen, " %s", *argv);
912 remlen = maxlen - (p - cmdline);
913 }
914
915 if (run_command_dot_com)
916 _snprintf (p, remlen, " /e:%d", envsize);
917 }
918 }
919
920 if (!progname)
921 fail ("Internal error: program name not defined\n");
922
923 if (!cmdline)
924 cmdline = progname;
925
926 if (spawn (progname, cmdline, dir, &rc))
927 return rc;
928
929 if (!need_shell)
930 {
931 need_shell = TRUE;
932 goto pass_to_shell;
933 }
934
935 fail ("Could not run %s\n", progname);
936
937 return 0;
938 }