* sim/ucsim/cmd.src/cmdutil.cc: NUL device is detected as CG_FILE type
[fw/sdcc] / support / Util / MySystem.c
index e18646b211fa94adf5bd0cf261c5e82020f6c174..30e657c571e9757900a1e807947563c691ba8049 100644 (file)
    what you give them.   Help stamp out software-hoarding!
 -------------------------------------------------------------------------*/
 
-#include "common.h"
-#include "newalloc.h"
-#if defined(_MSC_VER)
+#ifdef _WIN32
+#undef DATADIR
+#include <windows.h>
+/* avoid DATADIR definition clash :-( */
 #include <io.h>
 #else
-#include <sys/stat.h>
+#include <unistd.h>
 #endif
+#include <ctype.h>
+#include "SDCCglobl.h"
+#include "SDCCutil.h"
+#include "MySystem.h"
+#include "newalloc.h"
 
 
-#if !defined(__BORLANDC__) && !defined(_MSC_VER)
-#include <unistd.h>
-#else
-// No unistd.h in Borland C++
-extern int access (const char *, int);
-#define X_OK 1
-#endif
+set *binPathSet = NULL; /* set of binary paths */
+
 
 /*!
-Call an external program with arguements
-*/
+ * get command and arguments from command line
+ */
 
-//char *ExePathList[]= {SRCDIR "/bin",PREFIX "/bin", NULL};
-char *ExePathList[] = {NULL, NULL};                    /* First entry may be overwritten, so use two. */
+static void
+split_command(const char *cmd_line, char **command, char **params)
+{
+  const char *p, *cmd_start;
+  char delim;
+  char *str;
+  unsigned len;
 
-int
-my_system (const char *cmd)
+  /* skip leading spaces */
+  for (p = cmd_line; isspace(*p); p++)
+    ;
+
+  /* get command */
+  switch (*p) {
+  case '\'':
+  case '"':
+    delim = *p;
+    cmd_start = ++p;
+    break;
+
+  default:
+    delim = ' ';
+    cmd_start = p;
+  }
+
+  if (delim == ' ') {
+    while (*p != '\0' && !isspace(*p))
+      p++;
+  }
+  else {
+    while (*p != '\0' && *p != delim)
+      p++;
+  }
+
+  if (command != NULL) {
+    len = p - cmd_start;
+    str = Safe_alloc(len + 1);
+    strncpy(str, cmd_start, len);
+    str[len] = '\0';
+    *command = str;
+  }
+
+  p++;
+
+  /* skip spaces before parameters */
+  while (isspace(*p))
+    p++;
+
+  /* get parameters */
+  if (params != NULL)
+    *params = Safe_strdup(p);
+}
+
+
+/*!
+ * find the command:
+ * 1) if the command is specified by path, try it
+ * 2) try to find the command in predefined path's
+ * 3) trust on $PATH
+ */
+
+#ifdef _WIN32
+/* WIN32 version */
+
+/*
+ * I don't like this solution, but unfortunately cmd.exe and command.com
+ * don't accept something like this:
+ * "program" "argument"
+ * Cmd.exe accepts the following:
+ * ""program" "argument""
+ * but command.com doesn't.
+ * The following is accepted by both:
+ * program "argument"
+ *
+ * So the most portable WIN32 solution is to use GetShortPathName() for
+ * program to get rid of spaces, so that quotes are not needed :-(
+ * Using spawnvp() instead of system() is more portable cross platform approach,
+ * but then also a substitute for _popen() should be developed...
+ */
+
+#define EXE_EXT ".exe"
+
+/*!
+ * merge command and parameters to command line
+ */
+
+static char *
+merge_command(const char *command, const char *params)
 {
-  int argsStart, e, i = 0;
-  char *cmdLine = NULL;
+  /* allocate extra space for ' ' and '\0' */
+  char *cmd_line = (char *)Safe_alloc(strlen(command) + strlen(params) + 2);
+  sprintf(cmd_line, "%s %s", command, params);
 
-  argsStart = strstr (cmd, " ") - cmd;
+  return cmd_line;
+}
 
-  // try to find the command in predefined path's
-  while (ExePathList[i])
-    {
-      cmdLine = (char *) Safe_alloc (strlen (ExePathList[i]) + strlen (cmd) + 10);
-      strcpy (cmdLine, ExePathList[i]);        // the path
 
-      strcat (cmdLine, DIR_SEPARATOR_STRING);
-      strncat (cmdLine, cmd, argsStart);       // the command
+/*!
+ * check if path/command exist by converting it to short file name
+ * if it exists, compose with args and return it
+ */
 
-#if NATIVE_WIN32
-      strcat (cmdLine, ".exe");
-#endif
+static char *
+compose_command_line(const char *path, const char *command, const char *args)
+{
+  unsigned len;
+  char cmdPath[PATH_MAX];
+  char shortPath[PATH_MAX];
+
+  if (path != NULL)
+    SNPRINTF(cmdPath, sizeof cmdPath,
+      "%s" DIR_SEPARATOR_STRING "%s", path, command);
+  else
+    strncpyz(cmdPath, command, sizeof cmdPath);
+
+  /* Try if cmdPath or cmdPath.exe exist by converting it to the short path name */
+  len = GetShortPathName(cmdPath, shortPath, sizeof shortPath);
+  assert(len < sizeof shortPath);
+  if (0 == len) {
+    len = GetShortPathName(strncatz(cmdPath, EXE_EXT, sizeof cmdPath), shortPath, sizeof shortPath);
+    assert(len < sizeof shortPath);
+  }
+  if (0 != len) {
+    /* compose the command line */
+    return merge_command(shortPath, args);
+  }
+  else {
+    /* path/command not found */
+    return NULL;
+  }
+}
 
-      if (access (cmdLine, X_OK) == 0)
-       {
-         // the arguments
-         strcat (cmdLine, cmd + argsStart);
-         break;
-       }
-      Safe_free (cmdLine);
-      cmdLine = NULL;
-      i++;
-    }
 
-  if (verboseExec)
-    {
-      printf ("+ %s\n", cmdLine ? cmdLine : cmd);
+static char *
+get_path(const char *cmd)
+{
+  char *cmdLine;
+  char *command;
+  char *args;
+  char *path;
+
+  /* get the command */
+  split_command(cmd, &command, &args);
+
+  if (NULL == (cmdLine = compose_command_line(NULL, command, args))) {
+    /* not an absolute path: try to find the command in predefined binary paths */
+    if (NULL != (path = (char *)setFirstItem(binPathSet))) {
+      while (NULL == (cmdLine  = compose_command_line(path, command, args)) &&
+        NULL != (path = (char *)setNextItem(binPathSet)))
+        ;
     }
 
-  if (cmdLine)
-    {
-      // command found in predefined path
-      e = system (cmdLine);
-      Safe_free (cmdLine);
+    if (NULL == cmdLine) {
+      /* didn't found the command in predefined binary paths: try with PATH */
+      char *envPath;
+
+      if (NULL != (envPath = getenv("PATH"))) {
+        /* make a local copy; strtok() will modify it */
+        envPath = Safe_strdup(envPath);
+
+        if (NULL != (path = strtok(envPath, ";"))) {
+          while (NULL == (cmdLine = compose_command_line(path, command, args)) &&
+           NULL != (path = strtok(NULL, ";")))
+           ;
+        }
+
+        Safe_free(envPath);
+      }
     }
-  else
-    {
-      // trust on $PATH
-      e = system (cmd);
+
+    /* didn't found it; probably this won't help neither :-( */
+    if (NULL == cmdLine)
+      cmdLine = merge_command(command, args);
+  }
+
+  Safe_free(command);
+  Safe_free(args);
+
+  return cmdLine;
+}
+
+#else
+/* *nix version */
+
+/*!
+ * merge command and parameters to command line
+ */
+
+static char *
+merge_command(const char *command, const char *params)
+{
+  /* allocate extra space for 2x'"', ' ' and '\0' */
+  char *cmd_line = (char *)Safe_alloc(strlen(command) + strlen(params) + 4);
+  sprintf(cmd_line, "\"%s\" %s", command, params);
+  return cmd_line;
+}
+
+
+/*!
+ * check if the path is relative or absolute (if contains the dir separator)
+ */
+
+static int
+has_path(const char *path)
+{
+  return splitPath(path, NULL, 0, NULL, 0);
+}
+
+
+static char *
+get_path(const char *cmd)
+{
+  char *cmdLine = NULL;
+  char *command;
+  char *args;
+  char *path;
+  char cmdPath[PATH_MAX];
+
+
+  /* get the command */
+  split_command(cmd, &command, &args);
+
+  if (!has_path(command)) {
+    /* try to find the command in predefined binary paths */
+    if (NULL != (path = (char *)setFirstItem(binPathSet))) {
+      do
+      {
+        SNPRINTF(cmdPath, sizeof cmdPath,
+          "%s" DIR_SEPARATOR_STRING "%s", path, command);
+
+        /* Try if cmdPath */
+        if (0 == access(cmdPath, X_OK)) {
+          /* compose the command line */
+          cmdLine = merge_command(cmdPath, args);
+          break;
+        }
+      } while (NULL != (path = (char *)setNextItem(binPathSet)));
     }
+    if (NULL == cmdLine)
+      cmdLine = merge_command(command, args);
+
+    Safe_free(command);
+    Safe_free(args);
+
+    return cmdLine;
+  }
+  else {
+    /*
+     * the command is defined with absolute path:
+     * just return it
+     */
+    Safe_free(command);
+    Safe_free(args);
+
+    return Safe_strdup(cmd);
+  }
+}
+#endif
+
+
+/*!
+ * call an external program with arguements
+ */
+
+int
+my_system(const char *cmd)
+{
+  int e;
+  char *cmdLine = get_path(cmd);
+
+  assert(NULL != cmdLine);
+
+  if (options.verboseExec) {
+      printf("+ %s\n", cmdLine);
+  }
+
+  e = system(cmdLine);
+  Safe_free(cmdLine);
+
   return e;
 }
 
+
+/*!
+ * pipe an external program with arguements
+ */
+
+#ifdef _WIN32
+#define popen_read(cmd) _popen((cmd), "rt")
+#else
+#define popen_read(cmd) popen((cmd), "r")
+#endif
+
+FILE *
+my_popen(const char *cmd)
+{
+  FILE *fp;
+  char *cmdLine = get_path(cmd);
+
+  assert(NULL != cmdLine);
+
+  if (options.verboseExec) {
+      printf("+ %s\n", cmdLine);
+  }
+
+  fp = popen_read(cmdLine);
+  Safe_free(cmdLine);
+
+  return fp;
+}