HOST: Use more modern syntax.
authorBen Pfaff <blp@cs.stanford.edu>
Tue, 6 Jul 2010 23:17:19 +0000 (16:17 -0700)
committerBen Pfaff <blp@cs.stanford.edu>
Fri, 24 Sep 2010 03:45:30 +0000 (20:45 -0700)
An old SPSS paper manual I have describes the syntax that PSPP
implemented for HOST up until this commit, but newer versions
describe the syntax that this commit adopts.

NEWS
doc/utilities.texi
src/language/command.c
src/language/command.def
src/language/utilities/automake.mk
src/language/utilities/host.c [new file with mode: 0644]
tests/bugs/overwrite-input-file.sh
tests/bugs/signals.sh

diff --git a/NEWS b/NEWS
index 921e804123fbaaddc12390c47b201ff2bc8e65d2..0e64c432e73068688e636a4bf8d30d5409ae6070 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -1,5 +1,5 @@
 PSPP NEWS -- history of user-visible changes.
-Time-stamp: <2010-08-29 15:12:21 blp>
+Time-stamp: <2010-09-10 20:46:06 blp>
 Copyright (C) 1996-9, 2000, 2008, 2009, 2010 Free Software Foundation, Inc.
 See the end for copying conditions.
 
@@ -14,6 +14,8 @@ Changes from 0.7.3 to 0.7.5:
 
  * The PRESERVE and RESTORE commands are now implemented.
 
+ * The HOST command has been updated to use more modern syntax.
+
 Changes from 0.7.2 to 0.7.3:
 
  * Charts are now produced with Cairo and Pango, instead of libplot.
index d4e7d3d9aa565e4da975e47610e26dbabfc83d44..9f57a76739b43c0a7921b96df18bf66c32905e26 100644 (file)
@@ -212,12 +212,19 @@ control to the operating system.
 
 @display
 HOST.
+HOST COMMAND=['command'...].
 @end display
 
 @cmd{HOST} suspends the current PSPP session and temporarily returns control 
 to the operating system.
 This command cannot be used if the SAFER setting is active.
 
+If the COMMAND subcommand is specified, as a sequence of shell
+commands as quoted strings within square brackets, then PSPP executes
+them together in a single subshell.
+
+If no subcommands are specified, then PSPP invokes an interactive
+subshell.
 
 @node INCLUDE
 @section INCLUDE
index 448fae7ea803463f11a54edbec50a23c816875a4..9f90db93a941af0762b20e4f02b3f6e4758d8732 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 1997-9, 2000, 2009 Free Software Foundation, Inc.
+   Copyright (C) 1997-9, 2000, 2009, 2010 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
 
 #include <config.h>
 
-#include <language/command.h>
+#include "language/command.h"
 
 #include <stdio.h>
 #include <stdlib.h>
 #include <ctype.h>
 #include <errno.h>
-#include <unistd.h>
-#if HAVE_SYS_WAIT_H
-#include <sys/wait.h>
-#endif
-#if HAVE_READLINE
-#include <readline/readline.h>
-#endif
-
-#include <data/casereader.h>
-#include <data/dictionary.h>
-#include <data/procedure.h>
-#include <data/settings.h>
-#include <data/variable.h>
-#include <language/lexer/lexer.h>
-#include <language/prompt.h>
-#include <libpspp/assertion.h>
-#include <libpspp/compiler.h>
-#include <libpspp/message.h>
-#include <libpspp/message.h>
-#include <libpspp/str.h>
-#include <libpspp/getl.h>
-#include <output/text-item.h>
+
+#include "data/casereader.h"
+#include "data/dictionary.h"
+#include "data/procedure.h"
+#include "data/settings.h"
+#include "data/variable.h"
+#include "language/lexer/lexer.h"
+#include "language/prompt.h"
+#include "libpspp/assertion.h"
+#include "libpspp/compiler.h"
+#include "libpspp/message.h"
+#include "libpspp/str.h"
+#include "libpspp/getl.h"
+#include "output/text-item.h"
 
 #include "xalloc.h"
 #include "xmalloca.h"
@@ -785,124 +777,6 @@ cmd_erase (struct lexer *lexer, struct dataset *ds UNUSED)
   return CMD_SUCCESS;
 }
 
-#if HAVE_FORK && HAVE_EXECL
-/* Spawn an interactive shell process. */
-static bool
-shell (void)
-{
-  int pid;
-
-  pid = fork ();
-  switch (pid)
-    {
-    case 0:
-      {
-       const char *shell_fn;
-       char *shell_process;
-
-       {
-         int i;
-
-         for (i = 3; i < 20; i++)
-           close (i);
-       }
-
-       shell_fn = getenv ("SHELL");
-       if (shell_fn == NULL)
-         shell_fn = "/bin/sh";
-
-       {
-         const char *cp = strrchr (shell_fn, '/');
-         cp = cp ? &cp[1] : shell_fn;
-         shell_process = xmalloca (strlen (cp) + 8);
-         strcpy (shell_process, "-");
-         strcat (shell_process, cp);
-         if (strcmp (cp, "sh"))
-           shell_process[0] = '+';
-       }
-
-       execl (shell_fn, shell_process, NULL);
-
-       _exit (1);
-      }
-
-    case -1:
-      msg (SE, _("Couldn't fork: %s."), strerror (errno));
-      return false;
-
-    default:
-      assert (pid > 0);
-      while (wait (NULL) != pid)
-       ;
-      return true;
-    }
-}
-#else /* !(HAVE_FORK && HAVE_EXECL) */
-/* Don't know how to spawn an interactive shell. */
-static bool
-shell (void)
-{
-  msg (SE, _("Interactive shell not supported on this platform."));
-  return false;
-}
-#endif
-
-/* Executes the specified COMMAND in a subshell.  Returns true if
-   successful, false otherwise. */
-static bool
-run_command (const char *command)
-{
-  if (system (NULL) == 0)
-    {
-      msg (SE, _("Command shell not supported on this platform."));
-      return false;
-    }
-
-  /* Execute the command. */
-  if (system (command) == -1)
-    msg (SE, _("Error executing command: %s."), strerror (errno));
-
-  return true;
-}
-
-/* Parses, performs the HOST command. */
-int
-cmd_host (struct lexer *lexer, struct dataset *ds UNUSED)
-{
-  int look_ahead;
-
-  if (settings_get_safer_mode ())
-    {
-      msg (SE, _("This command not allowed when the SAFER option is set."));
-      return CMD_FAILURE;
-    }
-
-  look_ahead = lex_look_ahead (lexer);
-  if (look_ahead == '.')
-    {
-      lex_get (lexer);
-      return shell () ? CMD_SUCCESS : CMD_FAILURE;
-    }
-  else if (look_ahead == '\'' || look_ahead == '"')
-    {
-      bool ok;
-
-      lex_get (lexer);
-      if (!lex_force_string (lexer))
-        NOT_REACHED ();
-      ok = run_command (ds_cstr (lex_tokstr (lexer)));
-
-      lex_get (lexer);
-      return ok ? lex_end_of_command (lexer) : CMD_FAILURE;
-    }
-  else
-    {
-      bool ok = run_command (lex_rest_of_line (lexer));
-      lex_discard_line (lexer);
-      return ok ? CMD_SUCCESS : CMD_FAILURE;
-    }
-}
-
 /* Parses, performs the NEW FILE command. */
 int
 cmd_new_file (struct lexer *lexer, struct dataset *ds)
index 8400fdee7dff6b2d7e5b26ea390ddbf8cf07ff06..8861d9197b048742c23c20f74ec53224be6f6279 100644 (file)
@@ -24,7 +24,7 @@ DEF_CMD (S_ANY, 0, "EXIT", cmd_finish)
 DEF_CMD (S_ANY, 0, "FILE HANDLE", cmd_file_handle)
 DEF_CMD (S_ANY, F_KEEP_FINAL_TOKEN, "FILE LABEL", cmd_file_label)
 DEF_CMD (S_ANY, 0, "FINISH", cmd_finish)
-DEF_CMD (S_ANY, F_KEEP_FINAL_TOKEN, "HOST", cmd_host)
+DEF_CMD (S_ANY, 0, "HOST", cmd_host)
 DEF_CMD (S_ANY, 0, "INCLUDE", cmd_include)
 DEF_CMD (S_ANY, 0, "INSERT", cmd_insert)
 DEF_CMD (S_ANY, 0, "N OF CASES", cmd_n_of_cases)
index c4ca92b0bc54bcbb4a1eb8b69e67a49624bff13f..3a3e16d50af4ab1176215e9246ff9a868ef24751 100644 (file)
@@ -8,6 +8,7 @@ language_utilities_sources = \
        src/language/utilities/cd.c \
        src/language/utilities/date.c \
        src/language/utilities/echo.c \
+       src/language/utilities/host.c \
        src/language/utilities/title.c \
        src/language/utilities/include.c \
        src/language/utilities/permissions.c 
diff --git a/src/language/utilities/host.c b/src/language/utilities/host.c
new file mode 100644 (file)
index 0000000..ae34436
--- /dev/null
@@ -0,0 +1,167 @@
+/* PSPP - a program for statistical analysis.
+   Copyright (C) 1997-9, 2000, 2009, 2010 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 <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <errno.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/message.h"
+#include "libpspp/str.h"
+
+#include "gl/xalloc.h"
+#include "gl/xmalloca.h"
+
+#include "gettext.h"
+#define _(msgid) gettext (msgid)
+#define N_(msgid) msgid
+\f
+#if HAVE_FORK && HAVE_EXECL
+/* Spawn an interactive shell process. */
+static bool
+shell (void)
+{
+  int pid;
+
+  pid = fork ();
+  switch (pid)
+    {
+    case 0:
+      {
+       const char *shell_fn;
+       char *shell_process;
+
+       {
+         int i;
+
+         for (i = 3; i < 20; i++)
+           close (i);
+       }
+
+       shell_fn = getenv ("SHELL");
+       if (shell_fn == NULL)
+         shell_fn = "/bin/sh";
+
+       {
+         const char *cp = strrchr (shell_fn, '/');
+         cp = cp ? &cp[1] : shell_fn;
+         shell_process = xmalloca (strlen (cp) + 8);
+         strcpy (shell_process, "-");
+         strcat (shell_process, cp);
+         if (strcmp (cp, "sh"))
+           shell_process[0] = '+';
+       }
+
+       execl (shell_fn, shell_process, NULL);
+
+       _exit (1);
+      }
+
+    case -1:
+      msg (SE, _("Couldn't fork: %s."), strerror (errno));
+      return false;
+
+    default:
+      assert (pid > 0);
+      while (wait (NULL) != pid)
+       ;
+      return true;
+    }
+}
+#else /* !(HAVE_FORK && HAVE_EXECL) */
+/* Don't know how to spawn an interactive shell. */
+static bool
+shell (void)
+{
+  msg (SE, _("Interactive shell not supported on this platform."));
+  return false;
+}
+#endif
+
+/* Executes the specified COMMAND in a subshell.  Returns true if
+   successful, false otherwise. */
+static bool
+run_command (const char *command)
+{
+  if (system (NULL) == 0)
+    {
+      msg (SE, _("Command shell not supported on this platform."));
+      return false;
+    }
+
+  /* Execute the command. */
+  if (system (command) == -1)
+    msg (SE, _("Error executing command: %s."), strerror (errno));
+
+  return true;
+}
+
+int
+cmd_host (struct lexer *lexer, struct dataset *ds UNUSED)
+{
+  if (settings_get_safer_mode ())
+    {
+      msg (SE, _("This command not allowed when the SAFER option is set."));
+      return CMD_FAILURE;
+    }
+
+  if (lex_token (lexer) == '.')
+    return shell () ? CMD_SUCCESS : CMD_FAILURE;
+  else if (lex_match_id (lexer, "COMMAND"))
+    {
+      struct string command;
+      bool ok;
+
+      lex_match (lexer, '=');
+      if (!lex_force_match (lexer, '['))
+        return CMD_FAILURE;
+
+      ds_init_empty (&command);
+      while (lex_token (lexer) == T_STRING)
+        {
+          if (!ds_is_empty (&command))
+            ds_put_char (&command, '\n');
+          ds_put_substring (&command, ds_ss (lex_tokstr (lexer)));
+          lex_get (lexer);
+        }
+      if (!lex_force_match (lexer, ']'))
+        {
+          ds_destroy (&command);
+          return CMD_FAILURE;
+        }
+
+      ok = run_command (ds_cstr (&command));
+      ds_destroy (&command);
+
+      return ok ? lex_end_of_command (lexer) : CMD_FAILURE;
+    }
+  else
+    {
+      lex_error (lexer, NULL);
+      return CMD_FAILURE;
+    }
+}
index cbb29b42cf68a178ba443f80e4d91c5855fb71f7..f1beabcca59d37aebeda13163816dba823a4fb1c 100755 (executable)
@@ -98,7 +98,7 @@ COMPUTE Y = X + 1.
 XSAVE OUTFILE='foo.sav'.
 XEXPORT OUTFILE='foo.por'.
 PRINT OUTFILE='foo.data'.
-HOST kill -TERM \$PPID
+HOST COMMAND=['kill -TERM \$PPID'].
 EOF
 if [ $? -ne 0 ] ; then no_result ; fi
 
index f46c4deacdfff21d228e35119db184ba4be204e9..20ca0700947e96a3201520ba2eda95e58f9374cf 100755 (executable)
@@ -59,7 +59,7 @@ mkdir -p $TEMPDIR
 cd $TEMPDIR
 
 activity="sending SIGINT to pspp"
-echo 'host kill -INT $PPID' | $PSPP -o pspp.csv > /dev/null 2> $TEMPDIR/stderr1
+echo 'host command=["kill -INT $PPID"].' | $PSPP -o pspp.csv > /dev/null 2> $TEMPDIR/stderr1
 if [ $? -ne 0 ] ; then no_result ; fi
 
 # SIGINT should have caused a clean shutdown
@@ -68,7 +68,7 @@ activity="checking for absence of error messages 1"
 if [ $? -ne 0 ] ; then fail ; fi
 
 activity="sending SIGSEGV to pspp"
-echo 'host kill -SEGV $PPID' | $PSPP -o pspp.csv > /dev/null 2> $TEMPDIR/stderr2
+echo 'host command=["kill -SEGV $PPID"].' | $PSPP -o pspp.csv > /dev/null 2> $TEMPDIR/stderr2
 if [ $? -eq 0 ] ; then no_result ; fi
 
 # SIGSEGV should have caused an error message