changeset 19236:9bfe8a6b9575

Initial revision
author Geoff Voelker <voelker@cs.washington.edu>
date Sat, 09 Aug 1997 02:53:48 +0000
parents 759e45894579
children 42cc2b7bc6c6
files nt/cmdproxy.c nt/debug.bat nt/ftime.bat
diffstat 3 files changed, 630 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nt/cmdproxy.c	Sat Aug 09 02:53:48 1997 +0000
@@ -0,0 +1,580 @@
+/* Proxy shell designed for use with Emacs on Windows 95 and NT.
+   Copyright (C) 1997 Free Software Foundation, Inc.
+
+   Accepts subset of Unix sh(1) command-line options, for compatability
+   with elisp code written for Unix.  When possible, executes external
+   programs directly (a common use of /bin/sh by Emacs), otherwise
+   invokes the user-specified command processor to handle built-in shell
+   commands, batch files and interactive mode.
+
+   The main function is simply to process the "-c string" option in the
+   way /bin/sh does, since the standard Windows command shells use the
+   convention that everything after "/c" (the Windows equivalent of
+   "-c") is the input string.
+
+This file is part of GNU Emacs.
+
+GNU Emacs is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Emacs is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Emacs; see the file COPYING.  If not, write to
+the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.  */
+
+#include <windows.h>
+
+#include <stdarg.h>  /* va_args */
+#include <malloc.h>  /* alloca */
+#include <stdlib.h>  /* getenv */
+#include <string.h>  /* strlen */
+
+
+/*******  Mock C library routines  *********************************/
+
+/* These routines are used primarily to minimize the executable size.  */
+
+#define stdin  GetStdHandle (STD_INPUT_HANDLE)
+#define stdout GetStdHandle (STD_OUTPUT_HANDLE)
+#define stderr GetStdHandle (STD_ERROR_HANDLE)
+
+int
+vfprintf(HANDLE hnd, char * msg, va_list args)
+{
+  DWORD bytes_written;
+  char buf[1024];
+
+  wvsprintf (buf, msg, args);
+  return WriteFile (hnd, buf, strlen (buf), &bytes_written, NULL);
+}
+
+int
+fprintf(HANDLE hnd, char * msg, ...)
+{
+  va_list args;
+  int rc;
+
+  va_start (args, msg);
+  rc = vfprintf (hnd, msg, args);
+  va_end (args);
+
+  return rc;
+}
+
+int
+printf(char * msg, ...)
+{
+  va_list args;
+  int rc;
+
+  va_start (args, msg);
+  rc = vfprintf (stdout, msg, args);
+  va_end (args);
+
+  return rc;
+}
+
+void
+fail (char * msg, ...)
+{
+  va_list args;
+
+  va_start (args, msg);
+  vfprintf (stderr, msg, args);
+  va_end (args);
+
+  exit (1);
+}
+
+void
+warn (char * msg, ...)
+{
+  va_list args;
+
+  va_start (args, msg);
+  vfprintf (stderr, msg, args);
+  va_end (args);
+}
+
+/******************************************************************/
+
+char *
+canon_filename (char *fname)
+{
+  char *p = fname;
+
+  while (*p)
+    {
+      if (*p == '/')
+	*p = '\\';
+      p++;
+    }
+
+  return fname;
+}
+
+char *
+skip_space (char *str)
+{
+  while (isspace (*str)) str++;
+  return str;
+}
+
+char *
+skip_nonspace (char *str)
+{
+  while (*str && !isspace (*str)) str++;
+  return str;
+}
+
+int escape_char = '\\';
+
+/* Get next token from input, advancing pointer.  */
+int
+get_next_token (char * buf, char ** pSrc)
+{
+  char * p = *pSrc;
+  char * o = buf;
+
+  p = skip_space (p);
+  if (*p == '"')
+    {
+      int escape_char_run = 0;
+
+      /* Go through src until an ending quote is found, unescaping
+	 quotes along the way.  If the escape char is not quote, then do
+	 special handling of multiple escape chars preceding a quote
+	 char (ie. the reverse of what Emacs does to escape quotes).  */
+      p++;
+      while (1)
+	{
+	  if (p[0] == escape_char && escape_char != '"')
+	    {
+	      escape_char_run++;
+	      continue;
+	    }
+	  else if (p[0] == '"')
+	    {
+	      while (escape_char_run > 1)
+		{
+		  *o++ = escape_char;
+		  escape_char_run -= 2;
+		}
+
+	      if (escape_char_run > 0)
+		{
+		  /* escaped quote */
+		  *o++ = *p++;
+		  escape_char_run = 0;
+		}
+	      else if (p[1] == escape_char && escape_char == '"')
+		{
+		  /* quote escaped by doubling */
+		  *o++ = *p;
+		  p += 2;
+		}
+	      else
+		{
+		  /* The ending quote.  */
+		  *o = '\0';
+		  /* Leave input pointer after token.  */
+		  p++;
+		  break;
+		}
+	    }
+	  else if (p[0] == '\0')
+	    {
+	      /* End of string, but no ending quote found.  We might want to
+		 flag this as an error, but for now will consider the end as
+		 the end of the token.  */
+	      *o = '\0';
+	      break;
+	    }
+	  else
+	    {
+	      *o++ = *p++;
+	    }
+	}
+    }
+  else
+    {
+      /* Next token is delimited by whitespace.  */
+      char * p1 = skip_nonspace (p);
+      memcpy (o, p, p1 - p);
+      o += (p1 - p);
+      p = p1;
+    }
+
+  *pSrc = p;
+
+  return o - buf;
+}
+
+/* Search for EXEC file in DIR.  If EXEC does not have an extension,
+   DIR is searched for EXEC with the standard extensions appended.  */
+int
+search_dir (char *dir, char *exec, int bufsize, char *buffer)
+{
+  char *exts[] = {".bat", ".cmd", ".exe", ".com"};
+  int n_exts = sizeof (exts) / sizeof (char *);
+  char *dummy;
+  int i, rc;
+
+  /* Search the directory for the program.  */
+  for (i = 0; i < n_exts; i++) 
+    {
+      rc = SearchPath (dir, exec, exts[i], bufsize, buffer, &dummy);
+      if (rc > 0)
+	return rc;
+    }
+
+  return 0;
+}
+
+/* Return the absolute name of executable file PROG, including 
+   any file extensions.  If an absolute name for PROG cannot be found,
+   return NULL.  */
+char *
+make_absolute (char *prog)
+{
+  char absname[MAX_PATH];
+  char dir[MAX_PATH];
+  char curdir[MAX_PATH];
+  char *p, *fname;
+  char *path;
+  int i;
+
+  /* At least partial absolute path specified; search there.  */
+  if ((isalpha (prog[0]) && prog[1] == ':') ||
+      (prog[0] == '\\'))
+    {
+      /* Split the directory from the filename.  */
+      fname = strrchr (prog, '\\');
+      if (!fname)
+	/* Only a drive specifier is given.  */
+	fname = prog + 2;
+      strncpy (dir, prog, fname - prog);
+      dir[fname - prog] = '\0';
+
+      /* Search the directory for the program.  */
+      if (search_dir (dir, prog, MAX_PATH, absname) > 0)
+	return strdup (absname);
+      else
+	return NULL;
+    }
+
+  if (GetCurrentDirectory (MAX_PATH, curdir) <= 0) 
+    return NULL;
+
+  /* Relative path; search in current dir. */
+  if (strpbrk (prog, "\\")) 
+    {
+      if (search_dir (curdir, prog, MAX_PATH, absname) > 0)
+	return strdup (absname);
+      else 
+	return NULL;
+    }
+  
+  /* Just filename; search current directory then PATH.  */
+  path = alloca (strlen (getenv ("PATH")) + strlen (curdir) + 2);
+  strcpy (path, curdir);
+  strcat (path, ";");
+  strcat (path, getenv ("PATH"));
+
+  while (*path)
+    {
+      /* Get next directory from path.  */
+      p = path;
+      while (*p && *p != ';') p++;
+      strncpy (dir, path, p - path);
+      dir[p - path] = '\0';
+
+      /* Search the directory for the program.  */
+      if (search_dir (dir, prog, MAX_PATH, absname) > 0)
+	return strdup (absname);
+
+      /* Move to the next directory.  */
+      path = p + 1;
+    } 
+
+  return NULL;
+}
+
+/*****************************************************************/
+
+#if 0
+char ** _argv;
+int     _argc;
+
+/* Parse commandline into argv array, allowing proper quoting of args.  */
+void
+setup_argv (void)
+{
+  char * cmdline = GetCommandLine ();
+  int arg_bytes = 0;
+
+  
+}
+#endif
+
+/* Information about child proc is global, to allow for automatic
+   termination when interrupted.  At the moment, only one child process
+   can be running at any one time.  */
+
+PROCESS_INFORMATION child;
+int interactive = TRUE;
+
+BOOL
+console_event_handler (DWORD event)
+{
+  switch (event)
+    {
+    case CTRL_C_EVENT:
+    case CTRL_BREAK_EVENT:
+      if (!interactive)
+	{
+	  /* Both command.com and cmd.exe have the annoying behaviour of
+	     prompting "Terminate batch job (y/n)?" when interrupted
+	     while running a batch file, even if running in
+	     non-interactive (-c) mode.  Try to make up for this
+	     deficiency by forcibly terminating the subprocess if
+	     running non-interactively.  */
+	  if (child.hProcess &&
+	      WaitForSingleObject (child.hProcess, 500) != WAIT_OBJECT_0)
+	    TerminateProcess (child.hProcess, 0);
+	  exit (STATUS_CONTROL_C_EXIT);
+	}
+      break;
+
+#if 0
+    default:
+      /* CLOSE, LOGOFF and SHUTDOWN events - actually we don't get these
+         under Windows 95.  */
+      fail ("cmdproxy: received %d event\n", event);
+      if (child.hProcess)
+	TerminateProcess (child.hProcess, 0);
+#endif
+    }
+  return TRUE;
+}
+
+int
+spawn (char * progname, char * cmdline)
+{
+  DWORD rc = 0xff;
+  SECURITY_ATTRIBUTES sec_attrs;
+  STARTUPINFO start;
+
+  sec_attrs.nLength = sizeof (sec_attrs);
+  sec_attrs.lpSecurityDescriptor = NULL;
+  sec_attrs.bInheritHandle = FALSE;
+  
+  memset (&start, 0, sizeof (start));
+  start.cb = sizeof (start);
+
+  if (CreateProcess (progname, cmdline, &sec_attrs, NULL, TRUE,
+		     0, NULL, NULL, &start, &child))
+  {
+    /* wait for completion and pass on return code */
+    WaitForSingleObject (child.hProcess, INFINITE);
+    GetExitCodeProcess (child.hProcess, &rc);
+    CloseHandle (child.hThread);
+    CloseHandle (child.hProcess);
+    child.hProcess = NULL;
+  }
+
+  return (int) rc;
+}
+
+/*******  Main program  ********************************************/
+
+int
+main (int argc, char ** argv)
+{
+  int rc;
+  int need_shell;
+  char * cmdline;
+  char * progname;
+  int envsize;
+  char modname[MAX_PATH];
+  char path[MAX_PATH];
+
+
+  interactive = TRUE;
+
+  SetConsoleCtrlHandler ((PHANDLER_ROUTINE) console_event_handler, TRUE);
+
+  /* We serve double duty: we can be called either as a proxy for the
+     real shell (that is, because we are defined to be the user shell),
+     or in our role as a helper application for running DOS programs.
+     In the former case, we interpret the command line options as if we
+     were a Unix shell, but in the latter case we simply pass our
+     command line to CreateProcess.  We know which case we are dealing
+     with by whether argv[0] refers to ourself or to some other program.
+     (This relies on an arcane feature of CreateProcess, where we can
+     specify cmdproxy as the module to run, but specify a different
+     program in the command line - the MSVC startup code sets argv[0]
+     from the command line.)  */
+
+  if (!GetModuleFileName (NULL, modname, sizeof (modname)))
+    fail ("GetModuleFileName failed");
+
+  /* Although Emacs always sets argv[0] to an absolute pathname, we
+     might get run in other ways as well, so convert argv[0] to an
+     absolute name before comparing to the module name.  */
+  if (!SearchPath (NULL, argv[0], ".exe", sizeof (path), path, &progname)
+      || stricmp (modname, path) != 0)
+    {
+      /* We are being used as a helper to run a DOS app; just pass
+	 command line to DOS app without change.  */
+      /* TODO: fill in progname.  */
+      return spawn (NULL, GetCommandLine ());
+    }
+
+  /* Process command line.  If running interactively (-c or /c not
+     specified) then spawn a real command shell, passing it the command
+     line arguments.
+
+     If not running interactively, then attempt to execute the specified
+     command directly.  If necessary, spawn a real shell to execute the
+     command.
+
+  */
+
+  progname = NULL;
+  cmdline = NULL;
+  /* If no args, spawn real shell for interactive use.  */
+  need_shell = TRUE;
+  interactive = TRUE;
+  /* Ask for a reasonable size environment for command.com.  */
+  envsize = 1024;
+
+  while (--argc > 0)
+    {
+      ++argv;
+      /* Only support single letter switches (except for -e); allow / as
+	 switch char for compatability with cmd.exe.  */
+      if ( ((*argv)[0] == '-' || (*argv)[0] == '/')
+	   && (*argv)[1] != '\0' && (*argv)[2] == '\0' )
+	{
+	  if ( ((*argv)[1] == 'c') && ((*argv)[2] == '\0')  )
+	    {
+	      if (--argc == 0)
+		fail ("error: expecting arg for %s", *argv);
+	      cmdline = *(++argv);
+	      interactive = FALSE;
+	    }
+	  else if ( ((*argv)[1] == 'i') && ((*argv)[2] == '\0')  )
+	    {
+	      if (cmdline)
+		warn ("warning: %s ignored because of -c", *argv);
+	    }
+	  else if ( ((*argv)[1] == 'e') && ((*argv)[2] == ':')  )
+	    {
+	      envsize = atoi (*argv + 3);
+	      /* Enforce a reasonable minimum size.  */
+	      if (envsize < 256)
+		envsize = 256;
+	    }
+	  else
+	    {
+	      warn ("warning: unknown option %s ignored", *argv);
+	    }
+	}
+      else
+	break;
+    }
+
+  /* If -c option, determine if we must spawn a real shell, or if we can
+     execute the command directly ourself.  */
+  if (cmdline)
+    {
+      /* If no redirection or piping, and if program can be found, then
+	 run program directly.  Otherwise invoke a real shell. */
+
+      static char copout_chars[] = "|<>&";
+
+      if (strpbrk (cmdline, copout_chars) == NULL)
+	{
+ 	  char *args;
+
+	  /* The program name is the first token of cmdline.  Since
+	     filenames cannot legally contain embedded quotes, the value
+	     of escape_char doesn't matter.  */
+	  args = cmdline;
+	  if (!get_next_token (path, &args))
+	    fail ("error: no program name specified.\n");
+
+	  canon_filename (path);
+	  progname = make_absolute (path);
+
+	  /* If we found the program, run it directly (if not found it
+             might be an internal shell command, so don't fail).  */
+	  if (progname != NULL)
+	    need_shell = FALSE;
+	}
+    }
+
+  if (need_shell)
+    {
+      char * p;
+
+      progname = getenv ("COMSPEC");
+      if (!progname)
+	fail ("error: COMSPEC is not set");
+
+      canon_filename (progname);
+      progname = make_absolute (progname);
+
+      if (progname == NULL || strchr (progname, '\\') == NULL)
+	fail ("make_absolute failed");
+
+      if (cmdline)
+	{
+	  /* Convert to syntax expected by cmd.exe/command.com for
+	     running non-interactively.  Always quote program name in
+	     case path contains spaces (fortunately it can't contain
+	     quotes, since they are illegal in path names).  */
+	  wsprintf (p = alloca (strlen (cmdline) + strlen (progname) + 7),
+		    "\"%s\" /c %s", progname, cmdline);
+	  cmdline = p;
+	}
+      else
+	{
+	  /* Provide dir arg expected by command.com when first started
+	     interactively (the "command search path").  cmd.exe does
+	     not require it, but accepts it silently - presumably other
+	     DOS compatible shells do the same.  To avoid potential
+	     problems with spaces in command dir (which cannot be quoted
+	     - command.com doesn't like it), we always use the 8.3 form.  */
+	  GetShortPathName (progname, path, sizeof (path));
+	  p = strrchr (path, '\\');
+	  /* Trailing slash is acceptable.  */
+	  p++;
+
+	  /* Set environment size - again cmd.exe ignores this silently.  */
+	  wsprintf (p, " /e:%d", envsize);
+
+	  /* Quote progname in case it contains spaces.  */
+	  wsprintf (cmdline = alloca (strlen (progname) + strlen (path) + 4),
+		    "\"%s\" %s", progname, path);
+	}
+    }
+
+  if (!progname)
+    fail ("Internal error: program name not defined\n");
+
+  if (!cmdline)
+    cmdline = progname;
+
+  rc = spawn (progname, cmdline);
+
+  return rc;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nt/debug.bat	Sat Aug 09 02:53:48 1997 +0000
@@ -0,0 +1,40 @@
+@echo off 
+set emacs_dir=c:\emacs
+
+REM Here begins emacs.bat.in
+
+REM Set OS specific values.
+set ARCH_SAVE=%PROCESSOR_ARCHITECTURE%
+set PROCESSOR_ARCHITECTURE=
+if "%ARCH_SAVE%" == "%PROCESSOR_ARCHITECTURE%" goto win95
+set PROCESSOR_ARCHITECTURE=%ARCH_SAVE%
+set SHELL=cmd
+goto next
+
+:win95
+set SHELL=command
+
+:next
+
+set EMACSLOADPATH=%emacs_dir%\lisp
+set EMACSDATA=%emacs_dir%\etc
+set EMACSPATH=%emacs_dir%\bin
+set EMACSLOCKDIR=%emacs_dir%\lock
+set INFOPATH=%emacs_dir%\info
+set EMACSDOC=%emacs_dir%\etc
+set TERM=CMD
+
+REM The variable HOME is used to find the startup file, ~\_emacs.  Ideally,
+REM this will not be set in this file but should already be set before
+REM this file is invoked.  If HOME is not set, use some generic default.
+
+set HOME_SAVE=%HOME%
+set HOME_EXISTS=yes
+set HOME_DEFAULT=C:\
+set HOME=
+if "%HOME%" == "%HOME_SAVE%" set HOME_EXISTS=no
+if "%HOME_EXISTS%" == "yes" set HOME=%HOME_SAVE%
+if "%HOME_EXISTS%" == "no" set HOME=%HOME_DEFAULT%
+if "%HOME_EXISTS%" == "no" echo HOME is not set!  Using %HOME% as a default...
+
+start c:\msdev\bin\msdev -nologo %emacs_dir%\bin\emacs.exe %1 %2 %3 %4 %5 %6 %7 %8 %9
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nt/ftime.bat	Sat Aug 09 02:53:48 1997 +0000
@@ -0,0 +1,10 @@
+@echo off
+set emacs_dir=c:\emacs
+prep /om /ft %emacs_dir%\src\obj\i386\emacs
+if errorlevel 1 goto done
+profile %emacs_dir%\src\obj\i386\emacs %1 %2 %3 %4 %5 %6 %7 %8 %9
+if errorlevel 1 goto done
+prep /m  %emacs_dir%\src\obj\i386\emacs
+if errorlevel 1 goto done
+plist  %emacs_dir%\src\obj\i386\emacs > info/emacs.prof
+:done