886bcdc2fe86bafcb7b602d0fcef3e0bdbdd4ffc
[pspp-builds.git] / src / ui / terminal / terminal-opts.c
1 /* PSPPIRE - a graphical user interface for PSPP.
2    Copyright (C) 2008, 2010  Free Software Foundation
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 "terminal-opts.h"
20
21 #include <stdbool.h>
22 #include <xalloc.h>
23 #include <stdlib.h>
24 #include <unistd.h>
25
26 #include "data/settings.h"
27 #include "data/file-name.h"
28 #include "language/syntax-file.h"
29 #include "libpspp/argv-parser.h"
30 #include "libpspp/assertion.h"
31 #include "libpspp/compiler.h"
32 #include "libpspp/getl.h"
33 #include "libpspp/llx.h"
34 #include "libpspp/str.h"
35 #include "libpspp/string-array.h"
36 #include "libpspp/string-map.h"
37 #include "libpspp/string-set.h"
38 #include "libpspp/version.h"
39 #include "output/driver.h"
40 #include "output/driver-provider.h"
41 #include "ui/terminal/msg-ui.h"
42 #include "ui/terminal/read-line.h"
43
44 #include "gl/error.h"
45 #include "gl/progname.h"
46 #include "gl/version-etc.h"
47 #include "gl/xmemdup0.h"
48
49 #include "gettext.h"
50 #define _(msgid) gettext (msgid)
51 #define N_(msgid) msgid
52
53 struct terminal_opts
54   {
55     struct source_stream *source_stream;
56     enum syntax_mode syntax_mode;
57     struct string_map options;  /* Output driver options. */
58     bool has_output_driver;
59     bool has_terminal_driver;
60     bool process_statrc;
61   };
62
63 enum
64   {
65     OPT_TESTING_MODE,
66     OPT_ERROR_FILE,
67     OPT_OUTPUT,
68     OPT_OUTPUT_OPTION,
69     OPT_INTERACTIVE,
70     OPT_NO_STATRC,
71     OPT_HELP,
72     OPT_VERSION,
73     N_TERMINAL_OPTIONS
74   };
75
76 static struct argv_option terminal_argv_options[N_TERMINAL_OPTIONS] =
77   {
78     {"testing-mode", 0, no_argument, OPT_TESTING_MODE},
79     {"error-file", 'e', required_argument, OPT_ERROR_FILE},
80     {"output", 'o', required_argument, OPT_OUTPUT},
81     {NULL, 'O', required_argument, OPT_OUTPUT_OPTION},
82     {"interactive", 'i', no_argument, OPT_INTERACTIVE},
83     {"no-statrc", 'r', no_argument, OPT_NO_STATRC},
84     {"help", 'h', no_argument, OPT_HELP},
85     {"version", 'V', no_argument, OPT_VERSION},
86   };
87
88 static void
89 register_output_driver (struct terminal_opts *to)
90 {
91   if (!string_map_is_empty (&to->options))
92     {
93       struct output_driver *driver;
94
95       driver = output_driver_create (&to->options);
96       if (driver != NULL)
97         {
98           output_driver_register (driver);
99
100           to->has_output_driver = true;
101           if (driver->device_type == SETTINGS_DEVICE_TERMINAL)
102             to->has_terminal_driver = true;
103         }
104       string_map_clear (&to->options);
105     }
106 }
107
108 static void
109 parse_output_option (struct terminal_opts *to, const char *option)
110 {
111   const char *equals;
112   char *key, *value;
113
114   equals = strchr (option, '=');
115   if (equals == NULL)
116     {
117       error (0, 0, _("%s: output option missing `='"), option);
118       return;
119     }
120
121   key = xmemdup0 (option, equals - option);
122   if (string_map_contains (&to->options, key))
123     {
124       error (0, 0, _("%s: output option %s specified more than twice"), key);
125       free (key);
126       return;
127     }
128
129   value = xmemdup0 (equals + 1, strlen (equals + 1));
130   string_map_insert_nocopy (&to->options, key, value);
131 }
132
133 static char *
134 get_supported_formats (void)
135 {
136   const struct string_set_node *node;
137   struct string_array format_array;
138   struct string_set format_set;
139   char *format_string;
140   const char *format;
141   size_t i;
142
143   /* Get supported formats as unordered set. */
144   string_set_init (&format_set);
145   output_get_supported_formats (&format_set);
146
147   /* Converted supported formats to sorted array. */
148   string_array_init (&format_array);
149   STRING_SET_FOR_EACH (format, node, &format_set)
150     string_array_append (&format_array, format);
151   string_array_sort (&format_array);
152   string_set_destroy (&format_set);
153
154   /* Converted supported formats to string. */
155   format_string = string_array_join (&format_array, " ");
156   string_array_destroy (&format_array);
157   return format_string;
158 }
159
160 static char *
161 get_default_include_path (void)
162 {
163   struct source_stream *ss;
164   struct string dst;
165   char **path;
166   size_t i;
167
168   ss = create_source_stream ();
169   path = getl_include_path (ss);
170   ds_init_empty (&dst);
171   for (i = 0; path[i] != NULL; i++)
172     ds_put_format (&dst, " %s", path[i]);
173   destroy_source_stream (ss);
174
175   return ds_steal_cstr (&dst);
176 }
177
178 static void
179 usage (void)
180 {
181   char *supported_formats = get_supported_formats ();
182   char *default_include_path = get_default_include_path ();
183
184   printf ("\
185 PSPP, a program for statistical analysis of sample data.\n\
186 Usage: %s [OPTION]... FILE...\n\
187 \n\
188 Arguments to long options also apply to equivalent short options.\n\
189 \n\
190 Output options:\n\
191   -o, --output=FILE         output to FILE, default format from FILE's name\n\
192   -O format=FORMAT          override format for previous -o\n\
193   -O OPTION=VALUE           set output option to customize previous -o\n\
194   -O device={terminal|listing}  override device type for previous -o\n\
195   -e, --error-file=FILE     append errors, warnings, and notes to FILE\n\
196 Supported output formats: %s\n\
197 \n\
198 Language options:\n\
199   -I, --include=DIR         append DIR to search path\n\
200   -I-, --no-include         clear search path\n\
201   -r, --no-statrc           disable running rc file at startup\n\
202   -a, --algorithm={compatible|enhanced}\n\
203                             set to `compatible' if you want output\n\
204                             calculated from broken algorithms\n\
205   -x, --syntax={compatible|enhanced}\n\
206                             set to `compatible' to disable PSPP extensions\n\
207   -i, --interactive         interpret syntax in interactive mode\n\
208   -s, --safer               don't allow some unsafe operations\n\
209 Default search path:%s\n\
210 \n\
211 Informative output:\n\
212   -h, --help                display this help and exit\n\
213   -V, --version             output version information and exit\n\
214 \n\
215 Non-option arguments are interpreted as syntax files to execute.\n",
216           program_name, supported_formats, default_include_path);
217
218   free (supported_formats);
219   free (default_include_path);
220
221   emit_bug_reporting_address ();
222   exit (EXIT_SUCCESS);
223 }
224
225 static void
226 terminal_option_callback (int id, void *to_)
227 {
228   struct terminal_opts *to = to_;
229
230   switch (id)
231     {
232     case OPT_TESTING_MODE:
233       settings_set_testing_mode (true);
234       break;
235
236     case OPT_ERROR_FILE:
237       msg_ui_set_error_file (optarg);
238       break;
239
240     case OPT_OUTPUT:
241       register_output_driver (to);
242       string_map_insert (&to->options, "output-file", optarg);
243       break;
244
245     case OPT_OUTPUT_OPTION:
246       parse_output_option (to, optarg);
247       break;
248
249     case OPT_INTERACTIVE:
250       to->syntax_mode = GETL_INTERACTIVE;
251       break;
252
253     case OPT_NO_STATRC:
254       to->process_statrc = false;
255       break;
256
257     case OPT_HELP:
258       usage ();
259       exit (EXIT_SUCCESS);
260
261     case OPT_VERSION:
262       version_etc (stdout, "pspp", PACKAGE_NAME, PACKAGE_VERSION,
263                    "Ben Pfaff", "John Darrington", "Jason Stover",
264                    (char *) NULL);
265       exit (EXIT_SUCCESS);
266
267     default:
268       NOT_REACHED ();
269     }
270 }
271
272 struct terminal_opts *
273 terminal_opts_init (struct argv_parser *ap, struct source_stream *ss)
274 {
275   struct terminal_opts *to;
276
277   to = xmalloc (sizeof *to);
278   to->source_stream = ss;
279   to->syntax_mode = GETL_BATCH;
280   string_map_init (&to->options);
281   to->has_output_driver = false;
282   to->process_statrc = true;
283
284   argv_parser_add_options (ap, terminal_argv_options, N_TERMINAL_OPTIONS,
285                            terminal_option_callback, to);
286   return to;
287 }
288
289 static void
290 add_syntax_file (struct terminal_opts *to, const char *file_name)
291 {
292   if (!strcmp (file_name, "-") && isatty (STDIN_FILENO))
293     getl_append_source (to->source_stream, create_readln_source (),
294                         GETL_INTERACTIVE, ERRMODE_CONTINUE);
295   else
296     getl_append_source (to->source_stream,
297                         create_syntax_file_source (file_name),
298                         to->syntax_mode, ERRMODE_CONTINUE);
299 }
300
301 void
302 terminal_opts_done (struct terminal_opts *to, int argc, char *argv[])
303 {
304   if (to->process_statrc)
305     {
306       char *rc = fn_search_path ("rc", getl_include_path (to->source_stream));
307       if (rc != NULL)
308         {
309           getl_append_source (to->source_stream,
310                               create_syntax_file_source (rc), GETL_BATCH,
311                               ERRMODE_CONTINUE);
312           free (rc);
313         }
314     }
315
316   if (optind < argc)
317     {
318       int i;
319
320       for (i = optind; i < argc; i++)
321         add_syntax_file (to, argv[i]);
322     }
323   else
324     add_syntax_file (to, "-");
325
326   register_output_driver (to);
327   if (!to->has_output_driver)
328     {
329       string_map_insert (&to->options, "output-file", "-");
330       string_map_insert (&to->options, "format", "txt");
331       register_output_driver (to);
332     }
333
334   string_map_destroy (&to->options);
335   free (to);
336 }