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