From 5933892c21c6166b5714be979116d3aa70219c57 Mon Sep 17 00:00:00 2001 From: Ben Pfaff Date: Tue, 6 Jul 2010 16:17:19 -0700 Subject: [PATCH] HOST: Use more modern syntax. 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 | 4 +- doc/utilities.texi | 7 ++ src/language/command.c | 158 +++------------------------ src/language/command.def | 2 +- src/language/utilities/automake.mk | 1 + src/language/utilities/host.c | 167 +++++++++++++++++++++++++++++ tests/bugs/overwrite-input-file.sh | 2 +- tests/bugs/signals.sh | 4 +- 8 files changed, 198 insertions(+), 147 deletions(-) create mode 100644 src/language/utilities/host.c diff --git a/NEWS b/NEWS index 921e804123..0e64c432e7 100644 --- 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. diff --git a/doc/utilities.texi b/doc/utilities.texi index d4e7d3d9aa..9f57a76739 100644 --- a/doc/utilities.texi +++ b/doc/utilities.texi @@ -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 diff --git a/src/language/command.c b/src/language/command.c index 448fae7ea8..9f90db93a9 100644 --- a/src/language/command.c +++ b/src/language/command.c @@ -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 @@ -16,34 +16,26 @@ #include -#include +#include "language/command.h" #include #include #include #include -#include -#if HAVE_SYS_WAIT_H -#include -#endif -#if HAVE_READLINE -#include -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include + +#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) diff --git a/src/language/command.def b/src/language/command.def index 8400fdee7d..8861d9197b 100644 --- a/src/language/command.def +++ b/src/language/command.def @@ -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) diff --git a/src/language/utilities/automake.mk b/src/language/utilities/automake.mk index c4ca92b0bc..3a3e16d50a 100644 --- a/src/language/utilities/automake.mk +++ b/src/language/utilities/automake.mk @@ -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 index 0000000000..ae34436725 --- /dev/null +++ b/src/language/utilities/host.c @@ -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 . */ + +#include + +#include +#include +#include +#include +#include +#if HAVE_SYS_WAIT_H +#include +#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 + +#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; + } +} diff --git a/tests/bugs/overwrite-input-file.sh b/tests/bugs/overwrite-input-file.sh index cbb29b42cf..f1beabcca5 100755 --- a/tests/bugs/overwrite-input-file.sh +++ b/tests/bugs/overwrite-input-file.sh @@ -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 diff --git a/tests/bugs/signals.sh b/tests/bugs/signals.sh index f46c4deacd..20ca070094 100755 --- a/tests/bugs/signals.sh +++ b/tests/bugs/signals.sh @@ -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 -- 2.30.2