root/trunk/nt/cmdproxy.c

Revision 4220, 18.8 kB (checked in by miyoshi, 5 months ago)

Sync up with Emacs22.2.

  • Property svn:eol-style set to native
<
Line 
1 /* Proxy shell designed for use with Emacs on Windows 95 and NT.
2    Copyright (C) 1997, 2001, 2002, 2003, 2004, 2005,
3       2006, 2007, 2008  Free Software Foundation, Inc.
4
5    Accepts subset of Unix sh(1) command-line options, for compatability
6    with elisp code written for Unix.  When possible, executes external
7    programs directly (a common use of /bin/sh by Emacs), otherwise
8    invokes the user-specified command processor to handle built-in shell
9    commands, batch files and interactive mode.
10
11    The main function is simply to process the "-c string" option in the
12    way /bin/sh does, since the standard Windows command shells use the
13    convention that everything after "/c" (the Windows equivalent of
14    "-c") is the input string.
15
16 This file is part of GNU Emacs.
17
18 GNU Emacs is free software; you can redistribute it and/or modify
19 it under the terms of the GNU General Public License as published by
20 the Free Software Foundation; either version 3, or (at your option)
21 any later version.
22
23 GNU Emacs is distributed in the hope that it will be useful,
24 but WITHOUT ANY WARRANTY; without even the implied warranty of
25 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
26 GNU General Public License for more details.
27
28 You should have received a copy of the GNU General Public License
29 along with GNU Emacs; see the file COPYING.  If not, write to
30 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
31 Boston, MA 02110-1301, USA.  */
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
40
41 #ifdef MEADOW
42 /* alias old names */
43 #if _MSC_VER >= 1300
44 #define stricmp _stricmp
45 #define strdup _strdup
46 #endif
47 #endif
48
49 /*******  Mock C library routines  *********************************/
50
51 /* These routines are used primarily to minimize the executable size.  */
52
53 #define stdin  GetStdHandle (STD_INPUT_HANDLE)
54 #define stdout GetStdHandle (STD_OUTPUT_HANDLE)
55 #define stderr GetStdHandle (STD_ERROR_HANDLE)
56
57 int
58 vfprintf(HANDLE hnd, char * msg, va_list args)
59 {
60   DWORD bytes_written;
61   char buf[1024];
62
63   wvsprintf (buf, msg, args);
64   return WriteFile (hnd, buf, strlen (buf), &bytes_written, NULL);
65 }
66
67 int
68 fprintf(HANDLE hnd, char * msg, ...)
69 {
70   va_list args;
71   int rc;
72
73   va_start (args, msg);
74   rc = vfprintf (hnd, msg, args);
75   va_end (args);
76
77   return rc;
78 }
79
80 int
81 printf(char * msg, ...)
82 {
83   va_list args;
84   int rc;
85
86   va_start (args, msg);
87   rc = vfprintf (stdout, msg, args);
88   va_end (args);
89
90   return rc;
91 }
92
93 void
94 fail (char * msg, ...)
95 {
96   va_list args;
97
98   va_start (args, msg);
99   vfprintf (stderr, msg, args);
100   va_end (args);
101
102   exit (-1);
103 }
104
105 void
106 warn (char * msg, ...)
107 {
108   va_list args;
109
110   va_start (args, msg);
111   vfprintf (stderr, msg, args);
112   va_end (args);
113 }
114
115 /******************************************************************/
116
117 char *
118 canon_filename (char *fname)
119 {
120   char *p = fname;
121
122   while (*p)
123     {
124       if (*p == '/')
125         *p = '\\';
126       p++;
127     }
128
129   return fname;
130 }
131
132 char *
133 skip_space (char *str)
134 {
135   while (isspace (*str)) str++;
136   return str;
137 }
138
139 char *
140 skip_nonspace (char *str)
141 {
142   while (*str && !isspace (*str)) str++;
143   return str;
144 }
145
146 int escape_char = '\\';
147
148 /* Get next token from input, advancing pointer.  */
149 int
150 get_next_token (char * buf, char ** pSrc)
151 {
152   char * p = *pSrc;
153   char * o = buf;
154
155   p = skip_space (p);
156   if (*p == '"')
157     {
158       int escape_char_run = 0;
159
160       /* Go through src until an ending quote is found, unescaping
161          quotes along the way.  If the escape char is not quote, then do
162          special handling of multiple escape chars preceding a quote
163          char (ie. the reverse of what Emacs does to escape quotes).  */
164       p++;
165       while (1)
166         {
167           if (p[0] == escape_char && escape_char != '"')
168             {
169               escape_char_run++;
170               p++;
171               continue;
172             }
173           else if (p[0] == '"')
174             {
175               while (escape_char_run > 1)
176                 {
177                   *o++ = escape_char;
178                   escape_char_run -= 2;
179                 }
180
181               if (escape_char_run > 0)
182                 {
183                   /* escaped quote */
184                   *o++ = *p++;
185                   escape_char_run = 0;
186                 }
187               else if (p[1] == escape_char && escape_char == '"')
188                 {
189                   /* quote escaped by doubling */
190                   *o++ = *p;
191                   p += 2;
192                 }
193               else
194                 {
195                   /* The ending quote.  */
196                   *o = '\0';
197                   /* Leave input pointer after token.  */
198                   p++;
199                   break;
200                 }
201             }
202           else if (p[0] == '\0')
203             {
204               /* End of string, but no ending quote found.  We might want to
205                  flag this as an error, but for now will consider the end as
206                  the end of the token.  */
207               *o = '\0';
208               break;
209             }
210           else
211             {
212               *o++ = *p++;
213             }
214         }
215     }
216   else
217     {
218       /* Next token is delimited by whitespace.  */
219       char * p1 = skip_nonspace (p);
220       memcpy (o, p, p1 - p);
221       o += (p1 - p);
222       *o = '\0';
223       p = p1;
224     }
225
226   *pSrc = p;
227
228   return o - buf;
229 }
230
231 /* Search for EXEC file in DIR.  If EXEC does not have an extension,
232    DIR is searched for EXEC with the standard extensions appended.  */
233 int
234 search_dir (char *dir, char *exec, int bufsize, char *buffer)
235 {
236   char *exts[] = {".bat", ".cmd", ".exe", ".com"};
237   int n_exts = sizeof (exts) / sizeof (char *);
238   char *dummy;
239   int i, rc;
240
241   /* Search the directory for the program.  */
242   for (i = 0; i < n_exts; i++)
243     {
244       rc = SearchPath (dir, exec, exts[i], bufsize, buffer, &dummy);
245       if (rc > 0)
246         return rc;
247     }
248
249   return 0;
250 }
251
252 /* Return the absolute name of executable file PROG, including
253    any file extensions.  If an absolute name for PROG cannot be found,
254    return NULL.  */
255 char *
256 make_absolute (char *prog)
257 {
258   char absname[MAX_PATH];
259   char dir[MAX_PATH];
260   char curdir[MAX_PATH];
261   char *p, *fname;
262   char *path;
263   int i;
264
265   /* At least partial absolute path specified; search there.  */
266   if ((isalpha (prog[0]) && prog[1] == ':') ||
267       (prog[0] == '\\'))
268     {
269       /* Split the directory from the filename.  */
270       fname = strrchr (prog, '\\');
271       if (!fname)
272         /* Only a drive specifier is given.  */
273         fname = prog + 2;
274       strncpy (dir, prog, fname - prog);
275       dir[fname - prog] = '\0';
276
277       /* Search the directory for the program.  */
278       if (search_dir (dir, prog, MAX_PATH, absname) > 0)
279         return strdup (absname);
280       else
281         return NULL;
282     }
283
284   if (GetCurrentDirectory (MAX_PATH, curdir) <= 0)
285     return NULL;
286
287   /* Relative path; search in current dir. */
288   if (strpbrk (prog, "\\"))
289     {
290       if (search_dir (curdir, prog, MAX_PATH, absname) > 0)
291         return strdup (absname);
292       else
293         return NULL;
294     }
295
296   /* Just filename; search current directory then PATH.  */
297   path = alloca (strlen (getenv ("PATH")) + strlen (curdir) + 2);
298   strcpy (path, curdir);
299   strcat (path, ";");
300   strcat (path, getenv ("PATH"));
301
302   while (*path)
303     {
304       /* Get next directory from path.  */
305       p = path;
306       while (*p && *p != ';') p++;
307       strncpy (dir, path, p - path);
308       dir[p - path] = '\0';
309
310       /* Search the directory for the program.  */
311       if (search_dir (dir, prog, MAX_PATH, absname) > 0)
312         return strdup (absname);
313
314       /* Move to the next directory.  */
315       path = p + 1;
316     }
317
318   return NULL;
319 }
320
321 /*****************************************************************/
322
323 #if 0
324 char ** _argv;
325 int     _argc;
326
327 /* Parse commandline into argv array, allowing proper quoting of args.  */
328 void
329 setup_argv (void)
330 {
331   char * cmdline = GetCommandLine ();
332   int arg_bytes = 0;
333
334
335 }
336 #endif
337
338 /* Information about child proc is global, to allow for automatic
339    termination when interrupted.  At the moment, only one child process
340    can be running at any one time.  */
341
342 PROCESS_INFORMATION child;
343 int interactive = TRUE;
344
345 BOOL
346 console_event_handler (DWORD event)
347 {
348   switch (event)
349     {
350     case CTRL_C_EVENT:
351     case CTRL_BREAK_EVENT:
352       if (!interactive)
353         {
354           /* Both command.com and cmd.exe have the annoying behaviour of
355              prompting "Terminate batch job (y/n)?" when interrupted
356              while running a batch file, even if running in
357              non-interactive (-c) mode.  Try to make up for this
358              deficiency by forcibly terminating the subprocess if
359              running non-interactively.  */
360           if (child.hProcess &&
361               WaitForSingleObject (child.hProcess, 500) != WAIT_OBJECT_0)
362             TerminateProcess (child.hProcess, 0);
363           exit (STATUS_CONTROL_C_EXIT);
364         }
365       break;
366
367 #if 0
368     default:
369       /* CLOSE, LOGOFF and SHUTDOWN events - actually we don't get these
370          under Windows 95.  */
371       fail ("cmdproxy: received %d event\n", event);
372       if (child.hProcess)
373         TerminateProcess (child.hProcess, 0);
374 #endif
375     }
376   return TRUE;
377 }
378
379 /* Change from normal usage; return value indicates whether spawn
380    succeeded or failed - program return code is returned separately.  */
381 int
382 spawn (char * progname, char * cmdline, char * dir, int * retcode)
383 {
384   BOOL success = FALSE;
385   SECURITY_ATTRIBUTES sec_attrs;
386   STARTUPINFO start;
387   /* In theory, passing NULL for the environment block to CreateProcess
388      is the same as passing the value of GetEnvironmentStrings, but
389      doing this explicitly seems to cure problems running DOS programs
390      in some cases.  */
391   char * envblock = GetEnvironmentStrings ();
392
393   sec_attrs.nLength = sizeof (sec_attrs);
394   sec_attrs.lpSecurityDescriptor = NULL;
395   sec_attrs.bInheritHandle = FALSE;
396
397   memset (&start, 0, sizeof (start));
398   start.cb = sizeof (start);
399
400   if (CreateProcess (progname, cmdline, &sec_attrs, NULL, TRUE,
401                      0, envblock, dir, &start, &child))
402   {
403     success = TRUE;
404     /* wait for completion and pass on return code */
405     WaitForSingleObject (child.hProcess, INFINITE);
406     if (retcode)
407       GetExitCodeProcess (child.hProcess, (DWORD *)retcode);
408     CloseHandle (child.hThread);
409     CloseHandle (child.hProcess);
410     child.hProcess = NULL;
411   }
412
413   FreeEnvironmentStrings (envblock);
414
415   return success;
416 }
417
418 /* Return size of current environment block.  */
419 int
420 get_env_size ()
421 {
422   char * start = GetEnvironmentStrings ();
423   char * tmp = start;
424
425   while (tmp[0] || tmp[1])
426     ++tmp;
427   FreeEnvironmentStrings (start);
428   return  tmp + 2 - start;
429 }
430
431 /*******  Main program  ********************************************/
432
433 int
434 main (int argc, char ** argv)
435 {
436   int rc;
437   int need_shell;
438   char * cmdline;
439   char * progname;
440   int envsize;
441   char **pass_through_args;
442   int num_pass_through_args;
443   char modname[MAX_PATH];
444   char path[MAX_PATH];
445   char dir[MAX_PATH];
446
447
448   interactive = TRUE;
449
450   SetConsoleCtrlHandler ((PHANDLER_ROUTINE) console_event_handler, TRUE);
451
452   if (!GetCurrentDirectory (sizeof (dir), dir))
453     fail ("error: GetCurrentDirectory failed\n");
454
455   /* We serve double duty: we can be called either as a proxy for the
456      real shell (that is, because we are defined to be the user shell),
457      or in our role as a helper application for running DOS programs.
458      In the former case, we interpret the command line options as if we
459      were a Unix shell, but in the latter case we simply pass our
460      command line to CreateProcess.  We know which case we are dealing
461      with by whether argv[0] refers to ourself or to some other program.
462      (This relies on an arcane feature of CreateProcess, where we can
463      specify cmdproxy as the module to run, but specify a different
464      program in the command line - the MSVC startup code sets argv[0]
465      from the command line.)  */
466
467   if (!GetModuleFileName (NULL, modname, sizeof (modname)))
468     fail ("error: GetModuleFileName failed\n");
469
470   /* Change directory to location of .exe so startup directory can be
471      deleted.  */
472   progname = strrchr (modname, '\\');
473   *progname = '\0';
474   SetCurrentDirectory (modname);
475   *progname = '\\';
476
477   /* Due to problems with interaction between API functions that use "OEM"
478      codepage vs API functions that use the "ANSI" codepage, we need to
479      make things consistent by choosing one and sticking with it.  */
480   SetConsoleCP (GetACP());
481   SetConsoleOutputCP (GetACP());
482
483   /* Although Emacs always sets argv[0] to an absolute pathname, we
484      might get run in other ways as well, so convert argv[0] to an
485      absolute name before comparing to the module name.  Don't get
486      caught out by mixed short and long names.  */
487   GetShortPathName (modname, modname, sizeof (modname));
488   path[0] = '\0';
489   if (!SearchPath (NULL, argv[0], ".exe", sizeof (path), path, &progname)
490       || !GetShortPathName (path, path, sizeof (path))
491       || stricmp (modname, path) != 0)
492     {
493       /* We are being used as a helper to run a DOS app; just pass
494          command line to DOS app without change.  */
495       /* TODO: fill in progname.  */
496       if (spawn (NULL, GetCommandLine (), dir, &rc))
497         return rc;
498       fail ("Could not run %s\n", GetCommandLine ());
499     }
500
501   /* Process command line.  If running interactively (-c or /c not
502      specified) then spawn a real command shell, passing it the command
503      line arguments.
504
505      If not running interactively, then attempt to execute the specified
506      command directly.  If necessary, spawn a real shell to execute the
507      command.
508
509   */
510
511   progname = NULL;
512   cmdline = NULL;
513   /* If no args, spawn real shell for interactive use.  */
514   need_shell = TRUE;
515   interactive = TRUE;
516   /* Ask command.com to create an environment block with a reasonable
517      amount of free space.  */
518   envsize = get_env_size () + 300;
519   pass_through_args = (char **) alloca (argc * sizeof(char *));
520   num_pass_through_args = 0;
521
522   while (--argc > 0)
523     {
524       ++argv;
525       /* Act on switches we recognize (mostly single letter switches,
526          except for -e); all unrecognised switches and extra args are
527          passed on to real shell if used (only really of benefit for
528          interactive use, but allow for batch use as well).  Accept / as
529          switch char for compatability with cmd.exe.  */
530       if (((*argv)[0] == '-' || (*argv)[0] == '/') && (*argv)[1] != '\0')
531         {
532           if (((*argv)[1] == 'c' || (*argv)[1] == 'C') && ((*argv)[2] == '\0'))
533             {
534               if (--argc == 0)
535                 fail ("error: expecting arg for %s\n", *argv);
536               cmdline = *(++argv);
537               interactive = FALSE;
538             }
539           else if (((*argv)[1] == 'i' || (*argv)[1] == 'I') && ((*argv)[2] == '\0'))
540             {
541               if (cmdline)
542                 warn ("warning: %s ignored because of -c\n", *argv);
543             }
544           else if (((*argv)[1] == 'e' || (*argv)[1] == 'E') && ((*argv)[2] == ':'))
545             {
546               int requested_envsize = atoi (*argv + 3);
547               /* Enforce a reasonable minimum size, as above.  */
548               if (requested_envsize > envsize)
549                 envsize = requested_envsize;
550               /* For sanity, enforce a reasonable maximum.  */
551               if (envsize > 32768)
552                 envsize = 32768;
553             }
554           else
555             {
556               /* warn ("warning: unknown option %s ignored", *argv); */
557               pass_through_args[num_pass_through_args++] = *argv;
558             }
559         }
560       else
561         break;
562     }
563
564 #if 0
565   /* I think this is probably not useful - cmd.exe ignores extra
566      (non-switch) args in interactive mode, and they cannot be passed on
567      when -c was given.  */
568
569   /* Collect any remaining args after (initial) switches.  */
570   while (argc-- > 0)
571     {
572       pass_through_args[num_pass_through_args++] = *argv++;
573     }
574 #else
575   /* Probably a mistake for there to be extra args; not fatal.  */
576   if (argc > 0)
577     warn ("warning: extra args ignored after '%s'\n", argv[-1]);
578 #endif
579
580   pass_through_args[num_pass_through_args] = NULL;
581
582   /* If -c option, determine if we must spawn a real shell, or if we can
583      execute the command directly ourself.  */
584   if (cmdline)
585     {
586       /* If no redirection or piping, and if program can be found, then
587          run program directly.  Otherwise invoke a real shell. */
588
589       static char copout_chars[] = "|<>&";
590
591       if (strpbrk (cmdline, copout_chars) == NULL)
592         {
593           char *args;
594
595           /* The program name is the first token of cmdline.  Since
596              filenames cannot legally contain embedded quotes, the value
597              of escape_char doesn't matter.  */
598           args = cmdline;
599           if (!get_next_token (path, &args))
600             fail ("error: no program name specified.\n");
601
602           canon_filename (path);
603           progname = make_absolute (path);
604
605           /* If we found the program, run it directly (if not found it
606              might be an internal shell command, so don't fail).  */
607           if (progname != NULL)
608             need_shell = FALSE;
609         }
610     }
611
612  pass_to_shell:
613   if (need_shell)
614     {
615       char * p;
616       int    extra_arg_space = 0;