HOST: Use more modern syntax.
[pspp] / src / language / utilities / host.c
1 /* PSPP - a program for statistical analysis.
2    Copyright (C) 1997-9, 2000, 2009, 2010 Free Software Foundation, Inc.
3
4    This program is free software: you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation, either version 3 of the License, or
7    (at your option) any later version.
8
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13
14    You should have received a copy of the GNU General Public License
15    along with this program.  If not, see <http://www.gnu.org/licenses/>. */
16
17 #include <config.h>
18
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <ctype.h>
22 #include <errno.h>
23 #include <unistd.h>
24 #if HAVE_SYS_WAIT_H
25 #include <sys/wait.h>
26 #endif
27
28 #include "data/settings.h"
29 #include "language/command.h"
30 #include "language/lexer/lexer.h"
31 #include "libpspp/assertion.h"
32 #include "libpspp/compiler.h"
33 #include "libpspp/message.h"
34 #include "libpspp/str.h"
35
36 #include "gl/xalloc.h"
37 #include "gl/xmalloca.h"
38
39 #include "gettext.h"
40 #define _(msgid) gettext (msgid)
41 #define N_(msgid) msgid
42 \f
43 #if HAVE_FORK && HAVE_EXECL
44 /* Spawn an interactive shell process. */
45 static bool
46 shell (void)
47 {
48   int pid;
49
50   pid = fork ();
51   switch (pid)
52     {
53     case 0:
54       {
55         const char *shell_fn;
56         char *shell_process;
57
58         {
59           int i;
60
61           for (i = 3; i < 20; i++)
62             close (i);
63         }
64
65         shell_fn = getenv ("SHELL");
66         if (shell_fn == NULL)
67           shell_fn = "/bin/sh";
68
69         {
70           const char *cp = strrchr (shell_fn, '/');
71           cp = cp ? &cp[1] : shell_fn;
72           shell_process = xmalloca (strlen (cp) + 8);
73           strcpy (shell_process, "-");
74           strcat (shell_process, cp);
75           if (strcmp (cp, "sh"))
76             shell_process[0] = '+';
77         }
78
79         execl (shell_fn, shell_process, NULL);
80
81         _exit (1);
82       }
83
84     case -1:
85       msg (SE, _("Couldn't fork: %s."), strerror (errno));
86       return false;
87
88     default:
89       assert (pid > 0);
90       while (wait (NULL) != pid)
91         ;
92       return true;
93     }
94 }
95 #else /* !(HAVE_FORK && HAVE_EXECL) */
96 /* Don't know how to spawn an interactive shell. */
97 static bool
98 shell (void)
99 {
100   msg (SE, _("Interactive shell not supported on this platform."));
101   return false;
102 }
103 #endif
104
105 /* Executes the specified COMMAND in a subshell.  Returns true if
106    successful, false otherwise. */
107 static bool
108 run_command (const char *command)
109 {
110   if (system (NULL) == 0)
111     {
112       msg (SE, _("Command shell not supported on this platform."));
113       return false;
114     }
115
116   /* Execute the command. */
117   if (system (command) == -1)
118     msg (SE, _("Error executing command: %s."), strerror (errno));
119
120   return true;
121 }
122
123 int
124 cmd_host (struct lexer *lexer, struct dataset *ds UNUSED)
125 {
126   if (settings_get_safer_mode ())
127     {
128       msg (SE, _("This command not allowed when the SAFER option is set."));
129       return CMD_FAILURE;
130     }
131
132   if (lex_token (lexer) == '.')
133     return shell () ? CMD_SUCCESS : CMD_FAILURE;
134   else if (lex_match_id (lexer, "COMMAND"))
135     {
136       struct string command;
137       bool ok;
138
139       lex_match (lexer, '=');
140       if (!lex_force_match (lexer, '['))
141         return CMD_FAILURE;
142
143       ds_init_empty (&command);
144       while (lex_token (lexer) == T_STRING)
145         {
146           if (!ds_is_empty (&command))
147             ds_put_char (&command, '\n');
148           ds_put_substring (&command, ds_ss (lex_tokstr (lexer)));
149           lex_get (lexer);
150         }
151       if (!lex_force_match (lexer, ']'))
152         {
153           ds_destroy (&command);
154           return CMD_FAILURE;
155         }
156
157       ok = run_command (ds_cstr (&command));
158       ds_destroy (&command);
159
160       return ok ? lex_end_of_command (lexer) : CMD_FAILURE;
161     }
162   else
163     {
164       lex_error (lexer, NULL);
165       return CMD_FAILURE;
166     }
167 }