Move all command implementations into a single 'commands' directory.
[pspp] / src / language / utilities / host.c
diff --git a/src/language/utilities/host.c b/src/language/utilities/host.c
deleted file mode 100644 (file)
index 22d25c0..0000000
+++ /dev/null
@@ -1,342 +0,0 @@
-/* pspp - a program for statistical analysis.
-   Copyright (C) 1997-9, 2000, 2009, 2010, 2011 Free Software Foundation, Inc.
-
-   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 3 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, see <http://www.gnu.org/licenses/>. */
-
-#include <config.h>
-
-#include <ctype.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <signal.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <sys/time.h>
-#include <unistd.h>
-#if HAVE_SYS_WAIT_H
-#include <sys/wait.h>
-#endif
-
-#include "data/settings.h"
-#include "language/command.h"
-#include "language/lexer/lexer.h"
-#include "libpspp/assertion.h"
-#include "libpspp/compiler.h"
-#include "libpspp/i18n.h"
-#include "libpspp/message.h"
-#include "libpspp/str.h"
-#include "libpspp/string-array.h"
-#include "libpspp/temp-file.h"
-#include "output/driver.h"
-
-#include "gl/error.h"
-#include "gl/intprops.h"
-#include "gl/localcharset.h"
-#include "gl/read-file.h"
-#include "gl/timespec.h"
-#include "gl/xalloc.h"
-#include "gl/xmalloca.h"
-
-#include "gettext.h"
-#define _(msgid) gettext (msgid)
-#define N_(msgid) msgid
-\f
-#if !HAVE_FORK
-#define TIME_LIMIT_SUPPORTED 0
-static bool
-run_commands (const struct string_array *commands, double time_limit)
-{
-  assert (time_limit == DBL_MAX);
-
-  for (size_t i = 0; i < commands->n; i++)
-    {
-      /* XXX No way to capture command output */
-      char *s = recode_string (locale_charset (), "UTF-8",
-                               commands->strings[i], -1);
-      int retval = system (s);
-      free (s);
-
-      if (retval)
-        {
-          msg (SE, _("%s: Command exited with status %d."),
-               commands->strings[i], retval);
-          return false;
-        }
-    }
-  return true;
-}
-#else
-#define TIME_LIMIT_SUPPORTED 1
-static bool
-run_command (const char *command, struct timespec timeout)
-{
-  /* Same exit codes used by 'sh'. */
-  enum {
-    EXIT_CANNOT_INVOKE = 126,
-    EXIT_ENOENT = 127,
-  };
-
-  /* Create a temporary file to capture command output. */
-  FILE *output_file = create_temp_file ();
-  if (!output_file)
-    {
-      msg (SE, _("Failed to create temporary file (%s)."), strerror (errno));
-      return false;
-    }
-
-  int dev_null_fd = open ("/dev/null", O_RDONLY);
-  if (dev_null_fd < 0)
-    {
-      msg (SE, _("/dev/null: Failed to open (%s)."), strerror (errno));
-      fclose (output_file);
-      return false;
-    }
-
-  char *locale_command = recode_string (locale_charset (), "UTF-8",
-                                        command, -1);
-
-  pid_t pid = fork ();
-  if (pid < 0)
-    {
-      close (dev_null_fd);
-      fclose (output_file);
-      free (locale_command);
-
-      msg (SE, _("Couldn't fork: %s."), strerror (errno));
-      return false;
-    }
-  else if (!pid)
-    {
-      /* Running in the child. */
-
-#if __GNU__
-      /* Hurd doesn't support inheriting process timers in a way that works. */
-      if (setpgid (0, 0) < 0)
-        error (1, errno, _("Failed to set process group."));
-#else
-      /* Set up timeout. */
-      if (timeout.tv_sec < TYPE_MAXIMUM (time_t))
-        {
-          signal (SIGALRM, SIG_DFL);
-
-          struct timespec left = timespec_sub (timeout, current_timespec ());
-          if (timespec_sign (left) <= 0)
-            raise (SIGALRM);
-
-          struct itimerval it = {
-            .it_value = {
-              .tv_sec = left.tv_sec,
-              .tv_usec = left.tv_nsec / 1000
-            }
-          };
-          if (setitimer (ITIMER_REAL, &it, NULL) < 0)
-            error (1, errno, _("Failed to set timeout."));
-        }
-#endif
-
-      /* Set up file descriptors:
-         - /dev/null for stdin
-         - Temporary file to capture stdout and stderr.
-         - Close everything else.
-      */
-      dup2 (dev_null_fd, 0);
-      dup2 (fileno (output_file), 1);
-      dup2 (fileno (output_file), 2);
-      close (dev_null_fd);
-      for (int fd = 3; fd < 256; fd++)
-        close (fd);
-
-      /* Choose the shell. */
-      const char *shell = getenv ("SHELL");
-      if (shell == NULL)
-        shell = "/bin/sh";
-
-      /* Run subprocess. */
-      execl (shell, shell, "-c", locale_command, NULL);
-
-      /* Failed to start the shell. */
-      _exit (errno == ENOENT ? EXIT_ENOENT : EXIT_CANNOT_INVOKE);
-    }
-
-  /* Running in the parent. */
-  close (dev_null_fd);
-  free (locale_command);
-
-  /* Wait for child to exit. */
-  int status = 0;
-  int error = 0;
-  for (;;)
-    {
-#if __GNU__
-      if (timespec_cmp (current_timespec (), timeout) >= 0)
-        kill (-pid, SIGALRM);
-
-      int flags = WNOHANG;
-#else
-      int flags = 0;
-#endif
-      pid_t retval = waitpid (pid, &status, flags);
-      if (retval == pid)
-        break;
-      else if (retval < 0)
-        {
-          if (errno != EINTR)
-            {
-              error = errno;
-              break;
-            }
-        }
-#if __GNU__
-      else if (retval == 0)
-        sleep (1);
-#endif
-      else
-        NOT_REACHED ();
-    }
-
-  bool ok = true;
-  if (error)
-    {
-      msg (SW, _("While running \"%s\", waiting for child process "
-                 "failed (%s)."),
-           command, strerror (errno));
-      ok = false;
-    }
-
-  if (WIFSIGNALED (status))
-    {
-      int signum = WTERMSIG (status);
-      if (signum == SIGALRM)
-        msg (SW, _("Command \"%s\" timed out."), command);
-      else
-        msg (SW, _("Command \"%s\" terminated by signal %d."), command, signum);
-      ok = false;
-    }
-  else if (WIFEXITED (status) && WEXITSTATUS (status))
-    {
-      int exit_code = WEXITSTATUS (status);
-      const char *detail = (exit_code == EXIT_ENOENT
-                            ? _("Command or shell not found")
-                            : exit_code == EXIT_CANNOT_INVOKE
-                            ? _("Could not invoke command or shell")
-                            : NULL);
-      if (detail)
-        msg (SW, _("Command \"%s\" exited with status %d (%s)."),
-             command, exit_code, detail);
-      else
-        msg (SW, _("Command \"%s\" exited with status %d."),
-             command, exit_code);
-      ok = false;
-    }
-
-  rewind (output_file);
-  size_t length;
-  char *locale_output = fread_file (output_file, 0, &length);
-  if (!locale_output)
-    {
-      msg (SW, _("Command \"%s\" output could not be read (%s)."),
-           command, strerror (errno));
-      ok = false;
-    }
-  else if (length > 0)
-    {
-      char *output = recode_string ("UTF-8", locale_charset (),
-                                    locale_output, -1);
-
-      /* Drop final new-line, if any. */
-      char *end = strchr (output, '\0');
-      if (end > output && end[-1] == '\n')
-        end[-1] = '\0';
-
-      output_log_nocopy (output);
-    }
-  free (locale_output);
-
-  return ok;
-}
-
-static bool
-run_commands (const struct string_array *commands, double time_limit)
-{
-  struct timespec timeout = timespec_add (dtotimespec (time_limit),
-                                          current_timespec ());
-
-  for (size_t i = 0; i < commands->n; i++)
-    {
-      if (!run_command (commands->strings[i], timeout))
-        return false;
-    }
-
-  return true;
-}
-#endif
-
-int
-cmd_host (struct lexer *lexer, struct dataset *ds UNUSED)
-{
-  if (settings_get_safer_mode ())
-    {
-      lex_next_error (lexer, -1, -1,
-                      _("This command not allowed when the %s option is set."),
-                      "SAFER");
-      return CMD_FAILURE;
-    }
-
-  if (!lex_force_match_phrase (lexer, "COMMAND=[")
-      || !lex_force_string (lexer))
-    return CMD_FAILURE;
-
-  struct string_array commands = STRING_ARRAY_INITIALIZER;
-  while (lex_token (lexer) == T_STRING)
-    {
-      string_array_append (&commands, lex_tokcstr (lexer));
-      lex_get (lexer);
-    }
-  if (!lex_force_match (lexer, T_RBRACK))
-    {
-      string_array_destroy (&commands);
-      return CMD_FAILURE;
-    }
-
-  double time_limit = DBL_MAX;
-  if (lex_match_id (lexer, "TIMELIMIT"))
-    {
-      int time_limit_start = lex_ofs (lexer) - 1;
-      if (!lex_force_match (lexer, T_EQUALS)
-          || !lex_force_num (lexer))
-        {
-          string_array_destroy (&commands);
-          return CMD_FAILURE;
-        }
-
-      double num = lex_number (lexer);
-      lex_get (lexer);
-      time_limit = num < 0.0 ? 0.0 : num;
-
-      int time_limit_end = lex_ofs (lexer) - 1;
-      if (!TIME_LIMIT_SUPPORTED)
-        {
-          lex_ofs_error (lexer, time_limit_start, time_limit_end,
-                         _("Time limit not supported on this platform."));
-          string_array_destroy (&commands);
-          return false;
-        }
-    }
-
-  enum cmd_result result = lex_end_of_command (lexer);
-  if (result == CMD_SUCCESS && !run_commands (&commands, time_limit))
-    result = CMD_FAILURE;
-  string_array_destroy (&commands);
-  return result;
-}