/* run -- Wrapper program for console mode programs under Windows(TM)
 * Copyright (C) 1998  Charles S. Wilson
 *
 * This program 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
 * of the License, or (at your option) any later version.
 *
 * This program 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 this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */

/*
 * This program is based on the runemacs.c distributed with XEmacs 21.0
 *
 * Simple program to start gnu-win32 X11 programs (and native XEmacs) 
 * with its console window hidden.
 *
 * This program is provided purely for convenience, since most users will
 * use XEmacs in windowing (GUI) mode, and will not want to have an extra
 * console window lying around. Ditto for desktop shortcuts to gnu-win32 
 * X11 executables.
 */
#if HAVE_CONFIG_H
#  include "config.h"
#endif

#ifndef WIN32
#define WIN32
#endif

/* pull in WinXP function declarations, especially AttachConsole */
#define _WIN32_WINNT 0x0501

#include <windows.h>
#include <winnt.h>
#include <winuser.h>
#include <shellapi.h>
#include <string.h>
#include <malloc.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <process.h>
#include <errno.h>

#include "run.h"
#include "run2_gpl.h"
#include "tokenizer.h"
#include "util.h"
#include "env.h"

#if defined(__CYGWIN__)
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <sys/cygwin.h>
 #include <sys/unistd.h>
 #include <locale.h>
#else
 #include <direct.h>
#endif

/* Some versions of w32api have yet to define these */
#ifndef JOB_OBJECT_LIMIT_BREAKAWAY_OK
# define JOB_OBJECT_LIMIT_BREAKAWAY_OK        0x0800
#endif
#ifndef JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK
# define JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK 0x1000
#endif

int opt_nogui = 0;
int opt_notty = 0;
#if defined(DEBUG)
int opt_loglevel = RUN2_LOG_DEBUG;
#elif defined(SILENT)
int opt_loglevel = RUN2_DEFAULT_LOG_SILENT_LEVEL;
#else
int opt_loglevel = RUN2_DEFAULT_LOG_LEVEL;
#endif
int opt_wait = 0;
int opt_quote = 0;
int argv_is_malloc = 0;

static void
dumpOpts(void)
{
  debugMsg(1, "opt_loglevel: %d", opt_loglevel);
  debugMsg(1, "opt_nogui   : %d", opt_nogui);
  debugMsg(1, "opt_notty   : %d", opt_notty);
  debugMsg(1, "opt_wait    : %d", opt_wait);
  debugMsg(1, "opt_quote   : %d", opt_quote);
}

char buffer[1024];

#ifndef __CYGWIN__
int WINAPI
WinMain (HINSTANCE hSelf, HINSTANCE hPrev, LPSTR cmdline, int nShow)
{
  char **argv, **q;
  int argc;
  char *bufArg0;
  char* fullCmdLine;
  int rc, i;

  opt_nogui = !ENABLE_GUI;
  opt_notty = !ENABLE_TTY;
#if defined(DEBUG)
  opt_loglevel = RUN2_LOG_DEBUG;
#elif defined(SILENT)
  opt_loglevel = RUN2_DEFAULT_LOG_SILENT_LEVEL;
#else
  opt_loglevel = RUN2_DEFAULT_LOG_LEVEL;
#endif

  debugMsg(1, "orig cmdline = %s", cmdline);

#ifdef __CYGWIN__
  {
    wchar_t bufTemp[RUN2_PATHMAX+2];
    rc = GetModuleFileNameW (NULL, bufTemp, sizeof(bufTemp)-1);
    bufTemp[sizeof(bufTemp)-1] = L'\0'; /* 2k/XP don't null-terminate if buffer too small, AND returns incorrect rc */
    bufTemp[rc] = L'\0';
    bufArg0 = (char *) cygwin_create_path (CCP_WIN_W_TO_POSIX, bufTemp);
    if (!bufArg0)
    {
      errorMsg ("Unable to convert filename of this program to posix format: %s",
                strerror (errno));
      return 1;
    }
  }
#else
  {
    char bufTemp[RUN2_PATHMAX+2];
    rc = GetModuleFileNameA (NULL, bufTemp, sizeof(bufTemp)-1);
    bufTemp[sizeof(bufTemp)-1] = '\0'; /* 2k/XP don't null-terminate if buffer too small, AND returns incorrect rc */
    bufTemp[rc] = '\0';
    bufArg0 = run2_strdup (bufTemp);
  }
#endif

  fullCmdLine = (char*) run2_malloc(strlen(bufArg0) + strlen(cmdline) + 4);
  if (!fullCmdLine)
    {
      errorMsg ("out of memory");
      return 1;
    }

  {
    /* quote special chars in bufArg0 */
    char * quoteArg0 = run2_quote_strdup (bufArg0, 1);
    strcat(fullCmdLine, quoteArg0);
    strcat(fullCmdLine, cmdline);
    free (quoteArg0);
  }
  free (bufArg0);

  debugMsg(1, "new cmdline = %s", fullCmdLine);

  if ((rc = run2_split_string (fullCmdLine, &argc, (const char ***)&argv)) != 0)
    {
      errorMsg ("Could not create argv vector from |%s|", fullCmdLine);
      goto cleanup;
    }

  debugMsg(1, "argc = %d", argc);
  if(argv) {
    q = argv;
    for (q = argv; *q != NULL; q++)
      debugMsg(1, ": '%s'", *q);
  }

  for (i = 0; i < argc; i++) {
    debugMsg(1, "argv[%d]: '%s'", i, argv[i]);
  }
  argv_is_malloc = 1;
  rc = realMain(argc, argv);
cleanup:
  run2_freeargv(argv);

  infoMsg("Exiting with status %d", rc);
  /* note that this will kill the detached thread if it's still running */
  return rc;
}
#endif

#ifdef __CYGWIN__
int
main(int argc, char* argv[])
#else
static int
realMain(int argc, char* argv[])
#endif
{
   int compact_invocation = FALSE;
   int ret_code = 0;

   char execname[FILENAME_MAX];
   char execpath[MAX_PATH];
   int i,j;
   char exec[MAX_PATH + FILENAME_MAX + 100];
   char cmdline2[MAX_ARGS * MAX_PATH];
   int newargc;
   char **newargv;
   int save_default_log_level;

#ifdef __CYGWIN__
  opt_nogui = !ENABLE_GUI;
  opt_notty = !ENABLE_TTY;
# if defined(DEBUG)
  opt_loglevel = RUN2_LOG_DEBUG;
# elif defined(SILENT)
  opt_loglevel = RUN2_DEFAULT_LOG_SILENT_LEVEL;
# else
  opt_loglevel = RUN2_DEFAULT_LOG_LEVEL;
# endif

  setlocale(LC_ALL, "C");
#endif

  /* allow error messages during option parsing */
  run2_set_program_name(argv[0]);
  save_default_log_level = run2_get_verbose_level();
  run2_set_verbose_level(RUN2_DEFAULT_LOG_LEVEL);
  ret_code = parse_args(&argc, argv);
  run2_set_verbose_level (save_default_log_level);
  if (ret_code != 0)
    {
      ret_code = (ret_code < 0 ? -ret_code : ret_code);
      goto cleanup_realMain;
    }

  /* propagate options to utils */
  run2_set_tty_mode(!opt_notty);
  run2_set_gui_mode(!opt_nogui);
  run2_set_verbose_level(opt_loglevel);

  dumpOpts();

   compact_invocation = get_exec_name_and_path(execname,execpath);
   if (compact_invocation)
   {
      newargv = run2_dupargv (argv);
      newargc = argc;
      free (newargv[0]);
      newargv[0] = run2_strdup (execname);
   }
   else
   {
      newargv = run2_dupargv (argv);
      /* discard newargv[0] and shift up */
      free (newargv[0]);
      /* Account for NULL array terminator */
      for (newargc = 1; newargc <= argc; newargc++)
         newargv[newargc-1] = newargv[newargc];
      newargc = argc - 1;

      /* update execname */
      if (newargc >= 1)
      {
         if (newargv[0] && *newargv[0] == '"')
         {
            int l;
            strncpy(execname,newargv[0]+1,FILENAME_MAX);
            execname[FILENAME_MAX-1] = 0;
            l = strlen(execname);
            if (l > 0 && execname[l-1] == '"')
               execname[l-1] = 0;
         }
         else
         {
            strncpy(execname,newargv[0],FILENAME_MAX);
            execname[FILENAME_MAX-1] = 0;
         }
      }
   }
   /* at this point, execpath is defined, as are newargv[] and execname */
#ifdef DEBUG
   j = sprintf(buffer,"\nexecname : %s\nexecpath : %s\n",execname,execpath);
   for (i = 0; i < argc; i++)
      j += sprintf(buffer+j,"argv[%d]\t: %s\n",i,newargv[i]);
   debugMsg (1, buffer);
#endif

   if (execname == NULL || *execname == '\0')
      run2_error (EXIT_FAILURE, 0, "you must supply a program name to run");

#if defined(__CYGWIN__)
   /* this insures that we search for symlinks before .exe's */
   if (compact_invocation)
      run2_strip_exe(execname);
#endif

   process_execname(exec,execname,execpath);
   debugMsg (1, "exec\t%s\nexecname\t%s\nexecpath\t%s\n",
             exec,execname,execpath);

   build_cmdline(cmdline2,exec,newargc,newargv);
   debugMsg (1, cmdline2);

   xemacs_special(exec);
   if (run2_target_is_gui (exec))
     {
       /* much simpler if target is gui, because we don't
        * actually need to worry about consoles, stdio
        * handles, etc.  If -wait, then delegate to
	* spawnv/_spawnv -- since we have the argv array.
	* However, because _spawnv (_P_NOWAIT) doesn't work
	* reliably on cygwin, use a lobotomized version of
	* CreateProcess (but still don't worry about handles
	* or consoles).
        */
       run2_setup_win_environ();
       if (opt_wait)
         {
           debugMsg (1, "gui wait for child: %s", exec);
           /* ret_code is the child exit status for P_WAIT */
#if defined(__MINGW32__) || defined(_MSC_VER)
           ret_code = _spawnv (_P_WAIT, exec, (_SPAWNV_TYPE_ARG3) newargv);
#else
           ret_code = spawnv (_P_WAIT, exec, (SPAWNV_TYPE_ARG3) newargv);
#endif
           if (ret_code < 0)
             run2_error (EXIT_FAILURE, 0, "could not start %s", exec);
           return ret_code;
         }
       else
         {
           DWORD dwRetCode;
           STARTUPINFO start;
           PROCESS_INFORMATION child;
           JOBOBJECT_BASIC_LIMIT_INFORMATION jobinfo;
           DWORD create_flags = 0;

           debugMsg (1, "Launch GUI target, async: %s", cmdline2);
           ZeroMemory( &child, sizeof(PROCESS_INFORMATION) );
           ZeroMemory (&start, sizeof (STARTUPINFO));
           start.cb = sizeof (STARTUPINFO);

           if (QueryInformationJobObject (NULL, JobObjectBasicLimitInformation,
                                          &jobinfo, sizeof jobinfo, NULL)
              & (jobinfo.LimitFlags & (JOB_OBJECT_LIMIT_BREAKAWAY_OK
                                     | JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK)))
             create_flags |= CREATE_BREAKAWAY_FROM_JOB;

           dwRetCode = CreateProcess (NULL,
               cmdline2,/* command line                        */
               NULL,    /* process security attributes         */
               NULL,    /* primary thread security attributes  */
               FALSE,   /* handles are NOT inherited,          */
               create_flags, /* creation flags                 */
               NULL,    /* use parent's environment            */
               NULL,    /* use parent's current directory      */
               &start,  /* STARTUPINFO pointer                 */
               &child); /* receives PROCESS_INFORMATION        */
           if (dwRetCode == 0)
             {
               debugMsg (1, "getlasterror: %d\n", GetLastError());
               run2_error(EXIT_FAILURE, 0, "could not start %s", exec);
             }
           return 0;
         }
     }
   else
     {
       debugMsg (1, "Launch non-GUI target");
       ret_code = run2_start_child(cmdline2, NULL, opt_wait);
     }
   run2_freeargv (newargv);

cleanup_realMain:
#ifdef __CYGWIN__
   infoMsg ("Exiting with status %d", ret_code);
#else /* MinGW */
   debugMsg (1, "returning with status %d", ret_code);
#endif
   return ret_code;
}

static void
xemacs_special(char* exec)
{
  /*
   * if we're trying to run xemacs, AND this file was in %emacs_dir%\bin,
   * then set emacs_dir environment variable 
   */
   char* p;
   char* p2;
   char exec2[MAX_PATH + FILENAME_MAX + 100];
   char tmp[MAX_PATH + FILENAME_MAX + 100];
   strcpy(exec2,exec);
   /* this depends on short-circuit evaluation */
   if ( ((p = strrchr(exec2,'\\')) && strcasecmp(p,"\\xemacs") == 0) ||
        ((p = strrchr(exec2,'/')) && strcasecmp(p,"/xemacs") == 0) ||
        ((p = strrchr(exec2,'\\')) && strcasecmp(p,"\\xemacs.exe") == 0) ||
        ((p = strrchr(exec2,'/')) && strcasecmp(p,"/xemacs.exe") == 0) )
   {
      if ( ((p2 = strrchr(p, '\\')) && strcasecmp(p2, "\\bin") == 0) ||
           ((p2 = strrchr(p, '/')) && strcasecmp(p2, "/bin") == 0) )
      {
         *p2 = '\0';
#if defined(__CYGWIN__)
	 cygwin_conv_path (CCP_WIN_A_TO_POSIX | CCP_RELATIVE,
                           exec2, tmp, MAX_PATH + FILENAME_MAX + 100);
         strcpy(exec2,tmp);
#else /* NATIVE xemacs DOS-style paths with forward slashes */
         for (p = exec2; *p; p++)
            if (*p == '\\') *p = '/';
#endif
         SetEnvironmentVariable ("emacs_dir", exec2);
      }
   }
}

static void
remove_args(int *argc, char* argv[], int i)
{
  int j;
  /* remove argv[i] from argv array */
  if (argv_is_malloc) free (argv[i]);
  for (j = i+1; j < *argc; j++)
    argv[i++] = argv[j];
  argv[--*argc] = NULL;
}
static int
parse_args(int *argc, char* argv[])
{
   int i, j;
   /*
    * don't use getopt or other tools; manually parse because we
    * want to remove all items from argv[] that we recognize,
    * leaving the rest for the target app
    *
    * look for "-p ARG" and remove from arg vector
    * ditto: -wait, --run-nogui, --run-notty, --run-debug[=N] and
    * --run-verbose
    */
   for (i = 1; i < *argc; i++)
   {
      if (strcmp(argv[i], "-p") == 0)
      {
         int j = i;
         char * next = argv[i+1];
         if (next == NULL)
         {
            errorMsg("-p expects parameter");
            return -1;
         }
         /* if 'next' is quoted, remove them */
         if (*next == '"')
         {
            int l;
            next++;
            l = strlen (next);
            if (l > 0 && next[l-1] == '"')
               next[l-1] = '\0';
         }
         add_path (next);

         /* remove -p and its argument from argv */
	 remove_args(argc, argv, i);
	 remove_args(argc, argv, i--);
      }
      else if ((strcmp(argv[i],"--wait") == 0) ||
	       (strcmp(argv[i],"-wait")  == 0))
      {
         opt_wait = 1;
	 remove_args(argc, argv, i--);
      }
	       else if ((strcmp(argv[i],"--quote") == 0) ||
			(strcmp(argv[i],"-quote")  == 0))
      {
         opt_quote = 1;
	 remove_args(argc, argv, i--);
      }
#if (ENABLE_GUI == 1)
      else if (strcmp(argv[i],"--run-nogui") == 0)
      {
         opt_nogui = 1;
	 remove_args(argc, argv, i--);
      }
#endif
#if (ENABLE_TTY == 1)
      else if (strcmp(argv[i],"--run-notty") == 0)
      {
         opt_notty = 1;
	 remove_args(argc, argv, i--);
      }
#endif
      else if (strcmp(argv[i],"--run-verbose") == 0)
      {
         opt_loglevel = RUN2_DEFAULT_LOG_VERBOSE_LEVEL;
	 remove_args(argc, argv, i--);
      }
      else if (strncmp(argv[i],"--run-debug=", 12) == 0)
      {
         long val = 0;
         char* p = strchr(argv[i], '=');
         if (p != NULL)
         {
            if (*++p == '\0' || run2_strtol(p, &val) != 0)
            {
               errorMsg("bad value specified for --run-debug: %s", argv[i]);
               return -1;
            }
            if (val <= 0) opt_loglevel = RUN2_DEFAULT_LOG_VERBOSE_LEVEL;
            else opt_loglevel = (val-1) + RUN2_LOG_DEBUG;
         }
         else
         {
            opt_loglevel = RUN2_LOG_DEBUG;
         }
	 remove_args(argc, argv, i--);
      }
   }
   return 0;
}

static void
build_cmdline(char* new_cmdline, char* exec, int argc, char* argv[])
{
   int i;
   int char_cnt = 0;

   char_cnt = strlen(exec);
   for (i = 1; i < argc; i++)
     char_cnt += strlen(argv[i]);
   if (char_cnt > MAX_ARGS*MAX_PATH) /* then we ran out of room */
      run2_error(EXIT_FAILURE, 0, "command line too long -\n%s",new_cmdline);

   strcpy(new_cmdline,exec);
   for (i = 1; i < argc; i++)
   {
     char* quoteArgvi = run2_quote_strdup (argv[i], opt_quote);
     strcat(new_cmdline, " ");
     strcat(new_cmdline, quoteArgvi);
     free (quoteArgvi);
   }
}

/* process_execname : if execname
 * NATIVE:
 *  1) starts with '\\' or '/', it's a root-path and leave it alone
 *  2) starts with 'x:\\' or 'x:/', it's a root-path and leave it alone
 *  3) starts with '.\\' or './', two possible meanings:
 *       1) exec is in the current directory
 *       2) exec in same directory as this program
 *  4) otherwise, search path (and _prepend_ "." to the path!!!)
 *  5) convert all '/' to '\\'
 * CYGWIN
 *  1) starts with '\\' or '/', it's a root-path and leave it alone
 *  2) starts with 'x:\\' or 'x:/', it's a root-path and leave it alone
 *  3) starts with '.\\' or './', two possible meanings:
 *       1) exec is in the current directory
 *       2) exec in same directory as this program
 *  4) otherwise, search path (and _prepend_ "." to the path!!!)
 *  5) convert to cygwin-style path to resolve symlinks within the pathspec
 *  6) check filename: if it's a symlink, resolve it by peeking inside
 *  7) convert to win32-style path+filename since we're using Windows 
 *       createProcess() to launch
 */
static void
process_execname(char *exec, const char* execname,const char* execpath )
{
   char* orig_pathlist;
   char* pathlist;
   char exec_tmp[MAX_PATH + FILENAME_MAX + 100];
   char exec_tmp2[MAX_PATH + FILENAME_MAX + 100];
   char buf[MAX_PATH + FILENAME_MAX + 100];
   int i,j;

   int len = 0;
   /* 
    * STARTS WITH / or \ 
    * execpath NOT used
    */
   if ((execname[0] == '\\') || (execname[0] == '/'))
   {
#if defined(__CYGWIN__)
      strcpy(exec_tmp,execname);
#else
      exec_tmp[0] = ((char) (_getdrive() + ((int) 'A') - 1));
      exec_tmp[1] = ':';
      exec_tmp[2] = '\0';
      strcat(exec_tmp,execname);
#endif
      debugMsg (1, "/ -\nexec_tmp\t%s\nexecname\t%s\nexecpath\t%s\n",
                exec_tmp,execname,execpath);
      if (! file_exists_multi(exec_tmp2,NULL,exec_tmp,exts,NUM_EXTENSIONS) )
      {
          j = 0;
          for (i = 0; i < NUM_EXTENSIONS; i++)
              j += sprintf(buf + j," [%d]: %s\n",i+1,exts[i]);
          run2_error(EXIT_FAILURE, 0,
                "Couldn't locate %s\nI tried appending the following "
                "extensions: \n%s",exec_tmp,buf);
      }
      debugMsg (1, exec_tmp2);
   }
   /* 
    * STARTS WITH x:\ or x:/
    * execpath NOT used
    */
    else if ((strlen(execname) > 3) && /* avoid boundary errors */
       (execname[1] == ':') &&
       ((execname[2] == '\\') || (execname[2] == '/')))
   {
      strcpy(exec_tmp,execname);
      debugMsg (1,"x: -\nexec_tmp\t%s\nexecname\t%s\nexecpath\t%s\n",
                exec_tmp,execname,execpath);
      if (! file_exists_multi(exec_tmp2,NULL,exec_tmp,exts,NUM_EXTENSIONS) )
      {
          j = 0;
          for (i = 0; i < NUM_EXTENSIONS; i++)
              j += sprintf(buf + j," [%d]: %s\n",i+1,exts[i]);
          run2_error(EXIT_FAILURE, 0,
                "Couldn't locate %s\nI tried appending the following "
                "extensions: \n%s",exec_tmp,buf);
      }
      debugMsg (1, exec_tmp2);
   }
   /* 
    * STARTS WITH ./ or .\
    */
   else if ((execname[0] == '.') &&
            ((execname[1] == '\\') || (execname[1] == '/')))
   {
      if (((char*) getcwd(exec_tmp,MAX_PATH))==NULL)
         run2_error(EXIT_FAILURE, 0, "can't find current working directory");
      if (! file_exists_multi(exec_tmp2,exec_tmp,&(execname[2]),
                            exts,NUM_EXTENSIONS) )
          if (! file_exists_multi(exec_tmp2,execpath,&(execname[2]),
                                exts,NUM_EXTENSIONS) )
          {
              j = 0;
              for (i = 0; i < NUM_EXTENSIONS; i++)
                  j += sprintf(buf + j," [%d]: %s\n",i+1,exts[i]);
              run2_error(EXIT_FAILURE, 0, "Couldn't locate %s\n"
                    "I looked in the following directories:\n [1]: %s\n [2]: %s\n"
                    "I also tried appending the following "
                    "extensions: \n%s",execname,exec_tmp,execpath,buf);
          }
      debugMsg (1, exec_tmp2);
   }
   /*
    * OTHERWISE, SEARCH PATH (prepend '.' and run.exe's directory)
    * can't use file_exists_multi because we want to search entire path
    * for exts[0], then for exts[1], etc.
    */
   else
   {
      orig_pathlist = getenv("PATH");
      if ((pathlist = malloc (strlen(orig_pathlist)
                              + strlen(".")
                              + strlen(execpath)+ 3)) == NULL)
         run2_error(EXIT_FAILURE, ENOMEM, "internal error - out of memory");
      strcpy(pathlist,".");
      strcat(pathlist,SEP_CHARS);
      strcat(pathlist,execpath);
      strcat(pathlist,SEP_CHARS);
      strcat(pathlist,orig_pathlist);

      debugMsg (1, "PATH: %s\n", pathlist);
      for (i = 0; i < NUM_EXTENSIONS; i++)
      {
          char *t;
          strcpy(exec_tmp,execname);
          strcat(exec_tmp,exts[i]);
          t = run2_pfopen(exec_tmp,pathlist);
          if (t && *t && run2_fileExists(NULL,NULL,t))
          {
              strcpy (exec_tmp2, t);
              break;
          }
          exec_tmp2[0] = '\0';
      }
      debugMsg (1, "exec_tmp\t%s\npathlist\t%s\n",exec_tmp2,pathlist);

      free(pathlist);
      if (exec_tmp2[0] == '\0')
      {
          j = 0;
          for (i = 0; i < NUM_EXTENSIONS; i++)
              j += sprintf(buf + j," [%d]: %s\n",i+1,exts[i]);
          run2_error(EXIT_FAILURE, 0,
                "Couldn't find %s anywhere.\n"
                "I even looked in the PATH \n"
                "I also tried appending the following "
                "extensions: \n%s",execname,buf);
      }
   }
/*
 * At this point, we know that exec_tmp2 contains a filename
 * and we know that exec_tmp2 exists.
 */
#if defined(__CYGWIN__)
   {
      struct stat stbuf;
      char sym_link_name[MAX_PATH+1];
      char real_name[MAX_PATH+1];
      char dummy[MAX_PATH+1];

      strcpy(exec_tmp,exec_tmp2);

      cygwin_conv_path (CCP_WIN_A_TO_POSIX | CCP_RELATIVE,
                        exec_tmp, sym_link_name, MAX_PATH+1);
      debugMsg (1, sym_link_name);

      if (lstat(sym_link_name, &stbuf) == 0)
      {
         if ((stbuf.st_mode & S_IFLNK) == S_IFLNK)
         {
	    ssize_t len;

	    if ((len = readlink(sym_link_name, real_name, sizeof(real_name))) == -1)
               run2_error(EXIT_FAILURE, errno, "problem reading symbolic link for %s",exec_tmp);
            else
            {
		if (len < sizeof(real_name))
		    real_name[len] = '\0';
		else
		    run2_error(EXIT_FAILURE, ENAMETOOLONG, "symlink pointed to by %s is too long", sym_link_name);
                /* if realname starts with '/' it's a rootpath */
                if (real_name[0] == '/')
                    strcpy(exec_tmp2,real_name);
                else /* otherwise, it's relative to the symlink's location */
                {
                   cygwin_split_path(sym_link_name,exec_tmp2,dummy);
                   if (!run2_ends_with(exec_tmp2,PATH_SEP_CHAR_STR))
                      strcat(exec_tmp2,PATH_SEP_CHAR_STR);
                   strcat(exec_tmp2,real_name);
                }
            }
         }
         else /* NOT a symlink */
            strcpy(exec_tmp2, sym_link_name);
      }
      else
         run2_error(EXIT_FAILURE, 0, "can't locate executable - %s",sym_link_name);
   }
   /* length value here is a bit of a cheat */
   cygwin_conv_path (CCP_POSIX_TO_WIN_A | CCP_ABSOLUTE,
                     exec_tmp2, exec, MAX_PATH + FILENAME_MAX + 100);
#else
   strcpy (exec, exec_tmp2);
#endif
}

/*
 * Uses system info to determine the path used to invoke run
 * Also attempts to deduce the target execname if "compact_invocation"
 * method was used.
 *
 * returns TRUE if compact_invocation method was used
 *   (and target execname was deduced successfully)
 * otherwise returns FALSE, and execname == run or run.exe
 */
static int
get_exec_name_and_path(char* execname, char* execpath)
{
   char modname[MAX_PATH];
   char* tmp_execname;
   char* p;
   int retval = FALSE;

   if (!GetModuleFileName (NULL, modname, MAX_PATH))
      run2_error (EXIT_FAILURE, 0, "internal error - can't find my own name");
   if ((p = strrchr (modname, '\\')) == NULL)
      run2_error (EXIT_FAILURE, 0, "internal error - my own name has no path\n%s",modname);
   tmp_execname = p + 1;
   p[0] = '\0';
   /* if invoked by a name like "runxemacs" then strip off
    * the "run" and let "xemacs" be execname.
    * To check for this, make that:
    *   1) first three chars are "run"
    *   2) but the string doesn't end there, or start ".exe"
    * Also, set "compact_invocation" TRUE
    */
   if ( ((tmp_execname[0] == 'r') || (tmp_execname[0] == 'R')) &&
        ((tmp_execname[1] == 'u') || (tmp_execname[1] == 'U')) &&
        ((tmp_execname[2] == 'n') || (tmp_execname[2] == 'N')) &&
        ((tmp_execname[3] != '.') && (tmp_execname[3] != '\0')) )
   {
      tmp_execname += 3;
      retval = TRUE;
   }
   else
      tmp_execname = NULL;

   if (tmp_execname == NULL)
      strcpy(execname,"");
   else
      strcpy(execname,tmp_execname);
#if defined(__CYGWIN__)
   /* length value here is a bit of a cheat */
   cygwin_conv_path (CCP_WIN_A_TO_POSIX | CCP_RELATIVE,
                     modname, execpath, MAX_PATH);
#else
   strcpy(execpath,modname);
#endif
   return retval;
}

#ifdef __CYGWIN__
static void
add_path_cygwin(const char *str)
{
    char wstr[MAX_PATH];
    DWORD envsize;
    size_t slen;
    char *buffer;

    cygwin_conv_path_list (CCP_POSIX_TO_WIN_A | CCP_ABSOLUTE, str, wstr, MAX_PATH);
    slen = strlen(wstr);

    envsize = GetEnvironmentVariable("PATH", NULL, 0);
    if (envsize == 0)
    {
        DWORD lasterror = GetLastError();
        if (lasterror == ERROR_ENVVAR_NOT_FOUND)
        {
            if (!SetEnvironmentVariable("PATH", wstr))
                errorMsg("SetEnvironmentVariable failed: 0x%x", GetLastError());
        }
        errorMsg("GetEnvironmentVariable failed: 0x%x", lasterror);
    }
    buffer = malloc(envsize + slen + 2);
    if (buffer == NULL)
        errorMsg("internal error - out of memory");

    if (!GetEnvironmentVariable("PATH", buffer, envsize))
        errorMsg("GetEnvironmentVariable failed: 0x%x", GetLastError());

    strcat(buffer, ";");
    strcat(buffer, wstr);

    if (!SetEnvironmentVariable("PATH", buffer))
        errorMsg("SetEnvironmentVariable failed: 0x%x", GetLastError());
    free(buffer);
}

#endif
static void
add_path(const char *str)
{
    const char *path = getenv("PATH");
    if (path == NULL)
    {
        run2_setenv("PATH", str);
    } else
    {
        char *buffer = malloc(strlen(path) + strlen(str) + 2);
        strcpy(buffer, path);
        strcat(buffer, ":");
        strcat(buffer, str);
        run2_setenv("PATH", buffer);
        free(buffer);
    }
#ifdef __CYGWIN__
   add_path_cygwin(str);
#endif
}

static int
file_exists_multi(char* fullname, const char* path,
                  const char* name_noext, const char* exts[],
                  const int extcnt)
{
    char tryName[MAX_PATH + FILENAME_MAX];
    int i = 0;
    int retval = FALSE;
    char* t = NULL;
    fullname[0] = '\0';
    for (i = 0; i < extcnt; i++)
    {
        strcpy(tryName,name_noext);
        strcat(tryName,exts[i]);
        if ((run2_fileExists(&t, path, tryName) == TRUE) && t && *t)
        {
            strcpy (fullname, t);
            free(t);
            retval = TRUE;
            break;
        }
        else
        {
          free(t);
          t = NULL;
        }
    }
    return retval;
}

