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